summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2017-10-19 18:17:25 +0000
committerJason Monk <jmonk@google.com>2017-10-19 18:17:25 +0000
commit07f9f65561c2b81bcd189b895b31bb2ad0438d74 (patch)
tree49f76f879a89c256a4f65b674086be50760bdffb
parentd439404c9988df6001e4ff8bce31537e2692660e (diff)
downloadandroid-28-07f9f65561c2b81bcd189b895b31bb2ad0438d74.tar.gz
Revert "Import Android SDK Platform P [4402356]"
This reverts commit d439404c9988df6001e4ff8bce31537e2692660e. Change-Id: I825790bdf38523800388bc1bb531cecfcd7e60bd
-rw-r--r--android/accessibilityservice/GestureDescription.java12
-rw-r--r--android/app/Activity.java8
-rw-r--r--android/app/ActivityManager.java294
-rw-r--r--android/app/ActivityOptions.java43
-rw-r--r--android/app/KeyguardManager.java2
-rw-r--r--android/app/NotificationChannel.java2
-rw-r--r--android/app/NotificationManager.java9
-rw-r--r--android/app/StatusBarManager.java5
-rw-r--r--android/app/TaskStackListener.java7
-rw-r--r--android/app/WallpaperManager.java31
-rw-r--r--android/app/WindowConfiguration.java3
-rw-r--r--android/app/assist/AssistStructure.java57
-rw-r--r--android/app/job/JobScheduler.java41
-rw-r--r--android/app/job/JobService.java102
-rw-r--r--android/app/usage/UsageStatsManager.java19
-rw-r--r--android/arch/lifecycle/ActivityFullLifecycleTest.java58
-rw-r--r--android/arch/lifecycle/AndroidViewModel.java7
-rw-r--r--android/arch/lifecycle/ClassesInfoCache.java16
-rw-r--r--android/arch/lifecycle/ComputableLiveData.java139
-rw-r--r--android/arch/lifecycle/DispatcherActivityCallbackTest.java77
-rw-r--r--android/arch/lifecycle/Lifecycle.java7
-rw-r--r--android/arch/lifecycle/LifecycleDispatcher.java182
-rw-r--r--android/arch/lifecycle/LifecycleOwner.java3
-rw-r--r--android/arch/lifecycle/LifecycleRegistry.java63
-rw-r--r--android/arch/lifecycle/LifecycleRegistryOwner.java3
-rw-r--r--android/arch/lifecycle/LifecycleRegistryTest.java19
-rw-r--r--android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java (renamed from android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java)3
-rw-r--r--android/arch/lifecycle/LiveData.java410
-rw-r--r--android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java177
-rw-r--r--android/arch/lifecycle/LiveDataTest.java224
-rw-r--r--android/arch/lifecycle/MediatorLiveData.java39
-rw-r--r--android/arch/lifecycle/MissingClassTest.java44
-rw-r--r--android/arch/lifecycle/PartiallyCoveredActivityTest.java200
-rw-r--r--android/arch/lifecycle/ProcessLifecycleOwner.java5
-rw-r--r--android/arch/lifecycle/ProcessOwnerTest.java24
-rw-r--r--android/arch/lifecycle/ReportFragment.java1
-rw-r--r--android/arch/lifecycle/TestUtils.java108
-rw-r--r--android/arch/lifecycle/Transformations.java8
-rw-r--r--android/arch/lifecycle/ViewModelProvider.java11
-rw-r--r--android/arch/lifecycle/ViewModelProviders.java3
-rw-r--r--android/arch/lifecycle/ViewModelStoreOwner.java3
-rw-r--r--android/arch/lifecycle/ViewModelStores.java5
-rw-r--r--android/arch/lifecycle/testapp/CollectingActivity.java (renamed from android/arch/lifecycle/testapp/CollectingLifecycleOwner.java)11
-rw-r--r--android/arch/lifecycle/testapp/CollectingSupportActivity.java113
-rw-r--r--android/arch/lifecycle/testapp/CollectingSupportFragment.java104
-rw-r--r--android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java32
-rw-r--r--android/arch/lifecycle/testapp/FullLifecycleTestActivity.java88
-rw-r--r--android/arch/lifecycle/testapp/MainActivity.java31
-rw-r--r--android/arch/lifecycle/testapp/NavigationDialogActivity.java6
-rw-r--r--android/arch/lifecycle/testapp/NonSupportActivity.java85
-rw-r--r--android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java95
-rw-r--r--android/arch/lifecycle/testapp/TestEvent.java4
-rw-r--r--android/arch/lifecycle/testapp/TestObserver.java2
-rw-r--r--android/arch/paging/BoundedDataSource.java2
-rw-r--r--android/arch/paging/ContiguousDataSource.java110
-rw-r--r--android/arch/paging/ContiguousDiffHelper.java (renamed from android/arch/paging/PagedStorageDiffHelper.java)65
-rw-r--r--android/arch/paging/ContiguousDiffHelperTest.java (renamed from android/arch/paging/PagedStorageDiffHelperTest.java)67
-rw-r--r--android/arch/paging/ContiguousPagedList.java408
-rw-r--r--android/arch/paging/ContiguousPagedListTest.java62
-rw-r--r--android/arch/paging/DataSource.java14
-rw-r--r--android/arch/paging/KeyedDataSource.java15
-rw-r--r--android/arch/paging/LivePagedListProvider.java132
-rw-r--r--android/arch/paging/NullPaddedList.java75
-rw-r--r--android/arch/paging/Page.java51
-rw-r--r--android/arch/paging/PageArrayList.java130
-rw-r--r--android/arch/paging/PageArrayListTest.java49
-rw-r--r--android/arch/paging/PageResult.java56
-rw-r--r--android/arch/paging/PagedList.java145
-rw-r--r--android/arch/paging/PagedListAdapterHelper.java109
-rw-r--r--android/arch/paging/PagedListAdapterHelperTest.java59
-rw-r--r--android/arch/paging/PagedStorage.java433
-rw-r--r--android/arch/paging/PositionalDataSource.java25
-rw-r--r--android/arch/paging/SnapshotPagedList.java64
-rw-r--r--android/arch/paging/SparseDiffHelper.java99
-rw-r--r--android/arch/paging/StringPagedList.java56
-rw-r--r--android/arch/paging/TestExecutor.java2
-rw-r--r--android/arch/paging/TiledDataSource.java58
-rw-r--r--android/arch/paging/TiledPagedList.java276
-rw-r--r--android/arch/paging/TiledPagedListTest.java124
-rw-r--r--android/arch/persistence/room/InvalidationTracker.java4
-rw-r--r--android/arch/persistence/room/Relation.java18
-rw-r--r--android/arch/persistence/room/Room.java2
-rw-r--r--android/arch/persistence/room/RoomDatabase.java15
-rw-r--r--android/arch/persistence/room/RoomWarnings.java8
-rw-r--r--android/arch/persistence/room/Transaction.java37
-rw-r--r--android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java1
-rw-r--r--android/arch/persistence/room/integration/testapp/database/CustomerDao.java2
-rw-r--r--android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java47
-rw-r--r--android/arch/persistence/room/integration/testapp/test/InvalidationTest.java120
-rw-r--r--android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java8
-rw-r--r--android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java471
-rw-r--r--android/arch/persistence/room/migration/Migration.java3
-rw-r--r--android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java5
-rw-r--r--android/arch/persistence/room/paging/LimitOffsetDataSource.java36
-rw-r--r--android/arch/persistence/room/util/StringUtil.java7
-rw-r--r--android/content/ContentProvider.java3
-rw-r--r--android/content/ContentResolver.java36
-rw-r--r--android/content/Intent.java52
-rw-r--r--android/content/IntentFilter.java63
-rw-r--r--android/content/pm/FeatureInfo.java13
-rw-r--r--android/content/pm/LauncherApps.java51
-rw-r--r--android/content/pm/PackageManagerInternal.java5
-rw-r--r--android/content/pm/PackageParser.java23
-rw-r--r--android/content/pm/PermissionInfo.java5
-rw-r--r--android/content/pm/ShortcutInfo.java207
-rw-r--r--android/content/pm/ShortcutServiceInternal.java7
-rw-r--r--android/content/res/ResourcesImpl.java2
-rw-r--r--android/database/SQLiteDatabaseIoPerfTest.java176
-rw-r--r--android/database/SQLiteDatabasePerfTest.java223
-rw-r--r--android/graphics/Bitmap.java2
-rw-r--r--android/graphics/BitmapFactory.java3
-rw-r--r--android/media/AudioAttributes.java23
-rw-r--r--android/media/MediaMetadataRetriever.java2
-rw-r--r--android/media/MediaRecorder.java6
-rw-r--r--android/media/tv/TvInputManager.java7
-rw-r--r--android/net/LinkProperties.java30
-rw-r--r--android/net/ip/ConnectivityPacketTracker.java23
-rw-r--r--android/net/ip/IpManager.java26
-rw-r--r--android/net/util/SharedLog.java4
-rw-r--r--android/os/BatteryStats.java757
-rw-r--r--android/os/Debug.java20
-rw-r--r--android/os/ParcelFileDescriptor.java4
-rw-r--r--android/os/PatternMatcher.java16
-rw-r--r--android/os/ServiceManager.java105
-rw-r--r--android/os/SystemProperties.java6
-rw-r--r--android/os/UserManager.java19
-rw-r--r--android/preference/SeekBarVolumizer.java3
-rw-r--r--android/provider/Settings.java25
-rw-r--r--android/service/autofill/AutofillService.java77
-rw-r--r--android/service/autofill/SaveInfo.java88
-rw-r--r--android/service/autofill/SaveRequest.java12
-rw-r--r--android/service/notification/ZenModeConfig.java79
-rw-r--r--android/slice/Slice.java (renamed from android/app/slice/Slice.java)154
-rw-r--r--android/slice/SliceItem.java (renamed from android/app/slice/SliceItem.java)26
-rw-r--r--android/slice/SliceProvider.java (renamed from android/app/slice/SliceProvider.java)38
-rw-r--r--android/slice/SliceQuery.java (renamed from android/app/slice/SliceQuery.java)15
-rw-r--r--android/slice/views/ActionRow.java (renamed from android/app/slice/views/ActionRow.java)8
-rw-r--r--android/slice/views/GridView.java (renamed from android/app/slice/views/GridView.java)18
-rw-r--r--android/slice/views/LargeSliceAdapter.java (renamed from android/app/slice/views/LargeSliceAdapter.java)10
-rw-r--r--android/slice/views/LargeTemplateView.java (renamed from android/app/slice/views/LargeTemplateView.java)15
-rw-r--r--android/slice/views/MessageView.java (renamed from android/app/slice/views/MessageView.java)10
-rw-r--r--android/slice/views/RemoteInputView.java (renamed from android/app/slice/views/RemoteInputView.java)2
-rw-r--r--android/slice/views/ShortcutView.java (renamed from android/app/slice/views/ShortcutView.java)10
-rw-r--r--android/slice/views/SliceView.java (renamed from android/app/slice/views/SliceView.java)18
-rw-r--r--android/slice/views/SliceViewUtil.java (renamed from android/app/slice/views/SliceViewUtil.java)2
-rw-r--r--android/slice/views/SmallTemplateView.java (renamed from android/app/slice/views/SmallTemplateView.java)24
-rw-r--r--android/support/LibraryVersions.java6
-rw-r--r--android/support/car/drawer/CarDrawerActivity.java152
-rw-r--r--android/support/car/drawer/CarDrawerAdapter.java182
-rw-r--r--android/support/car/drawer/CarDrawerController.java306
-rw-r--r--android/support/car/drawer/DrawerItemViewHolder.java87
-rw-r--r--android/support/car/widget/PagedListView.java57
-rw-r--r--android/support/media/tv/BasePreviewProgram.java100
-rw-r--r--android/support/media/tv/BaseProgram.java23
-rw-r--r--android/support/media/tv/Program.java3
-rw-r--r--android/support/media/tv/TvContractCompat.java82
-rw-r--r--android/support/media/tv/WatchNextProgram.java27
-rw-r--r--android/support/mediacompat/testlib/IntentConstants.java2
-rw-r--r--android/support/mediacompat/testlib/MediaSessionConstants.java57
-rw-r--r--android/support/transition/AutoTransition.java6
-rw-r--r--android/support/v17/leanback/widget/GridLayoutManager.java18
-rw-r--r--android/support/v4/content/res/ResourcesCompat.java4
-rw-r--r--android/support/v4/media/RatingCompat.java23
-rw-r--r--android/support/v4/media/RatingCompatKitkat.java67
-rw-r--r--android/support/v4/provider/FontsContractCompat.java3
-rw-r--r--android/support/v7/app/MediaRouteButton.java3
-rw-r--r--android/support/v7/app/MediaRouteChooserDialog.java6
-rw-r--r--android/support/v7/app/MediaRouteControllerDialog.java8
-rw-r--r--android/support/v7/app/MediaRouterThemeHelper.java110
-rw-r--r--android/support/v7/util/DiffUtil.java67
-rw-r--r--android/support/v7/widget/AppCompatTextHelper.java37
-rw-r--r--android/support/v7/widget/TintTypedArray.java5
-rw-r--r--android/telephony/CarrierConfigManager.java34
-rw-r--r--android/telephony/MbmsDownloadSession.java41
-rw-r--r--android/telephony/NetworkScanRequest.java105
-rw-r--r--android/telephony/ServiceState.java9
-rw-r--r--android/telephony/mbms/DownloadRequest.java30
-rw-r--r--android/telephony/mbms/MbmsDownloadReceiver.java11
-rw-r--r--android/telephony/mbms/vendor/MbmsDownloadServiceBase.java18
-rw-r--r--android/telephony/mbms/vendor/MbmsStreamingServiceBase.java8
-rw-r--r--android/text/BoringLayoutCreateDrawPerfTest.java2
-rw-r--r--android/text/BoringLayoutIsBoringPerfTest.java2
-rw-r--r--android/text/DynamicLayout.java6
-rw-r--r--android/text/Hyphenator.java251
-rw-r--r--android/text/Layout.java14
-rw-r--r--android/text/PaintMeasureDrawPerfTest.java2
-rw-r--r--android/text/StaticLayout.java74
-rw-r--r--android/text/StaticLayoutCreateDrawPerfTest.java2
-rw-r--r--android/text/StaticLayout_Delegate.java26
-rw-r--r--android/text/TextLine.java10
-rw-r--r--android/text/TextViewSetTextMeasurePerfTest.java4
-rw-r--r--android/util/Log.java295
-rw-r--r--android/util/LruCache.java25
-rw-r--r--android/util/StatsLog.java76
-rw-r--r--android/util/StatsLogKey.java48
-rw-r--r--android/util/StatsLogTag.java (renamed from android/support/car/drawer/DrawerItemClickListener.java)25
-rw-r--r--android/util/StatsLogValue.java54
-rw-r--r--android/view/SurfaceControl.java538
-rw-r--r--android/view/SurfaceView.java1135
-rw-r--r--android/view/View.java262
-rw-r--r--android/view/ViewDebug.java167
-rw-r--r--android/view/ViewGroup.java98
-rw-r--r--android/view/ViewRootImpl.java758
-rw-r--r--android/view/ViewStructure.java24
-rw-r--r--android/view/WindowManagerInternal.java4
-rw-r--r--android/view/WindowManagerPolicy.java7
-rw-r--r--android/view/accessibility/AccessibilityCache.java2
-rw-r--r--android/view/accessibility/AccessibilityManager.java916
-rw-r--r--android/view/autofill/AutofillManager.java149
-rw-r--r--android/view/textclassifier/TextClassifier.java8
-rw-r--r--android/view/textclassifier/TextClassifierConstants.java90
-rw-r--r--android/view/textclassifier/TextClassifierImpl.java14
-rw-r--r--android/view/textservice/TextServicesManager.java200
-rw-r--r--android/webkit/WebView.java2878
-rw-r--r--android/widget/Editor.java21
-rw-r--r--android/widget/RemoteViews.java6
-rw-r--r--android/widget/SelectionActionModeHelper.java30
-rw-r--r--android/widget/TextView.java41
-rw-r--r--com/android/commands/pm/Pm.java36
-rw-r--r--com/android/ex/photo/ActionBarWrapper.java3
-rw-r--r--com/android/ex/photo/PhotoViewActivity.java8
-rw-r--r--com/android/internal/alsa/AlsaCardsParser.java32
-rw-r--r--com/android/internal/alsa/AlsaDevicesParser.java30
-rw-r--r--com/android/internal/notification/SystemNotificationChannels.java13
-rw-r--r--com/android/internal/os/BatteryStatsImpl.java44
-rw-r--r--com/android/internal/os/LoggingPrintStream.java5
-rw-r--r--com/android/internal/os/ZygoteInit.java2
-rw-r--r--com/android/internal/telephony/CarrierKeyDownloadManager.java40
-rw-r--r--com/android/internal/telephony/NetworkScanRequestTracker.java28
-rw-r--r--com/android/internal/telephony/Phone.java4
-rw-r--r--com/android/internal/telephony/RIL.java134
-rw-r--r--com/android/internal/telephony/cat/CatService.java7
-rw-r--r--com/android/internal/telephony/uicc/UiccCardApplication.java5
-rw-r--r--com/android/internal/util/MemInfoReader.java4
-rw-r--r--com/android/internal/view/menu/ListMenuItemView.java8
-rw-r--r--com/android/internal/widget/Magnifier.java132
-rw-r--r--com/android/keyguard/CarrierText.java59
-rw-r--r--com/android/keyguard/KeyguardSecurityModel.java10
-rw-r--r--com/android/keyguard/KeyguardUpdateMonitor.java7
-rw-r--r--com/android/layoutlib/bridge/Bridge.java655
-rw-r--r--com/android/server/AppOpsService.java38
-rw-r--r--com/android/server/BatteryService.java421
-rw-r--r--com/android/server/IntentResolver.java27
-rw-r--r--com/android/server/SystemServer.java10
-rw-r--r--com/android/server/VibratorService.java24
-rw-r--r--com/android/server/accessibility/AccessibilityInputFilter.java20
-rw-r--r--com/android/server/accessibility/AutoclickController.java36
-rw-r--r--com/android/server/accessibility/BaseEventStreamTransformation.java31
-rw-r--r--com/android/server/accessibility/EventStreamTransformation.java37
-rw-r--r--com/android/server/accessibility/KeyboardInterceptor.java35
-rw-r--r--com/android/server/accessibility/MagnificationController.java19
-rw-r--r--com/android/server/accessibility/MagnificationGestureHandler.java382
-rw-r--r--com/android/server/accessibility/MotionEventInjector.java41
-rw-r--r--com/android/server/accessibility/TouchExplorer.java40
-rw-r--r--com/android/server/am/ActivityDisplay.java150
-rw-r--r--com/android/server/am/ActivityManagerDebugConfig.java8
-rw-r--r--com/android/server/am/ActivityManagerService.java640
-rw-r--r--com/android/server/am/ActivityManagerShellCommand.java100
-rw-r--r--com/android/server/am/ActivityMetricsLogger.java3
-rw-r--r--com/android/server/am/ActivityRecord.java72
-rw-r--r--com/android/server/am/ActivityStack.java215
-rw-r--r--com/android/server/am/ActivityStackSupervisor.java528
-rw-r--r--com/android/server/am/ActivityStarter.java133
-rw-r--r--com/android/server/am/AppTaskImpl.java150
-rw-r--r--com/android/server/am/BatteryStatsService.java25
-rw-r--r--com/android/server/am/BroadcastFilter.java24
-rw-r--r--com/android/server/am/BroadcastQueue.java52
-rw-r--r--com/android/server/am/BroadcastRecord.java11
-rw-r--r--com/android/server/am/KeyguardController.java10
-rw-r--r--com/android/server/am/LaunchingTaskPositioner.java211
-rw-r--r--com/android/server/am/LockTaskController.java10
-rw-r--r--com/android/server/am/ProcessRecord.java18
-rw-r--r--com/android/server/am/ReceiverList.java31
-rw-r--r--com/android/server/am/RecentTasks.java1040
-rw-r--r--com/android/server/am/ServiceRecord.java20
-rw-r--r--com/android/server/am/TaskPersister.java19
-rw-r--r--com/android/server/am/TaskRecord.java135
-rw-r--r--com/android/server/appwidget/AppWidgetServiceImpl.java49
-rw-r--r--com/android/server/audio/PlaybackActivityMonitor.java8
-rw-r--r--com/android/server/autofill/AutofillManagerServiceImpl.java34
-rw-r--r--com/android/server/autofill/Session.java43
-rw-r--r--com/android/server/backup/BackupManagerService.java67
-rw-r--r--com/android/server/backup/RefactoredBackupManagerService.java68
-rw-r--r--com/android/server/backup/Trampoline.java29
-rw-r--r--com/android/server/clipboard/ClipboardService.java3
-rw-r--r--com/android/server/connectivity/Tethering.java1
-rw-r--r--com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java52
-rw-r--r--com/android/server/display/AutomaticBrightnessController.java2
-rw-r--r--com/android/server/display/DisplayPowerController.java33
-rw-r--r--com/android/server/job/controllers/TimeController.java32
-rw-r--r--com/android/server/location/GnssLocationProvider.java6
-rw-r--r--com/android/server/media/MediaSessionRecord.java8
-rw-r--r--com/android/server/media/MediaSessionService.java36
-rw-r--r--com/android/server/notification/NotificationManagerService.java38
-rw-r--r--com/android/server/notification/ZenModeFiltering.java21
-rw-r--r--com/android/server/notification/ZenModeHelper.java24
-rw-r--r--com/android/server/pm/LauncherAppsService.java33
-rw-r--r--com/android/server/pm/PackageDexOptimizer.java17
-rw-r--r--com/android/server/pm/PackageInstallerSession.java11
-rw-r--r--com/android/server/pm/PackageManagerService.java317
-rw-r--r--com/android/server/pm/Settings.java29
-rw-r--r--com/android/server/pm/SharedUserSetting.java9
-rw-r--r--com/android/server/pm/ShortcutBitmapSaver.java10
-rw-r--r--com/android/server/pm/ShortcutLauncher.java53
-rw-r--r--com/android/server/pm/ShortcutPackage.java295
-rw-r--r--com/android/server/pm/ShortcutPackageInfo.java138
-rw-r--r--com/android/server/pm/ShortcutPackageItem.java46
-rw-r--r--com/android/server/pm/ShortcutParser.java6
-rw-r--r--com/android/server/pm/ShortcutRequestPinProcessor.java12
-rw-r--r--com/android/server/pm/ShortcutService.java153
-rw-r--r--com/android/server/pm/ShortcutUser.java3
-rw-r--r--com/android/server/pm/UserRestrictionsUtils.java2
-rw-r--r--com/android/server/pm/permission/BasePermission.java27
-rw-r--r--com/android/server/pm/permission/PermissionManagerInternal.java22
-rw-r--r--com/android/server/pm/permission/PermissionManagerService.java273
-rw-r--r--com/android/server/pm/permission/PermissionSettings.java117
-rw-r--r--com/android/server/policy/GlobalActions.java3
-rw-r--r--com/android/server/policy/PhoneWindowManager.java147
-rw-r--r--com/android/server/stats/StatsCompanionService.java136
-rw-r--r--com/android/server/statusbar/StatusBarManagerInternal.java1
-rw-r--r--com/android/server/statusbar/StatusBarManagerService.java7
-rw-r--r--com/android/server/tv/TvInputHardwareManager.java14
-rw-r--r--com/android/server/usb/UsbAlsaManager.java9
-rw-r--r--com/android/server/usb/UsbHostManager.java2
-rw-r--r--com/android/server/wifi/SelfRecovery.java2
-rw-r--r--com/android/server/wifi/WifiNative.java9
-rw-r--r--com/android/server/wifi/WifiStateMachine.java5
-rw-r--r--com/android/server/wifi/WifiStateMachinePrime.java3
-rw-r--r--com/android/server/wifi/WificondControl.java9
-rw-r--r--com/android/server/wifi/scanner/WificondScannerImpl.java16
-rw-r--r--com/android/server/wifi/util/InformationElementUtil.java4
-rw-r--r--com/android/server/wm/AppWindowToken.java5
-rw-r--r--com/android/server/wm/BlackFrame.java11
-rw-r--r--com/android/server/wm/CircularDisplayMask.java12
-rw-r--r--com/android/server/wm/ConfigurationContainer.java15
-rw-r--r--com/android/server/wm/DimLayer.java11
-rw-r--r--com/android/server/wm/DisplayContent.java337
-rw-r--r--com/android/server/wm/DockedStackDividerController.java23
-rw-r--r--com/android/server/wm/EmulatorDisplayOverlay.java11
-rw-r--r--com/android/server/wm/InputMonitor.java3
-rw-r--r--com/android/server/wm/PinnedStackController.java3
-rw-r--r--com/android/server/wm/RootWindowContainer.java11
-rw-r--r--com/android/server/wm/ScreenRotationAnimation.java17
-rw-r--r--com/android/server/wm/StackWindowController.java11
-rw-r--r--com/android/server/wm/Task.java16
-rw-r--r--com/android/server/wm/TaskPositioner.java3
-rw-r--r--com/android/server/wm/TaskSnapshotController.java4
-rw-r--r--com/android/server/wm/TaskStack.java93
-rw-r--r--com/android/server/wm/WallpaperController.java4
-rw-r--r--com/android/server/wm/WindowContainer.java4
-rw-r--r--com/android/server/wm/WindowManagerDebugConfig.java1
-rw-r--r--com/android/server/wm/WindowManagerService.java22
-rw-r--r--com/android/server/wm/WindowState.java48
-rw-r--r--com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--com/android/server/wm/WindowSurfaceController.java259
-rw-r--r--com/android/server/wm/WindowSurfacePlacer.java3
-rw-r--r--com/android/settingslib/CustomEditTextPreference.java9
-rw-r--r--com/android/settingslib/development/AbstractLogdSizePreferenceController.java12
-rw-r--r--com/android/settingslib/development/AbstractLogpersistPreferenceController.java2
-rw-r--r--com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java85
-rw-r--r--com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java114
-rw-r--r--com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java95
-rw-r--r--com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java112
-rw-r--r--com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java119
-rw-r--r--com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java88
-rw-r--r--com/android/settingslib/suggestions/SuggestionParser.java2
-rw-r--r--com/android/settingslib/wifi/WifiTracker.java22
-rw-r--r--com/android/setupwizardlib/test/util/DrawingTestActivity.java9
-rw-r--r--com/android/setupwizardlib/util/WizardManagerHelper.java14
-rw-r--r--com/android/setupwizardlib/util/WizardManagerHelperTest.java8
-rw-r--r--com/android/setupwizardlib/view/NavigationBarButton.java127
-rw-r--r--com/android/setupwizardlib/view/RichTextView.java49
-rw-r--r--com/android/systemui/Dependency.java4
-rw-r--r--com/android/systemui/ImageWallpaper.java517
-rw-r--r--com/android/systemui/SwipeHelper.java62
-rw-r--r--com/android/systemui/doze/DozeUi.java3
-rw-r--r--com/android/systemui/globalactions/GlobalActionsComponent.java10
-rw-r--r--com/android/systemui/globalactions/GlobalActionsDialog.java49
-rw-r--r--com/android/systemui/globalactions/GlobalActionsImpl.java26
-rw-r--r--com/android/systemui/keyguard/WorkLockActivityController.java6
-rw-r--r--com/android/systemui/pip/phone/InputConsumerController.java2
-rw-r--r--com/android/systemui/pip/phone/PipManager.java5
-rw-r--r--com/android/systemui/pip/tv/PipManager.java5
-rw-r--r--com/android/systemui/plugins/GlobalActions.java3
-rw-r--r--com/android/systemui/power/PowerUI.java40
-rw-r--r--com/android/systemui/recents/Recents.java92
-rw-r--r--com/android/systemui/recents/RecentsActivity.java151
-rw-r--r--com/android/systemui/recents/RecentsActivityLaunchState.java20
-rw-r--r--com/android/systemui/recents/RecentsConfiguration.java34
-rw-r--r--com/android/systemui/recents/RecentsDebugFlags.java65
-rw-r--r--com/android/systemui/recents/RecentsImpl.java211
-rw-r--r--com/android/systemui/recents/RecentsSystemUser.java6
-rw-r--r--com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java2
-rw-r--r--com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java (renamed from com/android/systemui/shared/recents/model/TaskFilter.java)15
-rw-r--r--com/android/systemui/recents/events/activity/IterateRecentsEvent.java27
-rw-r--r--com/android/systemui/recents/events/activity/LaunchTaskEvent.java2
-rw-r--r--com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java2
-rw-r--r--com/android/systemui/recents/events/activity/PackagesChangedEvent.java8
-rw-r--r--com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java4
-rw-r--r--com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java31
-rw-r--r--com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java5
-rw-r--r--com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java2
-rw-r--r--com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java7
-rw-r--r--com/android/systemui/recents/misc/NamedCounter.java (renamed from java/lang/invoke/ArrayElementVarHandle.java)30
-rw-r--r--com/android/systemui/recents/misc/RectFEvaluator.java (renamed from com/android/systemui/shared/recents/utilities/RectFEvaluator.java)4
-rw-r--r--com/android/systemui/recents/misc/SystemServicesProxy.java740
-rw-r--r--com/android/systemui/recents/misc/TaskStackChangeListener.java65
-rw-r--r--com/android/systemui/recents/misc/TaskStackChangeListeners.java254
-rw-r--r--com/android/systemui/recents/misc/Utilities.java (renamed from com/android/systemui/shared/recents/utilities/Utilities.java)25
-rw-r--r--com/android/systemui/recents/model/HighResThumbnailLoader.java (renamed from com/android/systemui/shared/recents/model/HighResThumbnailLoader.java)19
-rw-r--r--com/android/systemui/recents/model/RecentsPackageMonitor.java74
-rw-r--r--com/android/systemui/recents/model/RecentsTaskLoadPlan.java330
-rw-r--r--com/android/systemui/recents/model/RecentsTaskLoader.java (renamed from com/android/systemui/shared/recents/model/RecentsTaskLoader.java)372
-rw-r--r--com/android/systemui/recents/model/Task.java (renamed from com/android/systemui/shared/recents/model/Task.java)101
-rw-r--r--com/android/systemui/recents/model/TaskGrouping.java106
-rw-r--r--com/android/systemui/recents/model/TaskKeyCache.java (renamed from com/android/systemui/shared/recents/model/TaskKeyCache.java)14
-rw-r--r--com/android/systemui/recents/model/TaskKeyLruCache.java (renamed from com/android/systemui/shared/recents/model/TaskKeyLruCache.java)8
-rw-r--r--com/android/systemui/recents/model/TaskKeyStrongCache.java (renamed from com/android/systemui/shared/recents/model/TaskKeyStrongCache.java)6
-rw-r--r--com/android/systemui/recents/model/TaskStack.java1140
-rw-r--r--com/android/systemui/recents/model/ThumbnailData.java (renamed from com/android/systemui/shared/recents/model/ThumbnailData.java)31
-rw-r--r--com/android/systemui/recents/views/AnimateableViewBounds.java2
-rw-r--r--com/android/systemui/recents/views/AnimationProps.java (renamed from com/android/systemui/shared/recents/utilities/AnimationProps.java)13
-rw-r--r--com/android/systemui/recents/views/DockState.java351
-rw-r--r--com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java170
-rw-r--r--com/android/systemui/recents/views/RecentsTransitionHelper.java57
-rw-r--r--com/android/systemui/recents/views/RecentsView.java141
-rw-r--r--com/android/systemui/recents/views/RecentsViewTouchHandler.java14
-rw-r--r--com/android/systemui/recents/views/SystemBarScrimViews.java4
-rw-r--r--com/android/systemui/recents/views/TaskStackAnimationHelper.java58
-rw-r--r--com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java199
-rw-r--r--com/android/systemui/recents/views/TaskStackView.java321
-rw-r--r--com/android/systemui/recents/views/TaskStackViewScroller.java4
-rw-r--r--com/android/systemui/recents/views/TaskStackViewTouchHandler.java19
-rw-r--r--com/android/systemui/recents/views/TaskView.java20
-rw-r--r--com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java24
-rw-r--r--com/android/systemui/recents/views/TaskViewHeader.java78
-rw-r--r--com/android/systemui/recents/views/TaskViewThumbnail.java18
-rw-r--r--com/android/systemui/recents/views/TaskViewTransform.java6
-rw-r--r--com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java5
-rw-r--r--com/android/systemui/recents/views/grid/TaskViewFocusFrame.java2
-rw-r--r--com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java4
-rw-r--r--com/android/systemui/shared/recents/model/BackgroundTaskLoader.java186
-rw-r--r--com/android/systemui/shared/recents/model/FilteredTaskList.java124
-rw-r--r--com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java205
-rw-r--r--com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java60
-rw-r--r--com/android/systemui/shared/recents/model/TaskStack.java403
-rw-r--r--com/android/systemui/shared/system/ActivityManagerWrapper.java201
-rw-r--r--com/android/systemui/shared/system/PackageManagerWrapper.java50
-rw-r--r--com/android/systemui/shortcut/ShortcutKeyDispatcher.java29
-rw-r--r--com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java4
-rw-r--r--com/android/systemui/statusbar/ExpandableNotificationRow.java66
-rw-r--r--com/android/systemui/statusbar/car/CarStatusBar.java23
-rw-r--r--com/android/systemui/statusbar/phone/LockscreenWallpaper.java4
-rw-r--r--com/android/systemui/statusbar/phone/NavigationBarTransitions.java27
-rw-r--r--com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java19
-rw-r--r--com/android/systemui/statusbar/phone/StatusBar.java16
-rw-r--r--com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java20
-rw-r--r--com/android/systemui/util/wakelock/DelayedWakeLock.java2
-rw-r--r--foo/bar/ComplexDao.java464
-rw-r--r--foo/bar/ComplexDatabase.java118
-rw-r--r--foo/bar/DeletionDao.java271
-rw-r--r--foo/bar/UpdateDao.java280
-rw-r--r--foo/bar/WriterDao.java136
-rw-r--r--java/lang/Boolean.java2
-rw-r--r--java/lang/Byte.java2
-rw-r--r--java/lang/Character.java2
-rw-r--r--java/lang/Double.java2
-rw-r--r--java/lang/Float.java2
-rw-r--r--java/lang/Integer.java15
-rw-r--r--java/lang/Long.java6
-rw-r--r--java/lang/Short.java2
-rw-r--r--java/lang/String.java12
-rw-r--r--java/lang/Void.java17
-rw-r--r--java/lang/invoke/ByteArrayVarHandle.java37
-rw-r--r--java/lang/invoke/ByteBufferViewVarHandle.java38
-rw-r--r--java/lang/invoke/CallSite.java350
-rw-r--r--java/lang/invoke/FieldVarHandle.java46
-rw-r--r--java/lang/invoke/MethodHandle.java1347
-rw-r--r--java/lang/invoke/MethodHandles.java3379
-rw-r--r--java/lang/invoke/MethodType.java1205
-rw-r--r--java/lang/invoke/VarHandle.java410
-rw-r--r--java/net/Socket.java21
-rw-r--r--java/net/SocketException.java2
-rw-r--r--java/net/SocketImpl.java1
-rw-r--r--java/net/SocketInputStream.java13
-rw-r--r--java/net/SocketOutputStream.java15
-rw-r--r--java/net/SocksSocketImpl.java541
-rw-r--r--java/net/URLClassLoader.java18
-rw-r--r--java/security/AlgorithmParameters.java6
-rw-r--r--java/security/KeyFactory.java4
-rw-r--r--java/security/KeyPairGenerator.java4
-rw-r--r--java/security/MessageDigest.java6
-rw-r--r--java/security/Signature.java4
-rw-r--r--java/security/cert/CertificateFactory.java4
-rw-r--r--javax/crypto/KeyAgreement.java4
-rw-r--r--javax/crypto/KeyGenerator.java4
-rw-r--r--javax/crypto/Mac.java4
-rw-r--r--javax/crypto/SecretKeyFactory.java4
506 files changed, 24440 insertions, 21420 deletions
diff --git a/android/accessibilityservice/GestureDescription.java b/android/accessibilityservice/GestureDescription.java
index 56f4ae2b..92567d75 100644
--- a/android/accessibilityservice/GestureDescription.java
+++ b/android/accessibilityservice/GestureDescription.java
@@ -428,18 +428,6 @@ public final class GestureDescription {
}
@Override
- public String toString() {
- return "TouchPoint{"
- + "mStrokeId=" + mStrokeId
- + ", mContinuedStrokeId=" + mContinuedStrokeId
- + ", mIsStartOfPath=" + mIsStartOfPath
- + ", mIsEndOfPath=" + mIsEndOfPath
- + ", mX=" + mX
- + ", mY=" + mY
- + '}';
- }
-
- @Override
public int describeContents() {
return 0;
}
diff --git a/android/app/Activity.java b/android/app/Activity.java
index 85f73bb7..e0ac9113 100644
--- a/android/app/Activity.java
+++ b/android/app/Activity.java
@@ -542,9 +542,9 @@ import java.util.List;
* <ul>
* <li> <p>When creating a new document, the backing database entry or file for
* it is created immediately. For example, if the user chooses to write
- * a new email, a new entry for that email is created as soon as they
+ * a new e-mail, a new entry for that e-mail is created as soon as they
* start entering data, so that if they go to any other activity after
- * that point this email will now appear in the list of drafts.</p>
+ * that point this e-mail will now appear in the list of drafts.</p>
* <li> <p>When an activity's <code>onPause()</code> method is called, it should
* commit to the backing content provider or file any changes the user
* has made. This ensures that those changes will be seen by any other
@@ -1879,7 +1879,7 @@ public class Activity extends ContextThemeWrapper
if (isFinishing()) {
if (mAutoFillResetNeeded) {
- getAutofillManager().onActivityFinished();
+ getAutofillManager().commit();
} else if (mIntent != null
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
// Activity was launched when user tapped a link in the Autofill Save UI - since
@@ -6259,8 +6259,6 @@ public class Activity extends ContextThemeWrapper
final AutofillManager afm = getAutofillManager();
if (afm != null) {
afm.dump(prefix, writer);
- } else {
- writer.print(prefix); writer.println("No AutofillManager");
}
}
diff --git a/android/app/ActivityManager.java b/android/app/ActivityManager.java
index fc4c8d7f..5e61727f 100644
--- a/android/app/ActivityManager.java
+++ b/android/app/ActivityManager.java
@@ -16,8 +16,14 @@
package android.app;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
import android.Manifest;
-import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -664,6 +670,138 @@ public class ActivityManager {
/** Invalid stack ID. */
public static final int INVALID_STACK_ID = -1;
+ /** First static stack ID.
+ * @hide */
+ private static final int FIRST_STATIC_STACK_ID = 0;
+
+ /** ID of stack where fullscreen activities are normally launched into.
+ * @hide */
+ public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
+
+ /** ID of stack where freeform/resized activities are normally launched into.
+ * @hide */
+ public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
+
+ /** ID of stack that occupies a dedicated region of the screen.
+ * @hide */
+ public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
+
+ /** ID of stack that always on top (always visible) when it exist.
+ * @hide */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+
+ /** Last static stack stack ID.
+ * @hide */
+ private static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
+
+ /** Start of ID range used by stacks that are created dynamically.
+ * @hide */
+ public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+
+ // TODO: Figure-out a way to remove this.
+ /** @hide */
+ public static boolean isStaticStack(int stackId) {
+ return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
+ }
+
+ // TODO: It seems this mostly means a stack on a secondary display now. Need to see if
+ // there are other meanings. If not why not just use information from the display?
+ /** @hide */
+ public static boolean isDynamicStack(int stackId) {
+ return stackId >= FIRST_DYNAMIC_STACK_ID;
+ }
+
+ /**
+ * Returns true if we try to maintain focus in the current stack when the top activity
+ * finishes.
+ * @hide
+ */
+ // TODO: Figure-out a way to remove. Probably isn't needed in the new world...
+ public static boolean keepFocusInStackIfPossible(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the windows of tasks being moved to the target stack from the source
+ * stack should be replaced, meaning that window manager will keep the old window around
+ * until the new is ready.
+ * @hide
+ */
+ public static boolean replaceWindowsOnTaskMove(int sourceStackId, int targetStackId) {
+ return sourceStackId == FREEFORM_WORKSPACE_STACK_ID
+ || targetStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ /**
+ * Returns true if the top task in the task is allowed to return home when finished and
+ * there are other tasks in the stack.
+ * @hide
+ */
+ public static boolean allowTopTaskToReturnHome(int stackId) {
+ return stackId != PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if the stack should be resized to match the bounds specified by
+ * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
+ * @hide
+ */
+ public static boolean resizeStackWithLaunchBounds(int stackId) {
+ return stackId == PINNED_STACK_ID;
+ }
+
+ /**
+ * Returns true if a window from the specified stack with {@param stackId} are normally
+ * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
+ * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
+ * etc.
+ * @hide
+ */
+ // TODO: What about the other side of docked stack if we move this to WindowConfiguration?
+ public static boolean normallyFullscreenWindows(int stackId) {
+ return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID;
+ }
+
+ /** Returns the stack id for the input windowing mode.
+ * @hide */
+ // TODO: To be removed once we are not using stack id for stuff...
+ public static int getStackIdForWindowingMode(int windowingMode) {
+ switch (windowingMode) {
+ case WINDOWING_MODE_PINNED: return PINNED_STACK_ID;
+ case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID;
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return FULLSCREEN_WORKSPACE_STACK_ID;
+ case WINDOWING_MODE_FULLSCREEN: return FULLSCREEN_WORKSPACE_STACK_ID;
+ default: return INVALID_STACK_ID;
+ }
+ }
+
+ /** Returns the windowing mode that should be used for this input stack id.
+ * @hide */
+ // TODO: To be removed once we are not using stack id for stuff...
+ public static int getWindowingModeForStackId(int stackId, boolean inSplitScreenMode) {
+ final int windowingMode;
+ switch (stackId) {
+ case FULLSCREEN_WORKSPACE_STACK_ID:
+ windowingMode = inSplitScreenMode
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN;
+ break;
+ case PINNED_STACK_ID:
+ windowingMode = WINDOWING_MODE_PINNED;
+ break;
+ case DOCKED_STACK_ID:
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ break;
+ case FREEFORM_WORKSPACE_STACK_ID:
+ windowingMode = WINDOWING_MODE_FREEFORM;
+ break;
+ default :
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
+ return windowingMode;
+ }
}
/**
@@ -942,14 +1080,11 @@ public class ActivityManager {
ATTR_TASKDESCRIPTION_PREFIX + "color";
private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
- private static final String ATTR_TASKDESCRIPTIONICON_FILENAME =
+ private static final String ATTR_TASKDESCRIPTIONICONFILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
- private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE =
- ATTR_TASKDESCRIPTION_PREFIX + "icon_resource";
private String mLabel;
private Bitmap mIcon;
- private int mIconRes;
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
@@ -963,27 +1098,9 @@ public class ActivityManager {
* @param icon An icon that represents the current state of this task.
* @param colorPrimary A color to override the theme's primary color. This color must be
* opaque.
- * @deprecated use TaskDescription constructor with icon resource instead
*/
- @Deprecated
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
- this(label, icon, 0, null, colorPrimary, 0, 0, 0);
- if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
- throw new RuntimeException("A TaskDescription's primary color should be opaque");
- }
- }
-
- /**
- * Creates the TaskDescription to the specified values.
- *
- * @param label A label and description of the current state of this task.
- * @param iconRes A drawable resource of an icon that represents the current state of this
- * activity.
- * @param colorPrimary A color to override the theme's primary color. This color must be
- * opaque.
- */
- public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
- this(label, null, iconRes, null, colorPrimary, 0, 0, 0);
+ this(label, icon, null, colorPrimary, 0, 0, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -994,22 +1111,9 @@ public class ActivityManager {
*
* @param label A label and description of the current state of this activity.
* @param icon An icon that represents the current state of this activity.
- * @deprecated use TaskDescription constructor with icon resource instead
*/
- @Deprecated
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, 0, null, 0, 0, 0, 0);
- }
-
- /**
- * Creates the TaskDescription to the specified values.
- *
- * @param label A label and description of the current state of this activity.
- * @param iconRes A drawable resource of an icon that represents the current state of this
- * activity.
- */
- public TaskDescription(String label, @DrawableRes int iconRes) {
- this(label, null, iconRes, null, 0, 0, 0, 0);
+ this(label, icon, null, 0, 0, 0, 0);
}
/**
@@ -1018,22 +1122,21 @@ public class ActivityManager {
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0, null, 0, 0, 0, 0);
+ this(label, null, null, 0, 0, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0, null, 0, 0, 0, 0);
+ this(null, null, null, 0, 0, 0, 0);
}
/** @hide */
- public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename,
- int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) {
+ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
+ int colorBackground, int statusBarColor, int navigationBarColor) {
mLabel = label;
- mIcon = bitmap;
- mIconRes = iconRes;
+ mIcon = icon;
mIconFilename = iconFilename;
mColorPrimary = colorPrimary;
mColorBackground = colorBackground;
@@ -1055,7 +1158,6 @@ public class ActivityManager {
public void copyFrom(TaskDescription other) {
mLabel = other.mLabel;
mIcon = other.mIcon;
- mIconRes = other.mIconRes;
mIconFilename = other.mIconFilename;
mColorPrimary = other.mColorPrimary;
mColorBackground = other.mColorBackground;
@@ -1071,7 +1173,6 @@ public class ActivityManager {
public void copyFromPreserveHiddenFields(TaskDescription other) {
mLabel = other.mLabel;
mIcon = other.mIcon;
- mIconRes = other.mIconRes;
mIconFilename = other.mIconFilename;
mColorPrimary = other.mColorPrimary;
if (other.mColorBackground != 0) {
@@ -1144,14 +1245,6 @@ public class ActivityManager {
}
/**
- * Sets the icon resource for this task description.
- * @hide
- */
- public void setIcon(int iconRes) {
- mIconRes = iconRes;
- }
-
- /**
* Moves the icon bitmap reference from an actual Bitmap to a file containing the
* bitmap.
* @hide
@@ -1179,13 +1272,6 @@ public class ActivityManager {
}
/** @hide */
- @TestApi
- public int getIconResource() {
- return mIconRes;
- }
-
- /** @hide */
- @TestApi
public String getIconFilename() {
return mIconFilename;
}
@@ -1251,10 +1337,7 @@ public class ActivityManager {
Integer.toHexString(mColorBackground));
}
if (mIconFilename != null) {
- out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename);
- }
- if (mIconRes != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, Integer.toString(mIconRes));
+ out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename);
}
}
@@ -1266,10 +1349,8 @@ public class ActivityManager {
setPrimaryColor((int) Long.parseLong(attrValue, 16));
} else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) {
setBackgroundColor((int) Long.parseLong(attrValue, 16));
- } else if (ATTR_TASKDESCRIPTIONICON_FILENAME.equals(attrName)) {
+ } else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) {
setIconFilename(attrValue);
- } else if (ATTR_TASKDESCRIPTIONICON_RESOURCE.equals(attrName)) {
- setIcon(Integer.parseInt(attrValue, 10));
}
}
@@ -1292,7 +1373,6 @@ public class ActivityManager {
dest.writeInt(1);
mIcon.writeToParcel(dest, 0);
}
- dest.writeInt(mIconRes);
dest.writeInt(mColorPrimary);
dest.writeInt(mColorBackground);
dest.writeInt(mStatusBarColor);
@@ -1308,7 +1388,6 @@ public class ActivityManager {
public void readFromParcel(Parcel source) {
mLabel = source.readInt() > 0 ? source.readString() : null;
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
- mIconRes = source.readInt();
mColorPrimary = source.readInt();
mColorBackground = source.readInt();
mStatusBarColor = source.readInt();
@@ -1329,8 +1408,8 @@ public class ActivityManager {
@Override
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
- " IconRes: " + mIconRes + " IconFilename: " + mIconFilename +
- " colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground +
+ " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
+ " colorBackground: " + mColorBackground +
" statusBarColor: " + mColorBackground +
" navigationBarColor: " + mNavigationBarColor;
}
@@ -1492,6 +1571,7 @@ public class ActivityManager {
}
dest.writeInt(stackId);
dest.writeInt(userId);
+ dest.writeLong(firstActiveTime);
dest.writeLong(lastActiveTime);
dest.writeInt(affiliatedTaskId);
dest.writeInt(affiliatedTaskColor);
@@ -1520,6 +1600,7 @@ public class ActivityManager {
TaskDescription.CREATOR.createFromParcel(source) : null;
stackId = source.readInt();
userId = source.readInt();
+ firstActiveTime = source.readLong();
lastActiveTime = source.readLong();
affiliatedTaskId = source.readInt();
affiliatedTaskColor = source.readInt();
@@ -1562,6 +1643,31 @@ public class ActivityManager {
public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
/**
+ * Provides a list that contains recent tasks for all
+ * profiles of a user.
+ * @hide
+ */
+ public static final int RECENT_INCLUDE_PROFILES = 0x0004;
+
+ /**
+ * Ignores all tasks that are on the home stack.
+ * @hide
+ */
+ public static final int RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS = 0x0008;
+
+ /**
+ * Ignores the top task in the docked stack.
+ * @hide
+ */
+ public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010;
+
+ /**
+ * Ignores all tasks that are on the pinned stack.
+ * @hide
+ */
+ public static final int RECENT_INGORE_PINNED_STACK_TASKS = 0x0020;
+
+ /**
* <p></p>Return a list of the tasks that the user has recently launched, with
* the most recent being first and older ones after in order.
*
@@ -1596,7 +1702,33 @@ public class ActivityManager {
public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
throws SecurityException {
try {
- return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList();
+ return getService().getRecentTasks(maxNum,
+ flags, UserHandle.myUserId()).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Same as {@link #getRecentTasks(int, int)} but returns the recent tasks for a
+ * specific user. It requires holding
+ * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many tasks the
+ * user has started and the maximum number the system can remember.
+ * @param flags Information about what to return. May be any combination
+ * of {@link #RECENT_WITH_EXCLUDED} and {@link #RECENT_IGNORE_UNAVAILABLE}.
+ *
+ * @return Returns a list of RecentTaskInfo records describing each of
+ * the recent tasks. Most recently activated tasks go first.
+ *
+ * @hide
+ */
+ public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId)
+ throws SecurityException {
+ try {
+ return getService().getRecentTasks(maxNum,
+ flags, userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1889,6 +2021,22 @@ public class ActivityManager {
}
/**
+ * Completely remove the given task.
+ *
+ * @param taskId Identifier of the task to be removed.
+ * @return Returns true if the given task was found and removed.
+ *
+ * @hide
+ */
+ public boolean removeTask(int taskId) throws SecurityException {
+ try {
+ return getService().removeTask(taskId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the windowing mode for a specific task. Only works on tasks of type
* {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
* @param taskId The id of the task to set the windowing mode for.
diff --git a/android/app/ActivityOptions.java b/android/app/ActivityOptions.java
index b62e4c2d..a68c3a5c 100644
--- a/android/app/ActivityOptions.java
+++ b/android/app/ActivityOptions.java
@@ -17,13 +17,13 @@
package android.app;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -159,12 +159,6 @@ public class ActivityOptions {
private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
/**
- * Whether the activity should be launched into LockTask mode.
- * @see #setLockTaskMode(boolean)
- */
- private static final String KEY_LOCK_TASK_MODE = "android:activity.lockTaskMode";
-
- /**
* The display id the activity should be launched into.
* @see #setLaunchDisplayId(int)
* @hide
@@ -285,7 +279,6 @@ public class ActivityOptions {
private int mResultCode;
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
- private boolean mLockTaskMode = false;
private int mLaunchDisplayId = INVALID_DISPLAY;
@WindowConfiguration.WindowingMode
private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -877,7 +870,6 @@ public class ActivityOptions {
mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
break;
}
- mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
@@ -1064,37 +1056,6 @@ public class ActivityOptions {
}
/**
- * Gets whether the activity is to be launched into LockTask mode.
- * @return {@code true} if the activity is to be launched into LockTask mode.
- * @see Activity#startLockTask()
- * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
- */
- public boolean getLockTaskMode() {
- return mLockTaskMode;
- }
-
- /**
- * Sets whether the activity is to be launched into LockTask mode.
- *
- * Use this option to start an activity in LockTask mode. Note that only apps permitted by
- * {@link android.app.admin.DevicePolicyManager} can run in LockTask mode. Therefore, if
- * {@link android.app.admin.DevicePolicyManager#isLockTaskPermitted(String)} returns
- * {@code false} for the package of the target activity, a {@link SecurityException} will be
- * thrown during {@link Context#startActivity(Intent, Bundle)}.
- *
- * Defaults to {@code false} if not set.
- *
- * @param lockTaskMode {@code true} if the activity is to be launched into LockTask mode.
- * @return {@code this} {@link ActivityOptions} instance.
- * @see Activity#startLockTask()
- * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
- */
- public ActivityOptions setLockTaskMode(boolean lockTaskMode) {
- mLockTaskMode = lockTaskMode;
- return this;
- }
-
- /**
* Gets the id of the display where activity should be launched.
* @return The id of the display where activity should be launched,
* {@link android.view.Display#INVALID_DISPLAY} if not set.
@@ -1287,7 +1248,6 @@ public class ActivityOptions {
mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
- mLockTaskMode = otherOptions.mLockTaskMode;
mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
@@ -1362,7 +1322,6 @@ public class ActivityOptions {
b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
break;
}
- b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
diff --git a/android/app/KeyguardManager.java b/android/app/KeyguardManager.java
index 1fe29004..54f74b15 100644
--- a/android/app/KeyguardManager.java
+++ b/android/app/KeyguardManager.java
@@ -387,6 +387,8 @@ public class KeyguardManager {
* such as the Home key and the right soft keys, don't work.
*
* @return true if in keyguard restricted input mode.
+ *
+ * @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode
*/
public boolean inKeyguardRestrictedInputMode() {
try {
diff --git a/android/app/NotificationChannel.java b/android/app/NotificationChannel.java
index c06ad3f3..47063f08 100644
--- a/android/app/NotificationChannel.java
+++ b/android/app/NotificationChannel.java
@@ -32,8 +32,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.Preconditions;
-
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
diff --git a/android/app/NotificationManager.java b/android/app/NotificationManager.java
index a52dc1e4..eb52cb7f 100644
--- a/android/app/NotificationManager.java
+++ b/android/app/NotificationManager.java
@@ -934,14 +934,8 @@ public class NotificationManager {
public static final int PRIORITY_CATEGORY_CALLS = 1 << 3;
/** Calls from repeat callers are prioritized. */
public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4;
- /** Alarms are prioritized */
- public static final int PRIORITY_CATEGORY_ALARMS = 1 << 5;
- /** Media, system, game (catch-all for non-never suppressible sounds) are prioritized */
- public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 1 << 6;
private static final int[] ALL_PRIORITY_CATEGORIES = {
- PRIORITY_CATEGORY_ALARMS,
- PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
PRIORITY_CATEGORY_REMINDERS,
PRIORITY_CATEGORY_EVENTS,
PRIORITY_CATEGORY_MESSAGES,
@@ -1141,9 +1135,6 @@ public class NotificationManager {
case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES";
case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS";
case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS";
- case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS";
- case PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER:
- return "PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER";
default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
}
}
diff --git a/android/app/StatusBarManager.java b/android/app/StatusBarManager.java
index 23c4166d..8987bc02 100644
--- a/android/app/StatusBarManager.java
+++ b/android/app/StatusBarManager.java
@@ -73,16 +73,15 @@ public class StatusBarManager {
public static final int DISABLE2_QUICK_SETTINGS = 1;
public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
- public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
public static final int DISABLE2_NONE = 0x00000000;
public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
- | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
+ | DISABLE2_NOTIFICATION_SHADE;
@IntDef(flag = true,
value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
- DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS})
+ DISABLE2_NOTIFICATION_SHADE})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
diff --git a/android/app/TaskStackListener.java b/android/app/TaskStackListener.java
index 895d12a7..402e2095 100644
--- a/android/app/TaskStackListener.java
+++ b/android/app/TaskStackListener.java
@@ -77,7 +77,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
}
@Override
- public void onTaskRemovalStarted(int taskId) throws RemoteException {
+ public void onTaskRemovalStarted(int taskId) {
}
@Override
@@ -91,10 +91,11 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
}
@Override
- public void onTaskProfileLocked(int taskId, int userId) throws RemoteException {
+ public void onTaskProfileLocked(int taskId, int userId) {
}
@Override
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
+ public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+ throws RemoteException {
}
}
diff --git a/android/app/WallpaperManager.java b/android/app/WallpaperManager.java
index 081bd814..942cc995 100644
--- a/android/app/WallpaperManager.java
+++ b/android/app/WallpaperManager.java
@@ -388,12 +388,11 @@ public class WallpaperManager {
public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
@SetWallpaperFlags int which) {
- return peekWallpaperBitmap(context, returnDefault, which, context.getUserId(),
- false /* hardware */);
+ return peekWallpaperBitmap(context, returnDefault, which, context.getUserId());
}
public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
- @SetWallpaperFlags int which, int userId, boolean hardware) {
+ @SetWallpaperFlags int which, int userId) {
if (mService != null) {
try {
if (!mService.isWallpaperSupported(context.getOpPackageName())) {
@@ -410,7 +409,7 @@ public class WallpaperManager {
mCachedWallpaper = null;
mCachedWallpaperUserId = 0;
try {
- mCachedWallpaper = getCurrentWallpaperLocked(context, userId, hardware);
+ mCachedWallpaper = getCurrentWallpaperLocked(context, userId);
mCachedWallpaperUserId = userId;
} catch (OutOfMemoryError e) {
Log.w(TAG, "Out of memory loading the current wallpaper: " + e);
@@ -448,7 +447,7 @@ public class WallpaperManager {
}
}
- private Bitmap getCurrentWallpaperLocked(Context context, int userId, boolean hardware) {
+ private Bitmap getCurrentWallpaperLocked(Context context, int userId) {
if (mService == null) {
Log.w(TAG, "WallpaperService not running");
return null;
@@ -461,9 +460,6 @@ public class WallpaperManager {
if (fd != null) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
- if (hardware) {
- options.inPreferredConfig = Bitmap.Config.HARDWARE;
- }
return BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, options);
} catch (OutOfMemoryError e) {
@@ -818,23 +814,12 @@ public class WallpaperManager {
}
/**
- * Like {@link #getDrawable()} but returns a Bitmap with default {@link Bitmap.Config}.
- *
- * @hide
- */
- public Bitmap getBitmap() {
- return getBitmap(false);
- }
-
- /**
* Like {@link #getDrawable()} but returns a Bitmap.
*
- * @param hardware Asks for a hardware backed bitmap.
- * @see Bitmap.Config#HARDWARE
* @hide
*/
- public Bitmap getBitmap(boolean hardware) {
- return getBitmapAsUser(mContext.getUserId(), hardware);
+ public Bitmap getBitmap() {
+ return getBitmapAsUser(mContext.getUserId());
}
/**
@@ -842,8 +827,8 @@ public class WallpaperManager {
*
* @hide
*/
- public Bitmap getBitmapAsUser(int userId, boolean hardware) {
- return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware);
+ public Bitmap getBitmapAsUser(int userId) {
+ return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId);
}
/**
diff --git a/android/app/WindowConfiguration.java b/android/app/WindowConfiguration.java
index 251863ca..6b405384 100644
--- a/android/app/WindowConfiguration.java
+++ b/android/app/WindowConfiguration.java
@@ -511,8 +511,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
}
- /** @hide */
- public static String windowingModeToString(@WindowingMode int windowingMode) {
+ private static String windowingModeToString(@WindowingMode int windowingMode) {
switch (windowingMode) {
case WINDOWING_MODE_UNDEFINED: return "undefined";
case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
diff --git a/android/app/assist/AssistStructure.java b/android/app/assist/AssistStructure.java
index e491a4f9..d9b7cd7e 100644
--- a/android/app/assist/AssistStructure.java
+++ b/android/app/assist/AssistStructure.java
@@ -616,9 +616,6 @@ public class AssistStructure implements Parcelable {
CharSequence[] mAutofillOptions;
boolean mSanitized;
HtmlInfo mHtmlInfo;
- int mMinEms = -1;
- int mMaxEms = -1;
- int mMaxLength = -1;
// POJO used to override some autofill-related values when the node is parcelized.
// Not written to parcel.
@@ -716,9 +713,6 @@ public class AssistStructure implements Parcelable {
if (p instanceof HtmlInfo) {
mHtmlInfo = (HtmlInfo) p;
}
- mMinEms = in.readInt();
- mMaxEms = in.readInt();
- mMaxLength = in.readInt();
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
mX = in.readInt();
@@ -882,9 +876,6 @@ public class AssistStructure implements Parcelable {
} else {
out.writeParcelable(null, 0);
}
- out.writeInt(mMinEms);
- out.writeInt(mMaxEms);
- out.writeInt(mMaxLength);
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
out.writeInt(mX);
@@ -1453,39 +1444,6 @@ public class AssistStructure implements Parcelable {
public ViewNode getChildAt(int index) {
return mChildren[index];
}
-
- /**
- * Returns the minimum width in ems of the text associated with this node, or {@code -1}
- * if not supported by the node.
- *
- * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
- * not for assist purposes.
- */
- public int getMinTextEms() {
- return mMinEms;
- }
-
- /**
- * Returns the maximum width in ems of the text associated with this node, or {@code -1}
- * if not supported by the node.
- *
- * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
- * not for assist purposes.
- */
- public int getMaxTextEms() {
- return mMaxEms;
- }
-
- /**
- * Returns the maximum length of the text associated with this node node, or {@code -1}
- * if not supported by the node or not set.
- *
- * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
- * not for assist purposes.
- */
- public int getMaxTextLength() {
- return mMaxLength;
- }
}
/**
@@ -1818,21 +1776,6 @@ public class AssistStructure implements Parcelable {
}
@Override
- public void setMinTextEms(int minEms) {
- mNode.mMinEms = minEms;
- }
-
- @Override
- public void setMaxTextEms(int maxEms) {
- mNode.mMaxEms = maxEms;
- }
-
- @Override
- public void setMaxTextLength(int maxLength) {
- mNode.mMaxLength = maxLength;
- }
-
- @Override
public void setDataIsSensitive(boolean sensitive) {
mNode.mSanitized = !sensitive;
}
diff --git a/android/app/job/JobScheduler.java b/android/app/job/JobScheduler.java
index 0deb2e13..3868439f 100644
--- a/android/app/job/JobScheduler.java
+++ b/android/app/job/JobScheduler.java
@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.ClipData;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -39,18 +40,16 @@ import java.util.List;
* and how to construct them. You will construct these JobInfo objects and pass them to the
* JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the
* system will execute this job on your application's {@link android.app.job.JobService}.
- * You identify the service component that implements the logic for your job when you
- * construct the JobInfo using
+ * You identify which JobService is meant to execute the logic for your job when you create the
+ * JobInfo with
* {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.
* </p>
* <p>
- * The framework will be intelligent about when it executes jobs, and attempt to batch
- * and defer them as much as possible. Typically if you don't specify a deadline on a job, it
- * can be run at any moment depending on the current state of the JobScheduler's internal queue.
- * <p>
- * While a job is running, the system holds a wakelock on behalf of your app. For this reason,
- * you do not need to take any action to guarantee that the device stays awake for the
- * duration of the job.
+ * The framework will be intelligent about when you receive your callbacks, and attempt to batch
+ * and defer them as much as possible. Typically if you don't specify a deadline on your job, it
+ * can be run at any moment depending on the current state of the JobScheduler's internal queue,
+ * however it might be deferred as long as until the next time the device is connected to a power
+ * source.
* </p>
* <p>You do not
* instantiate this class directly; instead, retrieve it through
@@ -142,34 +141,30 @@ public abstract class JobScheduler {
int userId, String tag);
/**
- * Cancel the specified job. If the job is currently executing, it is stopped
- * immediately and the return value from its {@link JobService#onStopJob(JobParameters)}
- * method is ignored.
- *
- * @param jobId unique identifier for the job to be canceled, as supplied to
- * {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName)
- * JobInfo.Builder(int, android.content.ComponentName)}.
+ * Cancel a job that is pending in the JobScheduler.
+ * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
+ * {@link #getAllPendingJobs()}.
*/
public abstract void cancel(int jobId);
/**
- * Cancel <em>all</em> jobs that have been scheduled by the calling application.
+ * Cancel all jobs that have been registered with the JobScheduler by this package.
*/
public abstract void cancelAll();
/**
- * Retrieve all jobs that have been scheduled by the calling application.
+ * Retrieve all jobs for this package that are pending in the JobScheduler.
*
- * @return a list of all of the app's scheduled jobs. This includes jobs that are
- * currently started as well as those that are still waiting to run.
+ * @return a list of all the jobs registered by this package that have not
+ * yet been executed.
*/
public abstract @NonNull List<JobInfo> getAllPendingJobs();
/**
- * Look up the description of a scheduled job.
+ * Retrieve a specific job for this package that is pending in the
+ * JobScheduler.
*
- * @return The {@link JobInfo} description of the given scheduled job, or {@code null}
- * if the supplied job ID does not correspond to any job.
+ * @return job registered by this package that has not yet been executed.
*/
public abstract @Nullable JobInfo getPendingJob(int jobId);
}
diff --git a/android/app/job/JobService.java b/android/app/job/JobService.java
index 69afed20..9096b47b 100644
--- a/android/app/job/JobService.java
+++ b/android/app/job/JobService.java
@@ -18,7 +18,16 @@ package android.app.job;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
/**
* <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
@@ -46,7 +55,7 @@ public abstract class JobService extends Service {
* </pre>
*
* <p>If a job service is declared in the manifest but not protected with this
- * permission, that service will be ignored by the system.
+ * permission, that service will be ignored by the OS.
*/
public static final String PERMISSION_BIND =
"android.permission.BIND_JOB_SERVICE";
@@ -72,36 +81,14 @@ public abstract class JobService extends Service {
}
/**
- * Called to indicate that the job has begun executing. Override this method with the
- * logic for your job. Like all other component lifecycle callbacks, this method executes
- * on your application's main thread.
- * <p>
- * Return {@code true} from this method if your job needs to continue running. If you
- * do this, the job remains active until you call
- * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed
- * its work, or until the job's required constraints are no longer satisfied. For
- * example, if the job was scheduled using
- * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)},
- * it will be immediately halted by the system if the user unplugs the device from power,
- * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app
- * will be expected to shut down all ongoing work connected with that job.
- * <p>
- * The system holds a wakelock on behalf of your app as long as your job is executing.
- * This wakelock is acquired before this method is invoked, and is not released until either
- * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes
- * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down
- * prematurely.
- * <p>
- * Returning {@code false} from this method means your job is already finished. The
- * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)}
- * will not be invoked.
+ * Override this method with the callback logic for your job. Any such logic needs to be
+ * performed on a separate thread, as this function is executed on your application's main
+ * thread.
*
- * @param params Parameters specifying info about this job, including the optional
- * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
- * This object serves to identify this specific running job instance when calling
- * {@link #jobFinished(JobParameters, boolean)}.
- * @return {@code true} if your service will continue running, using a separate thread
- * when appropriate. {@code false} means that this job has completed its work.
+ * @param params Parameters specifying info about this job, including the extras bundle you
+ * optionally provided at job-creation time.
+ * @return True if your service needs to process the work (on a separate thread). False if
+ * there's no more work to be done for this job.
*/
public abstract boolean onStartJob(JobParameters params);
@@ -114,44 +101,37 @@ public abstract class JobService extends Service {
* {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
* job was executing the user toggled WiFi. Another example is if you had specified
* {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
- * idle maintenance window. You are solely responsible for the behavior of your application
- * upon receipt of this message; your app will likely start to misbehave if you ignore it.
- * <p>
- * Once this method returns, the system releases the wakelock that it is holding on
- * behalf of the job.</p>
+ * idle maintenance window. You are solely responsible for the behaviour of your application
+ * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
+ * immediate repercussion is that the system will cease holding a wakelock for you.</p>
*
- * @param params The parameters identifying this job, as supplied to
- * the job in the {@link #onStartJob(JobParameters)} callback.
- * @return {@code true} to indicate to the JobManager whether you'd like to reschedule
- * this job based on the retry criteria provided at job creation-time; or {@code false}
- * to end the job entirely. Regardless of the value returned, your job must stop executing.
+ * @param params Parameters specifying info about this job.
+ * @return True to indicate to the JobManager whether you'd like to reschedule this job based
+ * on the retry criteria provided at job creation-time. False to drop the job. Regardless of
+ * the value returned, your job must stop executing.
*/
public abstract boolean onStopJob(JobParameters params);
/**
- * Call this to inform the JobScheduler that the job has finished its work. When the
- * system receives this message, it releases the wakelock being held for the job.
+ * Call this to inform the JobManager you've finished executing. This can be called from any
+ * thread, as it will ultimately be run on your application's main thread. When the system
+ * receives this message it will release the wakelock being held.
* <p>
- * You can request that the job be scheduled again by passing {@code true} as
- * the <code>wantsReschedule</code> parameter. This will apply back-off policy
- * for the job; this policy can be adjusted through the
- * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method
- * when the job is originally scheduled. The job's initial
- * requirements are preserved when jobs are rescheduled, regardless of backed-off
- * policy.
- * <p class="note">
- * A job running while the device is dozing will not be rescheduled with the normal back-off
- * policy. Instead, the job will be re-added to the queue and executed again during
- * a future idle maintenance window.
+ * You can specify post-execution behaviour to the scheduler here with
+ * <code>needsReschedule </code>. This will apply a back-off timer to your job based on
+ * the default, or what was set with
+ * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original
+ * requirements are always honoured even for a backed-off job. Note that a job running in
+ * idle mode will not be backed-off. Instead what will happen is the job will be re-added
+ * to the queue and re-executed within a future idle maintenance window.
* </p>
*
- * @param params The parameters identifying this job, as supplied to
- * the job in the {@link #onStartJob(JobParameters)} callback.
- * @param wantsReschedule {@code true} if this job should be rescheduled according
- * to the back-off criteria specified when it was first scheduled; {@code false}
- * otherwise.
+ * @param params Parameters specifying system-provided info about this job, this was given to
+ * your application in {@link #onStartJob(JobParameters)}.
+ * @param needsReschedule True if this job should be rescheduled according to the back-off
+ * criteria specified at schedule-time. False otherwise.
*/
- public final void jobFinished(JobParameters params, boolean wantsReschedule) {
- mEngine.jobFinished(params, wantsReschedule);
+ public final void jobFinished(JobParameters params, boolean needsReschedule) {
+ mEngine.jobFinished(params, needsReschedule);
}
-}
+} \ No newline at end of file
diff --git a/android/app/usage/UsageStatsManager.java b/android/app/usage/UsageStatsManager.java
index fd579fce..051dccbd 100644
--- a/android/app/usage/UsageStatsManager.java
+++ b/android/app/usage/UsageStatsManager.java
@@ -48,10 +48,10 @@ import java.util.Map;
* </pre>
* A request for data in the middle of a time interval will include that interval.
* <p/>
- * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS.
- * However, declaring the permission implies intention to use the API and the user of the device
- * still needs to grant permission through the Settings application.
- * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}
+ * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS, which
+ * is a system-level permission and will not be granted to third-party apps. However, declaring
+ * the permission implies intention to use the API and the user of the device can grant permission
+ * through the Settings application.
*/
@SystemService(Context.USAGE_STATS_SERVICE)
public final class UsageStatsManager {
@@ -122,7 +122,7 @@ public final class UsageStatsManager {
* @param intervalType The time interval by which the stats are aggregated.
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
- * @return A list of {@link UsageStats}
+ * @return A list of {@link UsageStats} or null if none are available.
*
* @see #INTERVAL_DAILY
* @see #INTERVAL_WEEKLY
@@ -139,7 +139,7 @@ public final class UsageStatsManager {
return slice.getList();
}
} catch (RemoteException e) {
- // fallthrough and return the empty list.
+ // fallthrough and return null.
}
return Collections.emptyList();
}
@@ -152,7 +152,7 @@ public final class UsageStatsManager {
* @param intervalType The time interval by which the stats are aggregated.
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
- * @return A list of {@link ConfigurationStats}
+ * @return A list of {@link ConfigurationStats} or null if none are available.
*/
public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
long endTime) {
@@ -185,7 +185,7 @@ public final class UsageStatsManager {
return iter;
}
} catch (RemoteException e) {
- // fallthrough and return empty result.
+ // fallthrough and return null
}
return sEmptyResults;
}
@@ -197,7 +197,8 @@ public final class UsageStatsManager {
*
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
- * @return A {@link java.util.Map} keyed by package name
+ * @return A {@link java.util.Map} keyed by package name, or null if no stats are
+ * available.
*/
public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
diff --git a/android/arch/lifecycle/ActivityFullLifecycleTest.java b/android/arch/lifecycle/ActivityFullLifecycleTest.java
index 78dd0150..ee4e661a 100644
--- a/android/arch/lifecycle/ActivityFullLifecycleTest.java
+++ b/android/arch/lifecycle/ActivityFullLifecycleTest.java
@@ -16,43 +16,48 @@
package android.arch.lifecycle;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.CREATE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
-import static android.arch.lifecycle.TestUtils.flatMap;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle.Event;
-import android.arch.lifecycle.testapp.CollectingLifecycleOwner;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
+import android.arch.lifecycle.testapp.CollectingActivity;
import android.arch.lifecycle.testapp.FrameworkLifecycleRegistryActivity;
+import android.arch.lifecycle.testapp.FullLifecycleTestActivity;
+import android.arch.lifecycle.testapp.SupportLifecycleRegistryActivity;
import android.arch.lifecycle.testapp.TestEvent;
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
-import android.support.v4.util.Pair;
+import android.util.Pair;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.util.ArrayList;
import java.util.List;
@SmallTest
@RunWith(Parameterized.class)
public class ActivityFullLifecycleTest {
@Rule
- public final ActivityTestRule<? extends CollectingLifecycleOwner> activityTestRule;
+ public ActivityTestRule activityTestRule =
+ new ActivityTestRule<>(FullLifecycleTestActivity.class);
@Parameterized.Parameters
public static Class[] params() {
- return new Class[]{CollectingSupportActivity.class,
+ return new Class[]{FullLifecycleTestActivity.class,
+ SupportLifecycleRegistryActivity.class,
FrameworkLifecycleRegistryActivity.class};
}
@@ -63,13 +68,28 @@ public class ActivityFullLifecycleTest {
@Test
- public void testFullLifecycle() throws Throwable {
- CollectingLifecycleOwner owner = activityTestRule.getActivity();
- TestUtils.waitTillResumed(owner, activityTestRule);
- activityTestRule.finishActivity();
+ public void testFullLifecycle() throws InterruptedException {
+ Activity activity = activityTestRule.getActivity();
+ List<Pair<TestEvent, Event>> results = ((CollectingActivity) activity)
+ .waitForCollectedEvents();
- TestUtils.waitTillDestroyed(owner, activityTestRule);
- List<Pair<TestEvent, Event>> results = owner.copyCollectedEvents();
- assertThat(results, is(flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY)));
+ Event[] expectedEvents =
+ new Event[]{ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
+
+ List<Pair<TestEvent, Event>> expected = new ArrayList<>();
+ boolean beforeResume = true;
+ for (Event i : expectedEvents) {
+ if (beforeResume) {
+ expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
+ expected.add(new Pair<>(LIFECYCLE_EVENT, i));
+ } else {
+ expected.add(new Pair<>(LIFECYCLE_EVENT, i));
+ expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
+ }
+ if (i == ON_RESUME) {
+ beforeResume = false;
+ }
+ }
+ assertThat(results, is(expected));
}
}
diff --git a/android/arch/lifecycle/AndroidViewModel.java b/android/arch/lifecycle/AndroidViewModel.java
index 106b2ef0..2c7e1739 100644
--- a/android/arch/lifecycle/AndroidViewModel.java
+++ b/android/arch/lifecycle/AndroidViewModel.java
@@ -16,9 +16,7 @@
package android.arch.lifecycle;
-import android.annotation.SuppressLint;
import android.app.Application;
-import android.support.annotation.NonNull;
/**
* Application context aware {@link ViewModel}.
@@ -27,19 +25,16 @@ import android.support.annotation.NonNull;
* <p>
*/
public class AndroidViewModel extends ViewModel {
- @SuppressLint("StaticFieldLeak")
private Application mApplication;
- public AndroidViewModel(@NonNull Application application) {
+ public AndroidViewModel(Application application) {
mApplication = application;
}
/**
* Return the application.
*/
- @NonNull
public <T extends Application> T getApplication() {
- //noinspection unchecked
return (T) mApplication;
}
}
diff --git a/android/arch/lifecycle/ClassesInfoCache.java b/android/arch/lifecycle/ClassesInfoCache.java
index d88e2762..f077daed 100644
--- a/android/arch/lifecycle/ClassesInfoCache.java
+++ b/android/arch/lifecycle/ClassesInfoCache.java
@@ -46,7 +46,7 @@ class ClassesInfoCache {
return mHasLifecycleMethods.get(klass);
}
- Method[] methods = getDeclaredMethods(klass);
+ Method[] methods = klass.getDeclaredMethods();
for (Method method : methods) {
OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
if (annotation != null) {
@@ -64,18 +64,6 @@ class ClassesInfoCache {
return false;
}
- private Method[] getDeclaredMethods(Class klass) {
- try {
- return klass.getDeclaredMethods();
- } catch (NoClassDefFoundError e) {
- throw new IllegalArgumentException("The observer class has some methods that use "
- + "newer APIs which are not available in the current OS version. Lifecycles "
- + "cannot access even other methods so you should make sure that your "
- + "observer classes only access framework classes that are available "
- + "in your min API level OR use lifecycle:compiler annotation processor.", e);
- }
- }
-
CallbackInfo getInfo(Class klass) {
CallbackInfo existing = mCallbackMap.get(klass);
if (existing != null) {
@@ -118,7 +106,7 @@ class ClassesInfoCache {
}
}
- Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);
+ Method[] methods = declaredMethods != null ? declaredMethods : klass.getDeclaredMethods();
boolean hasLifecycleMethods = false;
for (Method method : methods) {
OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
diff --git a/android/arch/lifecycle/ComputableLiveData.java b/android/arch/lifecycle/ComputableLiveData.java
index 1ddcb1a9..f1352446 100644
--- a/android/arch/lifecycle/ComputableLiveData.java
+++ b/android/arch/lifecycle/ComputableLiveData.java
@@ -1,136 +1,9 @@
-/*
- * 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.
- */
-
+//ComputableLiveData interface for tests
package android.arch.lifecycle;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.annotation.WorkerThread;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A LiveData class that can be invalidated & computed on demand.
- * <p>
- * This is an internal class for now, might be public if we see the necessity.
- *
- * @param <T> The type of the live data
- * @hide internal
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+import android.arch.lifecycle.LiveData;
public abstract class ComputableLiveData<T> {
-
- private final LiveData<T> mLiveData;
-
- private AtomicBoolean mInvalid = new AtomicBoolean(true);
- private AtomicBoolean mComputing = new AtomicBoolean(false);
-
- /**
- * Creates a computable live data which is computed when there are active observers.
- * <p>
- * It can also be invalidated via {@link #invalidate()} which will result in a call to
- * {@link #compute()} if there are active observers (or when they start observing)
- */
- @SuppressWarnings("WeakerAccess")
- public ComputableLiveData() {
- mLiveData = new LiveData<T>() {
- @Override
- protected void onActive() {
- // TODO if we make this class public, we should accept an executor
- ArchTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
- }
- };
- }
-
- /**
- * Returns the LiveData managed by this class.
- *
- * @return A LiveData that is controlled by ComputableLiveData.
- */
- @SuppressWarnings("WeakerAccess")
- @NonNull
- public LiveData<T> getLiveData() {
- return mLiveData;
- }
-
- @VisibleForTesting
- final Runnable mRefreshRunnable = new Runnable() {
- @WorkerThread
- @Override
- public void run() {
- boolean computed;
- do {
- computed = false;
- // compute can happen only in 1 thread but no reason to lock others.
- if (mComputing.compareAndSet(false, true)) {
- // as long as it is invalid, keep computing.
- try {
- T value = null;
- while (mInvalid.compareAndSet(true, false)) {
- computed = true;
- value = compute();
- }
- if (computed) {
- mLiveData.postValue(value);
- }
- } finally {
- // release compute lock
- mComputing.set(false);
- }
- }
- // check invalid after releasing compute lock to avoid the following scenario.
- // Thread A runs compute()
- // Thread A checks invalid, it is false
- // Main thread sets invalid to true
- // Thread B runs, fails to acquire compute lock and skips
- // Thread A releases compute lock
- // We've left invalid in set state. The check below recovers.
- } while (computed && mInvalid.get());
- }
- };
-
- // invalidation check always happens on the main thread
- @VisibleForTesting
- final Runnable mInvalidationRunnable = new Runnable() {
- @MainThread
- @Override
- public void run() {
- boolean isActive = mLiveData.hasActiveObservers();
- if (mInvalid.compareAndSet(false, true)) {
- if (isActive) {
- // TODO if we make this class public, we should accept an executor.
- ArchTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
- }
- }
- }
- };
-
- /**
- * Invalidates the LiveData.
- * <p>
- * When there are active observers, this will trigger a call to {@link #compute()}.
- */
- public void invalidate() {
- ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
- }
-
- @SuppressWarnings("WeakerAccess")
- @WorkerThread
- protected abstract T compute();
+ public ComputableLiveData(){}
+ abstract protected T compute();
+ public LiveData<T> getLiveData() {return null;}
+ public void invalidate() {}
}
diff --git a/android/arch/lifecycle/DispatcherActivityCallbackTest.java b/android/arch/lifecycle/DispatcherActivityCallbackTest.java
new file mode 100644
index 00000000..86b25b60
--- /dev/null
+++ b/android/arch/lifecycle/DispatcherActivityCallbackTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DispatcherActivityCallbackTest {
+ @Test
+ public void onCreateFrameworkActivity() {
+ LifecycleDispatcher.DispatcherActivityCallback callback =
+ new LifecycleDispatcher.DispatcherActivityCallback();
+ Activity activity = mock(Activity.class);
+ checkReportFragment(callback, activity);
+ }
+
+ @Test
+ public void onCreateFragmentActivity() {
+ LifecycleDispatcher.DispatcherActivityCallback callback =
+ new LifecycleDispatcher.DispatcherActivityCallback();
+ FragmentActivity activity = mock(FragmentActivity.class);
+ FragmentManager fragmentManager = mock(FragmentManager.class);
+ when(activity.getSupportFragmentManager()).thenReturn(fragmentManager);
+
+ checkReportFragment(callback, activity);
+
+ verify(activity).getSupportFragmentManager();
+ verify(fragmentManager).registerFragmentLifecycleCallbacks(
+ any(FragmentManager.FragmentLifecycleCallbacks.class), eq(true));
+ }
+
+ @SuppressLint("CommitTransaction")
+ private void checkReportFragment(LifecycleDispatcher.DispatcherActivityCallback callback,
+ Activity activity) {
+ android.app.FragmentManager fm = mock(android.app.FragmentManager.class);
+ FragmentTransaction transaction = mock(FragmentTransaction.class);
+ when(activity.getFragmentManager()).thenReturn(fm);
+ when(fm.beginTransaction()).thenReturn(transaction);
+ when(transaction.add(any(Fragment.class), anyString())).thenReturn(transaction);
+ callback.onActivityCreated(activity, mock(Bundle.class));
+ verify(activity).getFragmentManager();
+ verify(fm).beginTransaction();
+ verify(transaction).add(any(ReportFragment.class), anyString());
+ verify(transaction).commit();
+ }
+}
diff --git a/android/arch/lifecycle/Lifecycle.java b/android/arch/lifecycle/Lifecycle.java
index c0a2090c..02db5ff9 100644
--- a/android/arch/lifecycle/Lifecycle.java
+++ b/android/arch/lifecycle/Lifecycle.java
@@ -17,7 +17,6 @@
package android.arch.lifecycle;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
/**
* Defines an object that has an Android Lifecycle. {@link android.support.v4.app.Fragment Fragment}
@@ -84,7 +83,7 @@ public abstract class Lifecycle {
* @param observer The observer to notify.
*/
@MainThread
- public abstract void addObserver(@NonNull LifecycleObserver observer);
+ public abstract void addObserver(LifecycleObserver observer);
/**
* Removes the given observer from the observers list.
@@ -100,7 +99,7 @@ public abstract class Lifecycle {
* @param observer The observer to be removed.
*/
@MainThread
- public abstract void removeObserver(@NonNull LifecycleObserver observer);
+ public abstract void removeObserver(LifecycleObserver observer);
/**
* Returns the current state of the Lifecycle.
@@ -194,7 +193,7 @@ public abstract class Lifecycle {
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
*/
- public boolean isAtLeast(@NonNull State state) {
+ public boolean isAtLeast(State state) {
return compareTo(state) >= 0;
}
}
diff --git a/android/arch/lifecycle/LifecycleDispatcher.java b/android/arch/lifecycle/LifecycleDispatcher.java
new file mode 100644
index 00000000..9fdec959
--- /dev/null
+++ b/android/arch/lifecycle/LifecycleDispatcher.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+
+import android.app.Activity;
+import android.app.Application;
+import android.arch.lifecycle.Lifecycle.State;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * When initialized, it hooks into the Activity callback of the Application and observes
+ * Activities. It is responsible to hook in child-fragments to activities and fragments to report
+ * their lifecycle events. Another responsibility of this class is to mark as stopped all lifecycle
+ * providers related to an activity as soon it is not safe to run a fragment transaction in this
+ * activity.
+ */
+class LifecycleDispatcher {
+
+ private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ + ".LifecycleDispatcher.report_fragment_tag";
+
+ private static AtomicBoolean sInitialized = new AtomicBoolean(false);
+
+ static void init(Context context) {
+ if (sInitialized.getAndSet(true)) {
+ return;
+ }
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ @VisibleForTesting
+ static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
+ private final FragmentCallback mFragmentCallback;
+
+ DispatcherActivityCallback() {
+ mFragmentCallback = new FragmentCallback();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ if (activity instanceof FragmentActivity) {
+ ((FragmentActivity) activity).getSupportFragmentManager()
+ .registerFragmentLifecycleCallbacks(mFragmentCallback, true);
+ }
+ ReportFragment.injectIfNeededIn(activity);
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ if (activity instanceof FragmentActivity) {
+ markState((FragmentActivity) activity, CREATED);
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ if (activity instanceof FragmentActivity) {
+ markState((FragmentActivity) activity, CREATED);
+ }
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static class DestructionReportFragment extends Fragment {
+ @Override
+ public void onPause() {
+ super.onPause();
+ dispatch(ON_PAUSE);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ dispatch(ON_STOP);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ dispatch(ON_DESTROY);
+ }
+
+ protected void dispatch(Lifecycle.Event event) {
+ dispatchIfLifecycleOwner(getParentFragment(), event);
+ }
+ }
+
+ private static void markState(FragmentManager manager, State state) {
+ Collection<Fragment> fragments = manager.getFragments();
+ if (fragments == null) {
+ return;
+ }
+ for (Fragment fragment : fragments) {
+ if (fragment == null) {
+ continue;
+ }
+ markStateIn(fragment, state);
+ if (fragment.isAdded()) {
+ markState(fragment.getChildFragmentManager(), state);
+ }
+ }
+ }
+
+ private static void markStateIn(Object object, State state) {
+ if (object instanceof LifecycleRegistryOwner) {
+ LifecycleRegistry registry = ((LifecycleRegistryOwner) object).getLifecycle();
+ registry.markState(state);
+ }
+ }
+
+ private static void markState(FragmentActivity activity, State state) {
+ markStateIn(activity, state);
+ markState(activity.getSupportFragmentManager(), state);
+ }
+
+ private static void dispatchIfLifecycleOwner(Fragment fragment, Lifecycle.Event event) {
+ if (fragment instanceof LifecycleRegistryOwner) {
+ ((LifecycleRegistryOwner) fragment).getLifecycle().handleLifecycleEvent(event);
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ @VisibleForTesting
+ static class FragmentCallback extends FragmentManager.FragmentLifecycleCallbacks {
+
+ @Override
+ public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
+ dispatchIfLifecycleOwner(f, ON_CREATE);
+
+ if (!(f instanceof LifecycleRegistryOwner)) {
+ return;
+ }
+
+ if (f.getChildFragmentManager().findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
+ f.getChildFragmentManager().beginTransaction().add(new DestructionReportFragment(),
+ REPORT_FRAGMENT_TAG).commit();
+ }
+ }
+
+ @Override
+ public void onFragmentStarted(FragmentManager fm, Fragment f) {
+ dispatchIfLifecycleOwner(f, ON_START);
+ }
+
+ @Override
+ public void onFragmentResumed(FragmentManager fm, Fragment f) {
+ dispatchIfLifecycleOwner(f, ON_RESUME);
+ }
+ }
+}
diff --git a/android/arch/lifecycle/LifecycleOwner.java b/android/arch/lifecycle/LifecycleOwner.java
index 068bac1b..934cf3a2 100644
--- a/android/arch/lifecycle/LifecycleOwner.java
+++ b/android/arch/lifecycle/LifecycleOwner.java
@@ -16,8 +16,6 @@
package android.arch.lifecycle;
-import android.support.annotation.NonNull;
-
/**
* A class that has an Android lifecycle. These events can be used by custom components to
* handle lifecycle changes without implementing any code inside the Activity or the Fragment.
@@ -31,6 +29,5 @@ public interface LifecycleOwner {
*
* @return The lifecycle of the provider.
*/
- @NonNull
Lifecycle getLifecycle();
}
diff --git a/android/arch/lifecycle/LifecycleRegistry.java b/android/arch/lifecycle/LifecycleRegistry.java
index bf8aff79..b83e6b8a 100644
--- a/android/arch/lifecycle/LifecycleRegistry.java
+++ b/android/arch/lifecycle/LifecycleRegistry.java
@@ -29,12 +29,9 @@ import static android.arch.lifecycle.Lifecycle.State.RESUMED;
import static android.arch.lifecycle.Lifecycle.State.STARTED;
import android.arch.core.internal.FastSafeIterableMap;
-import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.util.Log;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
@@ -47,8 +44,6 @@ import java.util.Map.Entry;
*/
public class LifecycleRegistry extends Lifecycle {
- private static final String LOG_TAG = "LifecycleRegistry";
-
/**
* Custom list that keeps observers and can handle removals / additions during traversal.
*
@@ -64,12 +59,8 @@ public class LifecycleRegistry extends Lifecycle {
private State mState;
/**
* The provider that owns this Lifecycle.
- * Only WeakReference on LifecycleOwner is kept, so if somebody leaks Lifecycle, they won't leak
- * the whole Fragment / Activity. However, to leak Lifecycle object isn't great idea neither,
- * because it keeps strong references on all other listeners, so you'll leak all of them as
- * well.
*/
- private final WeakReference<LifecycleOwner> mLifecycleOwner;
+ private final LifecycleOwner mLifecycleOwner;
private int mAddingObserverCounter = 0;
@@ -95,19 +86,19 @@ public class LifecycleRegistry extends Lifecycle {
* @param provider The owner LifecycleOwner
*/
public LifecycleRegistry(@NonNull LifecycleOwner provider) {
- mLifecycleOwner = new WeakReference<>(provider);
+ mLifecycleOwner = provider;
mState = INITIALIZED;
}
/**
- * Moves the Lifecycle to the given state and dispatches necessary events to the observers.
+ * Only marks the current state as the given value. It doesn't dispatch any event to its
+ * listeners.
*
* @param state new state
*/
@SuppressWarnings("WeakerAccess")
- @MainThread
- public void markState(@NonNull State state) {
- moveToState(state);
+ public void markState(State state) {
+ mState = state;
}
/**
@@ -118,16 +109,8 @@ public class LifecycleRegistry extends Lifecycle {
*
* @param event The event that was received
*/
- public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
- State next = getStateAfter(event);
- moveToState(next);
- }
-
- private void moveToState(State next) {
- if (mState == next) {
- return;
- }
- mState = next;
+ public void handleLifecycleEvent(Lifecycle.Event event) {
+ mState = getStateAfter(event);
if (mHandlingEvent || mAddingObserverCounter != 0) {
mNewEventOccurred = true;
// we will figure out what to do on upper level.
@@ -157,7 +140,7 @@ public class LifecycleRegistry extends Lifecycle {
}
@Override
- public void addObserver(@NonNull LifecycleObserver observer) {
+ public void addObserver(LifecycleObserver observer) {
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
@@ -165,19 +148,15 @@ public class LifecycleRegistry extends Lifecycle {
if (previous != null) {
return;
}
- LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
- if (lifecycleOwner == null) {
- // it is null we should be destroyed. Fallback quickly
- return;
- }
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
+
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
- statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
+ statefulObserver.dispatchEvent(mLifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
@@ -199,7 +178,7 @@ public class LifecycleRegistry extends Lifecycle {
}
@Override
- public void removeObserver(@NonNull LifecycleObserver observer) {
+ public void removeObserver(LifecycleObserver observer) {
// we consciously decided not to send destruction events here in opposition to addObserver.
// Our reasons for that:
// 1. These events haven't yet happened at all. In contrast to events in addObservers, that
@@ -279,7 +258,7 @@ public class LifecycleRegistry extends Lifecycle {
throw new IllegalArgumentException("Unexpected state value " + state);
}
- private void forwardPass(LifecycleOwner lifecycleOwner) {
+ private void forwardPass() {
Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
mObserverMap.iteratorWithAdditions();
while (ascendingIterator.hasNext() && !mNewEventOccurred) {
@@ -288,13 +267,13 @@ public class LifecycleRegistry extends Lifecycle {
while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
pushParentState(observer.mState);
- observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
+ observer.dispatchEvent(mLifecycleOwner, upEvent(observer.mState));
popParentState();
}
}
}
- private void backwardPass(LifecycleOwner lifecycleOwner) {
+ private void backwardPass() {
Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
mObserverMap.descendingIterator();
while (descendingIterator.hasNext() && !mNewEventOccurred) {
@@ -304,7 +283,7 @@ public class LifecycleRegistry extends Lifecycle {
&& mObserverMap.contains(entry.getKey()))) {
Event event = downEvent(observer.mState);
pushParentState(getStateAfter(event));
- observer.dispatchEvent(lifecycleOwner, event);
+ observer.dispatchEvent(mLifecycleOwner, event);
popParentState();
}
}
@@ -313,22 +292,16 @@ public class LifecycleRegistry extends Lifecycle {
// happens only on the top of stack (never in reentrance),
// so it doesn't have to take in account parents
private void sync() {
- LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
- if (lifecycleOwner == null) {
- Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
- + "new events from it.");
- return;
- }
while (!isSynced()) {
mNewEventOccurred = false;
// no need to check eldest for nullability, because isSynced does it for us.
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
- backwardPass(lifecycleOwner);
+ backwardPass();
}
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
- forwardPass(lifecycleOwner);
+ forwardPass();
}
}
mNewEventOccurred = false;
diff --git a/android/arch/lifecycle/LifecycleRegistryOwner.java b/android/arch/lifecycle/LifecycleRegistryOwner.java
index 0c67fefe..38eeb6d3 100644
--- a/android/arch/lifecycle/LifecycleRegistryOwner.java
+++ b/android/arch/lifecycle/LifecycleRegistryOwner.java
@@ -16,8 +16,6 @@
package android.arch.lifecycle;
-import android.support.annotation.NonNull;
-
/**
* @deprecated Use {@code android.support.v7.app.AppCompatActivity}
* which extends {@link LifecycleOwner}, so there are no use cases for this class.
@@ -25,7 +23,6 @@ import android.support.annotation.NonNull;
@SuppressWarnings({"WeakerAccess", "unused"})
@Deprecated
public interface LifecycleRegistryOwner extends LifecycleOwner {
- @NonNull
@Override
LifecycleRegistry getLifecycle();
}
diff --git a/android/arch/lifecycle/LifecycleRegistryTest.java b/android/arch/lifecycle/LifecycleRegistryTest.java
index 2a7bbad2..6506454d 100644
--- a/android/arch/lifecycle/LifecycleRegistryTest.java
+++ b/android/arch/lifecycle/LifecycleRegistryTest.java
@@ -566,25 +566,6 @@ public class LifecycleRegistryTest {
verify(observer).onCreate();
}
- private static void forceGc() {
- Runtime.getRuntime().gc();
- Runtime.getRuntime().runFinalization();
- Runtime.getRuntime().gc();
- Runtime.getRuntime().runFinalization();
- }
-
- @Test
- public void goneLifecycleOwner() {
- fullyInitializeRegistry();
- mLifecycleOwner = null;
- forceGc();
- TestObserver observer = mock(TestObserver.class);
- mRegistry.addObserver(observer);
- verify(observer, never()).onCreate();
- verify(observer, never()).onStart();
- verify(observer, never()).onResume();
- }
-
private void dispatchEvent(Lifecycle.Event event) {
when(mLifecycle.getCurrentState()).thenReturn(LifecycleRegistry.getStateAfter(event));
mRegistry.handleLifecycleEvent(event);
diff --git a/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java b/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
index 8ba297fe..ac278c0c 100644
--- a/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
+++ b/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
@@ -29,9 +29,10 @@ import android.support.annotation.RestrictTo;
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ProcessLifecycleOwnerInitializer extends ContentProvider {
+public class LifecycleRuntimeTrojanProvider extends ContentProvider {
@Override
public boolean onCreate() {
+ LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}
diff --git a/android/arch/lifecycle/LiveData.java b/android/arch/lifecycle/LiveData.java
index 5b09c32f..3aea6acb 100644
--- a/android/arch/lifecycle/LiveData.java
+++ b/android/arch/lifecycle/LiveData.java
@@ -1,410 +1,4 @@
-/*
- * 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.
- */
-
+//LiveData interface for tests
package android.arch.lifecycle;
-
-import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
-import static android.arch.lifecycle.Lifecycle.State.STARTED;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.core.internal.SafeIterableMap;
-import android.arch.lifecycle.Lifecycle.State;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * LiveData is a data holder class that can be observed within a given lifecycle.
- * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
- * this observer will be notified about modifications of the wrapped data only if the paired
- * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
- * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
- * {@link #observeForever(Observer)} is considered as always active and thus will be always notified
- * about modifications. For those observers, you should manually call
- * {@link #removeObserver(Observer)}.
- *
- * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
- * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
- * activities and fragments where they can safely observe LiveData and not worry about leaks:
- * they will be instantly unsubscribed when they are destroyed.
- *
- * <p>
- * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
- * to get notified when number of active {@link Observer}s change between 0 and 1.
- * This allows LiveData to release any heavy resources when it does not have any Observers that
- * are actively observing.
- * <p>
- * This class is designed to hold individual data fields of {@link ViewModel},
- * but can also be used for sharing data between different modules in your application
- * in a decoupled fashion.
- *
- * @param <T> The type of data held by this instance
- * @see ViewModel
- */
-@SuppressWarnings({"WeakerAccess", "unused"})
-// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
-// thread.
-public abstract class LiveData<T> {
- private final Object mDataLock = new Object();
- static final int START_VERSION = -1;
- private static final Object NOT_SET = new Object();
-
- private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
-
- private LifecycleRegistry mRegistry = init();
-
- private LifecycleRegistry init() {
- LifecycleRegistry registry = new LifecycleRegistry(this);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
- return registry;
- }
-
- @Override
- public Lifecycle getLifecycle() {
- return mRegistry;
- }
- };
-
- private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
- new SafeIterableMap<>();
-
- // how many observers are in active state
- private int mActiveCount = 0;
- private volatile Object mData = NOT_SET;
- // when setData is called, we set the pending data and actual data swap happens on the main
- // thread
- private volatile Object mPendingData = NOT_SET;
- private int mVersion = START_VERSION;
-
- private boolean mDispatchingValue;
- @SuppressWarnings("FieldCanBeLocal")
- private boolean mDispatchInvalidated;
- private final Runnable mPostValueRunnable = new Runnable() {
- @Override
- public void run() {
- Object newValue;
- synchronized (mDataLock) {
- newValue = mPendingData;
- mPendingData = NOT_SET;
- }
- //noinspection unchecked
- setValue((T) newValue);
- }
- };
-
- private void considerNotify(LifecycleBoundObserver observer) {
- if (!observer.active) {
- return;
- }
- // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
- //
- // we still first check observer.active to keep it as the entrance for events. So even if
- // the observer moved to an active state, if we've not received that event, we better not
- // notify for a more predictable notification order.
- if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
- observer.activeStateChanged(false);
- return;
- }
- if (observer.lastVersion >= mVersion) {
- return;
- }
- observer.lastVersion = mVersion;
- //noinspection unchecked
- observer.observer.onChanged((T) mData);
- }
-
- private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
- if (mDispatchingValue) {
- mDispatchInvalidated = true;
- return;
- }
- mDispatchingValue = true;
- do {
- mDispatchInvalidated = false;
- if (initiator != null) {
- considerNotify(initiator);
- initiator = null;
- } else {
- for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
- mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
- considerNotify(iterator.next().getValue());
- if (mDispatchInvalidated) {
- break;
- }
- }
- }
- } while (mDispatchInvalidated);
- mDispatchingValue = false;
- }
-
- /**
- * Adds the given observer to the observers list within the lifespan of the given
- * owner. The events are dispatched on the main thread. If LiveData already has data
- * set, it will be delivered to the observer.
- * <p>
- * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
- * or {@link Lifecycle.State#RESUMED} state (active).
- * <p>
- * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
- * automatically be removed.
- * <p>
- * When data changes while the {@code owner} is not active, it will not receive any updates.
- * If it becomes active again, it will receive the last available data automatically.
- * <p>
- * LiveData keeps a strong reference to the observer and the owner as long as the
- * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
- * the observer &amp; the owner.
- * <p>
- * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
- * ignores the call.
- * <p>
- * If the given owner, observer tuple is already in the list, the call is ignored.
- * If the observer is already in the list with another owner, LiveData throws an
- * {@link IllegalArgumentException}.
- *
- * @param owner The LifecycleOwner which controls the observer
- * @param observer The observer that will receive the events
- */
- @MainThread
- public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- // ignore
- return;
- }
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
- LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
- if (existing != null && existing.owner != wrapper.owner) {
- throw new IllegalArgumentException("Cannot add the same observer"
- + " with different lifecycles");
- }
- if (existing != null) {
- return;
- }
- owner.getLifecycle().addObserver(wrapper);
- }
-
- /**
- * Adds the given observer to the observers list. This call is similar to
- * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
- * is always active. This means that the given observer will receive all events and will never
- * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
- * observing this LiveData.
- * While LiveData has one of such observers, it will be considered
- * as active.
- * <p>
- * If the observer was already added with an owner to this LiveData, LiveData throws an
- * {@link IllegalArgumentException}.
- *
- * @param observer The observer that will receive the events
- */
- @MainThread
- public void observeForever(@NonNull Observer<T> observer) {
- observe(ALWAYS_ON, observer);
- }
-
- /**
- * Removes the given observer from the observers list.
- *
- * @param observer The Observer to receive events.
- */
- @MainThread
- public void removeObserver(@NonNull final Observer<T> observer) {
- assertMainThread("removeObserver");
- LifecycleBoundObserver removed = mObservers.remove(observer);
- if (removed == null) {
- return;
- }
- removed.owner.getLifecycle().removeObserver(removed);
- removed.activeStateChanged(false);
- }
-
- /**
- * Removes all observers that are tied to the given {@link LifecycleOwner}.
- *
- * @param owner The {@code LifecycleOwner} scope for the observers to be removed.
- */
- @MainThread
- public void removeObservers(@NonNull final LifecycleOwner owner) {
- assertMainThread("removeObservers");
- for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
- if (entry.getValue().owner == owner) {
- removeObserver(entry.getKey());
- }
- }
- }
-
- /**
- * Posts a task to a main thread to set the given value. So if you have a following code
- * executed in the main thread:
- * <pre class="prettyprint">
- * liveData.postValue("a");
- * liveData.setValue("b");
- * </pre>
- * The value "b" would be set at first and later the main thread would override it with
- * the value "a".
- * <p>
- * If you called this method multiple times before a main thread executed a posted task, only
- * the last value would be dispatched.
- *
- * @param value The new value
- */
- protected void postValue(T value) {
- boolean postTask;
- synchronized (mDataLock) {
- postTask = mPendingData == NOT_SET;
- mPendingData = value;
- }
- if (!postTask) {
- return;
- }
- ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- }
-
- /**
- * Sets the value. If there are active observers, the value will be dispatched to them.
- * <p>
- * This method must be called from the main thread. If you need set a value from a background
- * thread, you can use {@link #postValue(Object)}
- *
- * @param value The new value
- */
- @MainThread
- protected void setValue(T value) {
- assertMainThread("setValue");
- mVersion++;
- mData = value;
- dispatchingValue(null);
- }
-
- /**
- * Returns the current value.
- * Note that calling this method on a background thread does not guarantee that the latest
- * value set will be received.
- *
- * @return the current value
- */
- @Nullable
- public T getValue() {
- Object data = mData;
- if (data != NOT_SET) {
- //noinspection unchecked
- return (T) data;
- }
- return null;
- }
-
- int getVersion() {
- return mVersion;
- }
-
- /**
- * Called when the number of active observers change to 1 from 0.
- * <p>
- * This callback can be used to know that this LiveData is being used thus should be kept
- * up to date.
- */
- protected void onActive() {
-
- }
-
- /**
- * Called when the number of active observers change from 1 to 0.
- * <p>
- * This does not mean that there are no observers left, there may still be observers but their
- * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
- * (like an Activity in the back stack).
- * <p>
- * You can check if there are observers via {@link #hasObservers()}.
- */
- protected void onInactive() {
-
- }
-
- /**
- * Returns true if this LiveData has observers.
- *
- * @return true if this LiveData has observers
- */
- public boolean hasObservers() {
- return mObservers.size() > 0;
- }
-
- /**
- * Returns true if this LiveData has active observers.
- *
- * @return true if this LiveData has active observers
- */
- public boolean hasActiveObservers() {
- return mActiveCount > 0;
- }
-
- class LifecycleBoundObserver implements GenericLifecycleObserver {
- public final LifecycleOwner owner;
- public final Observer<T> observer;
- public boolean active;
- public int lastVersion = START_VERSION;
-
- LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
- this.owner = owner;
- this.observer = observer;
- }
-
- @Override
- public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- removeObserver(observer);
- return;
- }
- // immediately set active state, so we'd never dispatch anything to inactive
- // owner
- activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
- }
-
- void activeStateChanged(boolean newActive) {
- if (newActive == active) {
- return;
- }
- active = newActive;
- boolean wasInactive = LiveData.this.mActiveCount == 0;
- LiveData.this.mActiveCount += active ? 1 : -1;
- if (wasInactive && active) {
- onActive();
- }
- if (LiveData.this.mActiveCount == 0 && !active) {
- onInactive();
- }
- if (active) {
- dispatchingValue(this);
- }
- }
- }
-
- static boolean isActiveState(State state) {
- return state.isAtLeast(STARTED);
- }
-
- private void assertMainThread(String methodName) {
- if (!ArchTaskExecutor.getInstance().isMainThread()) {
- throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
- + " thread");
- }
- }
+public class LiveData<T> {
}
diff --git a/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java b/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
deleted file mode 100644
index 836cfff0..00000000
--- a/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.app.Instrumentation;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
-import android.arch.lifecycle.testapp.CollectingSupportFragment;
-import android.arch.lifecycle.testapp.NavigationDialogActivity;
-import android.content.Intent;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.app.FragmentActivity;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LiveDataOnSaveInstanceStateTest {
- @Rule
- public ActivityTestRule<CollectingSupportActivity> mActivityTestRule =
- new ActivityTestRule<>(CollectingSupportActivity.class);
-
- @Test
- @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
- public void liveData_partiallyObscuredActivity_maxSdkM() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
-
- liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity);
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
- public void liveData_partiallyObscuredActivityWithFragment_maxSdkM() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
- CollectingSupportFragment fragment = new CollectingSupportFragment();
- mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
-
- liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment);
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
- public void liveData_partiallyObscuredActivityFragmentInFragment_maxSdkM() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
- CollectingSupportFragment fragment = new CollectingSupportFragment();
- CollectingSupportFragment fragment2 = new CollectingSupportFragment();
- mActivityTestRule.runOnUiThread(() -> {
- activity.replaceFragment(fragment);
- fragment.replaceFragment(fragment2);
- });
-
- liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment2);
- }
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
- public void liveData_partiallyObscuredActivity_minSdkN() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
-
- liveData_partiallyObscuredLifecycleOwner_minSdkN(activity);
- }
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
- public void liveData_partiallyObscuredActivityWithFragment_minSdkN() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
- CollectingSupportFragment fragment = new CollectingSupportFragment();
- mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
-
- liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment);
- }
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
- public void liveData_partiallyObscuredActivityFragmentInFragment_minSdkN() throws Throwable {
- CollectingSupportActivity activity = mActivityTestRule.getActivity();
- CollectingSupportFragment fragment = new CollectingSupportFragment();
- CollectingSupportFragment fragment2 = new CollectingSupportFragment();
- mActivityTestRule.runOnUiThread(() -> {
- activity.replaceFragment(fragment);
- fragment.replaceFragment(fragment2);
- });
-
- liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment2);
- }
-
- private void liveData_partiallyObscuredLifecycleOwner_maxSdkM(LifecycleOwner lifecycleOwner)
- throws Throwable {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
- mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(0));
-
- TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-
- mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
-
- final FragmentActivity dialogActivity = launchDialog();
-
- TestUtils.waitTillCreated(lifecycleOwner, mActivityTestRule);
-
- // Change the LiveData value and assert that the observer is not called given that the
- // lifecycle is in the CREATED state.
- mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(1));
- assertThat(atomicInteger.get(), is(0));
-
- // Finish the dialog Activity, wait for the main activity to be resumed, and assert that
- // the observer's onChanged method is called.
- mActivityTestRule.runOnUiThread(dialogActivity::finish);
- TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
- assertThat(atomicInteger.get(), is(1));
- }
-
- private void liveData_partiallyObscuredLifecycleOwner_minSdkN(LifecycleOwner lifecycleOwner)
- throws Throwable {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
- mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(0));
-
- TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-
- mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
-
- // Launch the NavigationDialogActivity, partially obscuring the activity, and wait for the
- // lifecycleOwner to hit onPause (or enter the STARTED state). On API 24 and above, this
- // onPause should be the last lifecycle method called (and the STARTED state should be the
- // final resting state).
- launchDialog();
- TestUtils.waitTillStarted(lifecycleOwner, mActivityTestRule);
-
- // Change the LiveData's value and verify that the observer's onChanged method is called
- // since we are in the STARTED state.
- mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(1));
- assertThat(atomicInteger.get(), is(1));
- }
-
- private FragmentActivity launchDialog() throws Throwable {
- Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
- NavigationDialogActivity.class.getCanonicalName(), null, false);
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.addMonitor(monitor);
-
- FragmentActivity activity = mActivityTestRule.getActivity();
- // helps with less flaky API 16 tests
- Intent intent = new Intent(activity, NavigationDialogActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- activity.startActivity(intent);
- FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
- TestUtils.waitTillResumed(fragmentActivity, mActivityTestRule);
- return fragmentActivity;
- }
-}
diff --git a/android/arch/lifecycle/LiveDataTest.java b/android/arch/lifecycle/LiveDataTest.java
index 647d5d7a..9f0b4257 100644
--- a/android/arch/lifecycle/LiveDataTest.java
+++ b/android/arch/lifecycle/LiveDataTest.java
@@ -53,29 +53,18 @@ import org.mockito.Mockito;
public class LiveDataTest {
private PublicLiveData<String> mLiveData;
private LifecycleOwner mOwner;
- private LifecycleOwner mOwner2;
private LifecycleRegistry mRegistry;
- private LifecycleRegistry mRegistry2;
private MethodExec mActiveObserversChanged;
private boolean mInObserver;
@Before
public void init() {
mLiveData = new PublicLiveData<>();
-
- mActiveObserversChanged = mock(MethodExec.class);
- mLiveData.activeObserversChanged = mActiveObserversChanged;
-
mOwner = mock(LifecycleOwner.class);
-
mRegistry = new LifecycleRegistry(mOwner);
when(mOwner.getLifecycle()).thenReturn(mRegistry);
-
- mOwner2 = mock(LifecycleOwner.class);
-
- mRegistry2 = new LifecycleRegistry(mOwner2);
- when(mOwner2.getLifecycle()).thenReturn(mRegistry2);
-
+ mActiveObserversChanged = mock(MethodExec.class);
+ mLiveData.activeObserversChanged = mActiveObserversChanged;
mInObserver = false;
}
@@ -170,11 +159,14 @@ public class LiveDataTest {
@Test
public void testAddSameObserverIn2LifecycleOwners() {
Observer<String> observer = (Observer<String>) mock(Observer.class);
+ LifecycleOwner owner2 = mock(LifecycleOwner.class);
+ LifecycleRegistry registry2 = new LifecycleRegistry(owner2);
+ when(owner2.getLifecycle()).thenReturn(registry2);
mLiveData.observe(mOwner, observer);
Throwable throwable = null;
try {
- mLiveData.observe(mOwner2, observer);
+ mLiveData.observe(owner2, observer);
} catch (Throwable t) {
throwable = t;
}
@@ -464,210 +456,6 @@ public class LiveDataTest {
inOrder.verifyNoMoreInteractions();
}
- @Test
- public void setValue_neverActive_observerOnChangedNotCalled() {
- Observer<String> observer = (Observer<String>) mock(Observer.class);
- mLiveData.observe(mOwner, observer);
-
- mLiveData.setValue("1");
-
- verify(observer, never()).onChanged(anyString());
- }
-
- @Test
- public void setValue_twoObserversTwoStartedOwners_onChangedCalledOnBoth() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- mLiveData.setValue("1");
-
- verify(observer1).onChanged("1");
- verify(observer2).onChanged("1");
- }
-
- @Test
- public void setValue_twoObserversOneStartedOwner_onChangedCalledOnOneCorrectObserver() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- mLiveData.setValue("1");
-
- verify(observer1).onChanged("1");
- verify(observer2, never()).onChanged(anyString());
- }
-
- @Test
- public void setValue_twoObserversBothStartedAfterSetValue_onChangedCalledOnBoth() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mLiveData.setValue("1");
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- verify(observer1).onChanged("1");
- verify(observer1).onChanged("1");
- }
-
- @Test
- public void setValue_twoObserversOneStartedAfterSetValue_onChangedCalledOnCorrectObserver() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mLiveData.setValue("1");
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- verify(observer1).onChanged("1");
- verify(observer2, never()).onChanged(anyString());
- }
-
- @Test
- public void setValue_twoObserversOneStarted_liveDataBecomesActive() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- verify(mActiveObserversChanged).onCall(true);
- }
-
- @Test
- public void setValue_twoObserversOneStopped_liveDataStaysActive() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- verify(mActiveObserversChanged).onCall(true);
-
- reset(mActiveObserversChanged);
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- verify(mActiveObserversChanged, never()).onCall(anyBoolean());
- }
-
- /**
- * Verifies that if a lifecycle's state changes without an event, and changes to something that
- * LiveData would become inactive in response to, LiveData will detect the change upon new data
- * being set and become inactive. Also verifies that once the lifecycle enters into a state
- * that LiveData should become active to, that it does indeed become active.
- */
- @Test
- public void liveDataActiveStateIsManagedCorrectlyWithoutEvent_oneObserver() {
- Observer<String> observer = (Observer<String>) mock(Observer.class);
- mLiveData.observe(mOwner, observer);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- // Marking state as CREATED should call onInactive.
- reset(mActiveObserversChanged);
- mRegistry.markState(Lifecycle.State.CREATED);
- verify(mActiveObserversChanged).onCall(false);
- reset(mActiveObserversChanged);
-
- // Setting a new value should trigger LiveData to realize the Lifecycle it is observing
- // is in a state where the LiveData should be inactive, so the LiveData will call onInactive
- // and the Observer shouldn't be affected.
- mLiveData.setValue("1");
-
- // state is already CREATED so should not call again
- verify(mActiveObserversChanged, never()).onCall(anyBoolean());
- verify(observer, never()).onChanged(anyString());
-
- // Sanity check. Because we've only marked the state as CREATED, sending ON_START
- // should re-dispatch events.
- reset(mActiveObserversChanged);
- reset(observer);
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- verify(mActiveObserversChanged).onCall(true);
- verify(observer).onChanged("1");
- }
-
- /**
- * This test verifies that LiveData will detect changes in LifecycleState that would make it
- * inactive upon the setting of new data, but only if all of the Lifecycles it's observing
- * are all in those states. It also makes sure that once it is inactive, that it will become
- * active again once one of the lifecycles it's observing moves to an appropriate state.
- */
- @Test
- public void liveDataActiveStateIsManagedCorrectlyWithoutEvent_twoObservers() {
- Observer<String> observer1 = mock(Observer.class);
- Observer<String> observer2 = mock(Observer.class);
-
- mLiveData.observe(mOwner, observer1);
- mLiveData.observe(mOwner2, observer2);
-
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
- mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
- // Marking the state to created won't change LiveData to be inactive.
- reset(mActiveObserversChanged);
- mRegistry.markState(Lifecycle.State.CREATED);
- verify(mActiveObserversChanged, never()).onCall(anyBoolean());
-
- // After setting a value, the LiveData will stay active because there is still a STARTED
- // lifecycle being observed. The one Observer associated with the STARTED lifecycle will
- // also have been called, but the other Observer will not have been called.
- reset(observer1);
- reset(observer2);
- mLiveData.setValue("1");
- verify(mActiveObserversChanged, never()).onCall(anyBoolean());
- verify(observer1, never()).onChanged(anyString());
- verify(observer2).onChanged("1");
-
- // Now we set the other Lifecycle to be inactive, live data should become inactive.
- reset(observer1);
- reset(observer2);
- mRegistry2.markState(Lifecycle.State.CREATED);
- verify(mActiveObserversChanged).onCall(false);
- verify(observer1, never()).onChanged(anyString());
- verify(observer2, never()).onChanged(anyString());
-
- // Now we post another value, because both lifecycles are in the Created state, live data
- // will not dispatch any values
- reset(mActiveObserversChanged);
- mLiveData.setValue("2");
- verify(mActiveObserversChanged, never()).onCall(anyBoolean());
- verify(observer1, never()).onChanged(anyString());
- verify(observer2, never()).onChanged(anyString());
-
- // Now that the first Lifecycle has been moved back to the Resumed state, the LiveData will
- // be made active and it's associated Observer will be called with the new value, but the
- // Observer associated with the Lifecycle that is still in the Created state won't be
- // called.
- reset(mActiveObserversChanged);
- mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
- verify(mActiveObserversChanged).onCall(true);
- verify(observer1).onChanged("2");
- verify(observer2, never()).onChanged(anyString());
- }
-
@SuppressWarnings("WeakerAccess")
static class PublicLiveData<T> extends LiveData<T> {
// cannot spy due to internal calls
diff --git a/android/arch/lifecycle/MediatorLiveData.java b/android/arch/lifecycle/MediatorLiveData.java
index 58647394..672b3a3b 100644
--- a/android/arch/lifecycle/MediatorLiveData.java
+++ b/android/arch/lifecycle/MediatorLiveData.java
@@ -19,49 +19,16 @@ package android.arch.lifecycle;
import android.arch.core.internal.SafeIterableMap;
import android.support.annotation.CallSuper;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.Map;
/**
- * {@link LiveData} subclass which may observe other {@code LiveData} objects and react on
+ * {@link LiveData} subclass which may observer other {@code LiveData} objects and react on
* {@code OnChanged} events from them.
* <p>
* This class correctly propagates its active/inactive states down to source {@code LiveData}
* objects.
- * <p>
- * Consider the following scenario: we have 2 instances of {@code LiveData}, let's name them
- * {@code liveData1} and {@code liveData2}, and we want to merge their emissions in one object:
- * {@code liveDataMerger}. Then, {@code liveData1} and {@code liveData2} will become sources for
- * the {@code MediatorLiveData liveDataMerger} and every time {@code onChanged} callback
- * is called for either of them, we set a new value in {@code liveDataMerger}.
- *
- * <pre>
- * LiveData<Integer> liveData1 = ...;
- * LiveData<Integer> liveData2 = ...;
- *
- * MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
- * liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
- * liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
- * </pre>
- * <p>
- * Let's consider that we only want 10 values emitted by {@code liveData1}, to be
- * merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code
- * liveData1} and remove it as a source.
- * <pre>
- * liveDataMerger.addSource(liveData1, new Observer<Integer>() {
- * private int count = 1;
- *
- * {@literal @}Override public void onChanged(@Nullable Integer s) {
- * count++;
- * liveDataMerger.setValue(s);
- * if (count > 10) {
- * liveDataMerger.removeSource(liveData1);
- * }
- * }
- * });
- * </pre>
*
* @param <T> The type of data hold by this instance
*/
@@ -82,7 +49,7 @@ public class MediatorLiveData<T> extends MutableLiveData<T> {
* @param <S> The type of data hold by {@code source} LiveData
*/
@MainThread
- public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<S> onChanged) {
+ public <S> void addSource(LiveData<S> source, Observer<S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
@@ -104,7 +71,7 @@ public class MediatorLiveData<T> extends MutableLiveData<T> {
* @param <S> the type of data hold by {@code source} LiveData
*/
@MainThread
- public <S> void removeSource(@NonNull LiveData<S> toRemote) {
+ public <S> void removeSource(LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
diff --git a/android/arch/lifecycle/MissingClassTest.java b/android/arch/lifecycle/MissingClassTest.java
deleted file mode 100644
index 81a07564..00000000
--- a/android/arch/lifecycle/MissingClassTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle;
-
-import android.app.PictureInPictureParams;
-import android.os.Build;
-import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.N_MR1)
-@SmallTest
-public class MissingClassTest {
- public static class ObserverWithMissingClasses {
- @SuppressWarnings("unused")
- public void newApiMethod(PictureInPictureParams params) {}
-
- @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
- public void onResume() {}
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMissingApi() {
- new ReflectiveGenericLifecycleObserver(new ObserverWithMissingClasses());
- }
-}
diff --git a/android/arch/lifecycle/PartiallyCoveredActivityTest.java b/android/arch/lifecycle/PartiallyCoveredActivityTest.java
deleted file mode 100644
index 07a9dc5a..00000000
--- a/android/arch/lifecycle/PartiallyCoveredActivityTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle;
-
-import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
-import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.CREATE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
-import static android.arch.lifecycle.TestUtils.flatMap;
-import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-
-import android.app.Instrumentation;
-import android.arch.lifecycle.testapp.CollectingLifecycleOwner;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
-import android.arch.lifecycle.testapp.CollectingSupportFragment;
-import android.arch.lifecycle.testapp.NavigationDialogActivity;
-import android.arch.lifecycle.testapp.TestEvent;
-import android.content.Intent;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.util.Pair;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-
-/**
- * Runs tests about the state when an activity is partially covered by another activity. Pre
- * API 24, framework behavior changes so the test rely on whether state is saved or not and makes
- * assertions accordingly.
- */
-@SuppressWarnings("unchecked")
-@RunWith(Parameterized.class)
-@LargeTest
-public class PartiallyCoveredActivityTest {
- private static final List[] IF_SAVED = new List[]{
- // when overlaid
- flatMap(CREATE, START, RESUME, PAUSE,
- singletonList(new Pair<>(LIFECYCLE_EVENT, ON_STOP))),
- // post dialog dismiss
- asList(new Pair<>(OWNER_CALLBACK, ON_RESUME),
- new Pair<>(LIFECYCLE_EVENT, ON_START),
- new Pair<>(LIFECYCLE_EVENT, ON_RESUME)),
- // post finish
- flatMap(PAUSE, STOP, DESTROY)};
-
- private static final List[] IF_NOT_SAVED = new List[]{
- // when overlaid
- flatMap(CREATE, START, RESUME, PAUSE),
- // post dialog dismiss
- flatMap(RESUME),
- // post finish
- flatMap(PAUSE, STOP, DESTROY)};
-
- private static final boolean sShouldSave = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
- private static final List<Pair<TestEvent, Lifecycle.Event>>[] EXPECTED =
- sShouldSave ? IF_SAVED : IF_NOT_SAVED;
-
- @Rule
- public ActivityTestRule<CollectingSupportActivity> activityRule =
- new ActivityTestRule<CollectingSupportActivity>(
- CollectingSupportActivity.class) {
- @Override
- protected Intent getActivityIntent() {
- // helps with less flaky API 16 tests
- Intent intent = new Intent(InstrumentationRegistry.getTargetContext(),
- CollectingSupportActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- return intent;
- }
- };
- private final boolean mDismissDialog;
-
- @Parameterized.Parameters(name = "dismissDialog_{0}")
- public static List<Boolean> dismissDialog() {
- return asList(true, false);
- }
-
- public PartiallyCoveredActivityTest(boolean dismissDialog) {
- mDismissDialog = dismissDialog;
- }
-
- @Test
- public void coveredWithDialog_activity() throws Throwable {
- final CollectingSupportActivity activity = activityRule.getActivity();
- runTest(activity);
- }
-
- @Test
- public void coveredWithDialog_fragment() throws Throwable {
- CollectingSupportFragment fragment = new CollectingSupportFragment();
- activityRule.runOnUiThread(() -> activityRule.getActivity().replaceFragment(fragment));
- runTest(fragment);
- }
-
- @Test
- public void coveredWithDialog_childFragment() throws Throwable {
- CollectingSupportFragment parentFragment = new CollectingSupportFragment();
- CollectingSupportFragment childFragment = new CollectingSupportFragment();
- activityRule.runOnUiThread(() -> {
- activityRule.getActivity().replaceFragment(parentFragment);
- parentFragment.replaceFragment(childFragment);
- });
- runTest(childFragment);
- }
-
- private void runTest(CollectingLifecycleOwner owner) throws Throwable {
- TestUtils.waitTillResumed(owner, activityRule);
- FragmentActivity dialog = launchDialog();
- assertStateSaving();
- waitForIdle();
- assertThat(owner.copyCollectedEvents(), is(EXPECTED[0]));
- List<Pair<TestEvent, Lifecycle.Event>> expected;
- if (mDismissDialog) {
- dialog.finish();
- TestUtils.waitTillResumed(activityRule.getActivity(), activityRule);
- assertThat(owner.copyCollectedEvents(), is(flatMap(EXPECTED[0], EXPECTED[1])));
- expected = flatMap(EXPECTED[0], EXPECTED[1], EXPECTED[2]);
- } else {
- expected = flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY);
- }
- CollectingSupportActivity activity = activityRule.getActivity();
- activityRule.finishActivity();
- TestUtils.waitTillDestroyed(activity, activityRule);
- assertThat(owner.copyCollectedEvents(), is(expected));
- }
-
- // test sanity
- private void assertStateSaving() throws ExecutionException, InterruptedException {
- final CollectingSupportActivity activity = activityRule.getActivity();
- if (sShouldSave) {
- // state should be saved. wait for it to be saved
- assertThat("test sanity",
- activity.waitForStateSave(20), is(true));
- assertThat("test sanity", activity.getSupportFragmentManager()
- .isStateSaved(), is(true));
- } else {
- // should should not be saved
- assertThat("test sanity", activity.getSupportFragmentManager()
- .isStateSaved(), is(false));
- }
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private FragmentActivity launchDialog() throws Throwable {
- Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
- NavigationDialogActivity.class.getCanonicalName(), null, false);
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.addMonitor(monitor);
-
- FragmentActivity activity = activityRule.getActivity();
-
- Intent intent = new Intent(activity, NavigationDialogActivity.class);
- // disabling animations helps with less flaky API 16 tests
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- activity.startActivity(intent);
- FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
- TestUtils.waitTillResumed(fragmentActivity, activityRule);
- return fragmentActivity;
- }
-}
diff --git a/android/arch/lifecycle/ProcessLifecycleOwner.java b/android/arch/lifecycle/ProcessLifecycleOwner.java
index 179e2c47..e2a12563 100644
--- a/android/arch/lifecycle/ProcessLifecycleOwner.java
+++ b/android/arch/lifecycle/ProcessLifecycleOwner.java
@@ -22,7 +22,6 @@ import android.arch.lifecycle.ReportFragment.ActivityInitializationListener;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
/**
@@ -157,8 +156,7 @@ public class ProcessLifecycleOwner implements LifecycleOwner {
app.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- ReportFragment.injectIfNeededIn(activity);
- ReportFragment.get(activity).setProcessListener(mInitializationListener);
+ ReportFragment .get(activity).setProcessListener(mInitializationListener);
}
@Override
@@ -173,7 +171,6 @@ public class ProcessLifecycleOwner implements LifecycleOwner {
});
}
- @NonNull
@Override
public Lifecycle getLifecycle() {
return mRegistry;
diff --git a/android/arch/lifecycle/ProcessOwnerTest.java b/android/arch/lifecycle/ProcessOwnerTest.java
index 77baf94c..37bdcdb4 100644
--- a/android/arch/lifecycle/ProcessOwnerTest.java
+++ b/android/arch/lifecycle/ProcessOwnerTest.java
@@ -31,7 +31,6 @@ import android.arch.lifecycle.Lifecycle.Event;
import android.arch.lifecycle.testapp.NavigationDialogActivity;
import android.arch.lifecycle.testapp.NavigationTestActivityFirst;
import android.arch.lifecycle.testapp.NavigationTestActivitySecond;
-import android.arch.lifecycle.testapp.NonSupportActivity;
import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
@@ -96,22 +95,6 @@ public class ProcessOwnerTest {
}
@Test
- public void testNavigationToNonSupport() throws Throwable {
- FragmentActivity firstActivity = setupObserverOnResume();
- Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
- NonSupportActivity.class.getCanonicalName(), null, false);
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.addMonitor(monitor);
-
- Intent intent = new Intent(firstActivity, NonSupportActivity.class);
- firstActivity.finish();
- firstActivity.startActivity(intent);
- NonSupportActivity secondActivity = (NonSupportActivity) monitor.waitForActivity();
- assertThat("Failed to navigate", secondActivity, notNullValue());
- checkProcessObserverSilent(secondActivity);
- }
-
- @Test
public void testRecreation() throws Throwable {
FragmentActivity activity = setupObserverOnResume();
FragmentActivity recreated = TestUtils.recreateActivity(activity, activityTestRule);
@@ -181,11 +164,4 @@ public class ProcessOwnerTest {
activityTestRule.runOnUiThread(() ->
ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
}
-
- private void checkProcessObserverSilent(NonSupportActivity activity) throws Throwable {
- assertThat(activity.awaitResumedState(), is(true));
- assertThat(mObserver.mChangedState, is(false));
- activityTestRule.runOnUiThread(() ->
- ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
- }
}
diff --git a/android/arch/lifecycle/ReportFragment.java b/android/arch/lifecycle/ReportFragment.java
index 16a89ce8..3e4ece82 100644
--- a/android/arch/lifecycle/ReportFragment.java
+++ b/android/arch/lifecycle/ReportFragment.java
@@ -28,6 +28,7 @@ import android.support.annotation.RestrictTo;
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class ReportFragment extends Fragment {
+
private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
diff --git a/android/arch/lifecycle/TestUtils.java b/android/arch/lifecycle/TestUtils.java
index f7f9bbe5..f0214bfb 100644
--- a/android/arch/lifecycle/TestUtils.java
+++ b/android/arch/lifecycle/TestUtils.java
@@ -16,35 +16,16 @@
package android.arch.lifecycle;
-import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
-import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
-import static android.arch.lifecycle.Lifecycle.State.CREATED;
-import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
import static android.arch.lifecycle.Lifecycle.State.RESUMED;
-import static android.arch.lifecycle.Lifecycle.State.STARTED;
-import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
-import android.arch.lifecycle.testapp.TestEvent;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
-import android.support.v4.util.Pair;
+import android.support.v4.app.FragmentActivity;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
class TestUtils {
@@ -80,88 +61,23 @@ class TestUtils {
return result;
}
- static void waitTillCreated(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
- throws Throwable {
- waitTillState(owner, activityRule, CREATED);
- }
-
- static void waitTillStarted(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
- throws Throwable {
- waitTillState(owner, activityRule, STARTED);
- }
-
- static void waitTillResumed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
- throws Throwable {
- waitTillState(owner, activityRule, RESUMED);
- }
-
- static void waitTillDestroyed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
- throws Throwable {
- waitTillState(owner, activityRule, DESTROYED);
- }
-
- static void waitTillState(final LifecycleOwner owner, ActivityTestRule<?> activityRule,
- Lifecycle.State state)
+ static void waitTillResumed(final FragmentActivity a, ActivityTestRule<?> activityRule)
throws Throwable {
final CountDownLatch latch = new CountDownLatch(1);
activityRule.runOnUiThread(() -> {
- Lifecycle.State currentState = owner.getLifecycle().getCurrentState();
- if (currentState == state) {
+ Lifecycle.State currentState = a.getLifecycle().getCurrentState();
+ if (currentState == RESUMED) {
latch.countDown();
- } else {
- owner.getLifecycle().addObserver(new LifecycleObserver() {
- @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
- public void onStateChanged(LifecycleOwner provider) {
- if (provider.getLifecycle().getCurrentState() == state) {
- latch.countDown();
- provider.getLifecycle().removeObserver(this);
- }
- }
- });
}
+ a.getLifecycle().addObserver(new LifecycleObserver() {
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ public void onStateChanged(LifecycleOwner provider) {
+ latch.countDown();
+ provider.getLifecycle().removeObserver(this);
+ }
+ });
});
- boolean latchResult = latch.await(1, TimeUnit.MINUTES);
- assertThat("expected " + state + " never happened. Current state:"
- + owner.getLifecycle().getCurrentState(), latchResult, is(true));
-
- // wait for another loop to ensure all observers are called
- activityRule.runOnUiThread(() -> {
- // do nothing
- });
+ latch.await();
}
- @SafeVarargs
- static <T> List<T> flatMap(List<T>... items) {
- ArrayList<T> result = new ArrayList<>();
- for (List<T> item : items) {
- result.addAll(item);
- }
- return result;
- }
-
- /**
- * Event tuples of {@link TestEvent} and {@link Lifecycle.Event}
- * in the order they should arrive.
- */
- @SuppressWarnings("unchecked")
- static class OrderedTuples {
- static final List<Pair<TestEvent, Lifecycle.Event>> CREATE =
- Arrays.asList(new Pair(OWNER_CALLBACK, ON_CREATE),
- new Pair(LIFECYCLE_EVENT, ON_CREATE));
- static final List<Pair<TestEvent, Lifecycle.Event>> START =
- Arrays.asList(new Pair(OWNER_CALLBACK, ON_START),
- new Pair(LIFECYCLE_EVENT, ON_START));
- static final List<Pair<TestEvent, Lifecycle.Event>> RESUME =
- Arrays.asList(new Pair(OWNER_CALLBACK, ON_RESUME),
- new Pair(LIFECYCLE_EVENT, ON_RESUME));
- static final List<Pair<TestEvent, Lifecycle.Event>> PAUSE =
- Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_PAUSE),
- new Pair(OWNER_CALLBACK, ON_PAUSE));
- static final List<Pair<TestEvent, Lifecycle.Event>> STOP =
- Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_STOP),
- new Pair(OWNER_CALLBACK, ON_STOP));
- static final List<Pair<TestEvent, Lifecycle.Event>> DESTROY =
- Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_DESTROY),
- new Pair(OWNER_CALLBACK, ON_DESTROY));
- }
}
diff --git a/android/arch/lifecycle/Transformations.java b/android/arch/lifecycle/Transformations.java
index c735f8ba..9ce9cbb7 100644
--- a/android/arch/lifecycle/Transformations.java
+++ b/android/arch/lifecycle/Transformations.java
@@ -18,7 +18,6 @@ package android.arch.lifecycle;
import android.arch.core.util.Function;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
@@ -61,8 +60,7 @@ public class Transformations {
* @return a LiveData which emits resulting values
*/
@MainThread
- public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
- @NonNull final Function<X, Y> func) {
+ public static <X, Y> LiveData<Y> map(LiveData<X> source, final Function<X, Y> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
@@ -122,8 +120,8 @@ public class Transformations {
* @param <Y> a type of resulting LiveData
*/
@MainThread
- public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
- @NonNull final Function<X, LiveData<Y>> func) {
+ public static <X, Y> LiveData<Y> switchMap(LiveData<X> trigger,
+ final Function<X, LiveData<Y>> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(trigger, new Observer<X>() {
LiveData<Y> mSource;
diff --git a/android/arch/lifecycle/ViewModelProvider.java b/android/arch/lifecycle/ViewModelProvider.java
index 29cbab8e..7ef591f3 100644
--- a/android/arch/lifecycle/ViewModelProvider.java
+++ b/android/arch/lifecycle/ViewModelProvider.java
@@ -43,8 +43,7 @@ public class ViewModelProvider {
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
- @NonNull
- <T extends ViewModel> T create(@NonNull Class<T> modelClass);
+ <T extends ViewModel> T create(Class<T> modelClass);
}
private final Factory mFactory;
@@ -71,7 +70,7 @@ public class ViewModelProvider {
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
- public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
+ public ViewModelProvider(ViewModelStore store, Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
@@ -89,8 +88,7 @@ public class ViewModelProvider {
* @param <T> The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
- @NonNull
- public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
+ public <T extends ViewModel> T get(Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
@@ -138,9 +136,8 @@ public class ViewModelProvider {
*/
public static class NewInstanceFactory implements Factory {
- @NonNull
@Override
- public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ public <T extends ViewModel> T create(Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
diff --git a/android/arch/lifecycle/ViewModelProviders.java b/android/arch/lifecycle/ViewModelProviders.java
index b4b20aa4..746162a9 100644
--- a/android/arch/lifecycle/ViewModelProviders.java
+++ b/android/arch/lifecycle/ViewModelProviders.java
@@ -139,9 +139,8 @@ public class ViewModelProviders {
mApplication = application;
}
- @NonNull
@Override
- public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ public <T extends ViewModel> T create(Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
diff --git a/android/arch/lifecycle/ViewModelStoreOwner.java b/android/arch/lifecycle/ViewModelStoreOwner.java
index e26fa325..50583056 100644
--- a/android/arch/lifecycle/ViewModelStoreOwner.java
+++ b/android/arch/lifecycle/ViewModelStoreOwner.java
@@ -16,8 +16,6 @@
package android.arch.lifecycle;
-import android.support.annotation.NonNull;
-
/**
* A scope that owns {@link ViewModelStore}.
* <p>
@@ -32,6 +30,5 @@ public interface ViewModelStoreOwner {
*
* @return a {@code ViewModelStore}
*/
- @NonNull
ViewModelStore getViewModelStore();
}
diff --git a/android/arch/lifecycle/ViewModelStores.java b/android/arch/lifecycle/ViewModelStores.java
index d7d769d6..8c17dd98 100644
--- a/android/arch/lifecycle/ViewModelStores.java
+++ b/android/arch/lifecycle/ViewModelStores.java
@@ -19,7 +19,6 @@ package android.arch.lifecycle;
import static android.arch.lifecycle.HolderFragment.holderFragmentFor;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
@@ -39,7 +38,7 @@ public class ViewModelStores {
* @return a {@code ViewModelStore}
*/
@MainThread
- public static ViewModelStore of(@NonNull FragmentActivity activity) {
+ public static ViewModelStore of(FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
@@ -50,7 +49,7 @@ public class ViewModelStores {
* @return a {@code ViewModelStore}
*/
@MainThread
- public static ViewModelStore of(@NonNull Fragment fragment) {
+ public static ViewModelStore of(Fragment fragment) {
return holderFragmentFor(fragment).getViewModelStore();
}
}
diff --git a/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java b/android/arch/lifecycle/testapp/CollectingActivity.java
index 4213cab9..6e243b6c 100644
--- a/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
+++ b/android/arch/lifecycle/testapp/CollectingActivity.java
@@ -17,20 +17,21 @@
package android.arch.lifecycle.testapp;
import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.support.v4.util.Pair;
+import android.util.Pair;
import java.util.List;
/**
* For activities that collect their events.
*/
-public interface CollectingLifecycleOwner extends LifecycleOwner {
+public interface CollectingActivity {
+ long TIMEOUT = 5;
+
/**
- * Return a copy of currently collected events
+ * Return collected events
*
* @return The list of collected events.
* @throws InterruptedException
*/
- List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents();
+ List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents() throws InterruptedException;
}
diff --git a/android/arch/lifecycle/testapp/CollectingSupportActivity.java b/android/arch/lifecycle/testapp/CollectingSupportActivity.java
deleted file mode 100644
index f38d4224..00000000
--- a/android/arch/lifecycle/testapp/CollectingSupportActivity.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import android.arch.lifecycle.Lifecycle.Event;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.util.Pair;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * LifecycleRegistryOwner that extends FragmentActivity.
- */
-public class CollectingSupportActivity extends FragmentActivity implements
- CollectingLifecycleOwner {
-
- private final List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
- private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
- private CountDownLatch mSavedStateLatch = new CountDownLatch(1);
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FrameLayout layout = new FrameLayout(this);
- layout.setId(R.id.fragment_container);
- setContentView(layout);
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_CREATE));
- getLifecycle().addObserver(mTestObserver);
- }
-
- /**
- * replaces the main content fragment w/ the given fragment.
- */
- public void replaceFragment(Fragment fragment) {
- getSupportFragmentManager()
- .beginTransaction()
- .add(R.id.fragment_container, fragment)
- .commitNow();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_START));
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_RESUME));
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_DESTROY));
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_STOP));
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_PAUSE));
- // helps with less flaky API 16 tests.
- overridePendingTransition(0, 0);
- }
-
- @Override
- public List<Pair<TestEvent, Event>> copyCollectedEvents() {
- return new ArrayList<>(mCollectedEvents);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mSavedStateLatch.countDown();
- }
-
- /**
- * Waits for onSaveInstanceState to be called.
- */
- public boolean waitForStateSave(@SuppressWarnings("SameParameterValue") int seconds)
- throws InterruptedException {
- return mSavedStateLatch.await(seconds, TimeUnit.SECONDS);
- }
-}
diff --git a/android/arch/lifecycle/testapp/CollectingSupportFragment.java b/android/arch/lifecycle/testapp/CollectingSupportFragment.java
deleted file mode 100644
index 9bbbe165..00000000
--- a/android/arch/lifecycle/testapp/CollectingSupportFragment.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import android.annotation.SuppressLint;
-import android.arch.lifecycle.Lifecycle;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.util.Pair;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A support fragment that collects all of its events.
- */
-@SuppressLint("ValidFragment")
-public class CollectingSupportFragment extends Fragment implements CollectingLifecycleOwner {
- private final List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents =
- new ArrayList<>();
- private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
- getLifecycle().addObserver(mTestObserver);
- }
-
- @Nullable
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- //noinspection ConstantConditions
- FrameLayout layout = new FrameLayout(container.getContext());
- layout.setId(R.id.child_fragment_container);
- return layout;
- }
-
- /**
- * Runs a replace fragment transaction with 'fragment' on this Fragment.
- */
- public void replaceFragment(Fragment fragment) {
- getChildFragmentManager()
- .beginTransaction()
- .add(R.id.child_fragment_container, fragment)
- .commitNow();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
- }
-
- @Override
- public void onStop() {
- super.onStop();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
- }
-
- @Override
- public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
- return new ArrayList<>(mCollectedEvents);
- }
-}
diff --git a/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java b/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
index cdf577c1..d8f4fb39 100644
--- a/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
+++ b/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
@@ -16,29 +16,27 @@
package android.arch.lifecycle.testapp;
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleRegistry;
import android.arch.lifecycle.LifecycleRegistryOwner;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.util.Pair;
+import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* LifecycleRegistryOwner that extends framework activity.
*/
-@SuppressWarnings("deprecation")
public class FrameworkLifecycleRegistryActivity extends Activity implements
- LifecycleRegistryOwner, CollectingLifecycleOwner {
+ LifecycleRegistryOwner, CollectingActivity {
private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
- @NonNull
@Override
public LifecycleRegistry getLifecycle() {
return mLifecycleRegistry;
@@ -51,43 +49,49 @@ public class FrameworkLifecycleRegistryActivity extends Activity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
getLifecycle().addObserver(mTestObserver);
}
@Override
protected void onStart() {
super.onStart();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
}
@Override
protected void onResume() {
super.onResume();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
+ finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
mLatch.countDown();
}
@Override
protected void onStop() {
super.onStop();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
}
@Override
protected void onPause() {
super.onPause();
- mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
}
+ /**
+ * awaits for all events and returns them.
+ */
@Override
- public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
- return new ArrayList<>(mCollectedEvents);
+ public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
+ throws InterruptedException {
+ mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+ return mCollectedEvents;
}
}
diff --git a/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java b/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
new file mode 100644
index 00000000..5f33c282
--- /dev/null
+++ b/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+
+import android.arch.lifecycle.Lifecycle;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity for testing full lifecycle
+ */
+public class FullLifecycleTestActivity extends FragmentActivity implements CollectingActivity {
+
+ private List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents = new ArrayList<>();
+ private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+ private CountDownLatch mLatch = new CountDownLatch(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
+ getLifecycle().addObserver(mTestObserver);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
+ mLatch.countDown();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
+ }
+
+ /**
+ * awaits for all events and returns them.
+ */
+ @Override
+ public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
+ throws InterruptedException {
+ mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+ return mCollectedEvents;
+ }
+}
diff --git a/android/arch/lifecycle/testapp/MainActivity.java b/android/arch/lifecycle/testapp/MainActivity.java
new file mode 100644
index 00000000..b9d59142
--- /dev/null
+++ b/android/arch/lifecycle/testapp/MainActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Simple test activity
+ */
+public class MainActivity extends FragmentActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity);
+ }
+}
diff --git a/android/arch/lifecycle/testapp/NavigationDialogActivity.java b/android/arch/lifecycle/testapp/NavigationDialogActivity.java
index 7d53528f..0ae94033 100644
--- a/android/arch/lifecycle/testapp/NavigationDialogActivity.java
+++ b/android/arch/lifecycle/testapp/NavigationDialogActivity.java
@@ -22,10 +22,4 @@ import android.support.v4.app.FragmentActivity;
* an activity with Dialog theme.
*/
public class NavigationDialogActivity extends FragmentActivity {
- @Override
- protected void onPause() {
- super.onPause();
- // helps with less flaky API 16 tests
- overridePendingTransition(0, 0);
- }
}
diff --git a/android/arch/lifecycle/testapp/NonSupportActivity.java b/android/arch/lifecycle/testapp/NonSupportActivity.java
deleted file mode 100644
index 835d846a..00000000
--- a/android/arch/lifecycle/testapp/NonSupportActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Activity which doesn't extend FragmentActivity, to test ProcessLifecycleOwner because it
- * should work anyway.
- */
-public class NonSupportActivity extends Activity {
-
- private static final int TIMEOUT = 1; //secs
- private final Lock mLock = new ReentrantLock();
- private Condition mIsResumedCondition = mLock.newCondition();
- private boolean mIsResumed = false;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mLock.lock();
- try {
- mIsResumed = true;
- mIsResumedCondition.signalAll();
- } finally {
- mLock.unlock();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mLock.lock();
- try {
- mIsResumed = false;
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * awaits resumed state
- * @return
- * @throws InterruptedException
- */
- public boolean awaitResumedState() throws InterruptedException {
- mLock.lock();
- try {
- while (!mIsResumed) {
- if (!mIsResumedCondition.await(TIMEOUT, TimeUnit.SECONDS)) {
- return false;
- }
- }
- return true;
- } finally {
- mLock.unlock();
- }
- }
-}
diff --git a/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java b/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
new file mode 100644
index 00000000..c46c6d3e
--- /dev/null
+++ b/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.LifecycleRegistryOwner;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * LifecycleRegistryOwner that extends FragmentActivity.
+ */
+public class SupportLifecycleRegistryActivity extends FragmentActivity implements
+ LifecycleRegistryOwner, CollectingActivity {
+ private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
+ @Override
+ public LifecycleRegistry getLifecycle() {
+ return mLifecycleRegistry;
+ }
+
+ private List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
+ private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+ private CountDownLatch mLatch = new CountDownLatch(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_CREATE));
+ getLifecycle().addObserver(mTestObserver);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_START));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_RESUME));
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_DESTROY));
+ mLatch.countDown();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_STOP));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_PAUSE));
+ }
+
+ /**
+ * awaits for all events and returns them.
+ */
+ @Override
+ public List<Pair<TestEvent, Event>> waitForCollectedEvents() throws InterruptedException {
+ mLatch.await(TIMEOUT, TimeUnit.SECONDS);
+ return mCollectedEvents;
+ }
+}
diff --git a/android/arch/lifecycle/testapp/TestEvent.java b/android/arch/lifecycle/testapp/TestEvent.java
index 788045a2..0929f84a 100644
--- a/android/arch/lifecycle/testapp/TestEvent.java
+++ b/android/arch/lifecycle/testapp/TestEvent.java
@@ -17,6 +17,6 @@
package android.arch.lifecycle.testapp;
public enum TestEvent {
- OWNER_CALLBACK,
- LIFECYCLE_EVENT,
+ ACTIVITY_CALLBACK,
+ LIFECYCLE_EVENT
}
diff --git a/android/arch/lifecycle/testapp/TestObserver.java b/android/arch/lifecycle/testapp/TestObserver.java
index 00b8e16d..c6112396 100644
--- a/android/arch/lifecycle/testapp/TestObserver.java
+++ b/android/arch/lifecycle/testapp/TestObserver.java
@@ -28,7 +28,7 @@ import android.arch.lifecycle.Lifecycle.Event;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.OnLifecycleEvent;
-import android.support.v4.util.Pair;
+import android.util.Pair;
import java.util.List;
diff --git a/android/arch/paging/BoundedDataSource.java b/android/arch/paging/BoundedDataSource.java
index 06564907..664ab16c 100644
--- a/android/arch/paging/BoundedDataSource.java
+++ b/android/arch/paging/BoundedDataSource.java
@@ -21,6 +21,7 @@ import android.support.annotation.RestrictTo;
import android.support.annotation.WorkerThread;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -74,6 +75,7 @@ public abstract class BoundedDataSource<Value> extends PositionalDataSource<Valu
if (result.size() != loadSize) {
throw new IllegalStateException("invalid number of items returned.");
}
+ Collections.reverse(result);
}
return result;
}
diff --git a/android/arch/paging/ContiguousDataSource.java b/android/arch/paging/ContiguousDataSource.java
index be9da200..afcc208c 100644
--- a/android/arch/paging/ContiguousDataSource.java
+++ b/android/arch/paging/ContiguousDataSource.java
@@ -26,65 +26,21 @@ import java.util.List;
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, Value> {
+ /**
+ * Number of items that this DataSource can provide in total, or COUNT_UNDEFINED.
+ *
+ * @return number of items that this DataSource can provide in total, or COUNT_UNDEFINED
+ * if difficult or undesired to compute.
+ */
+ public int countItems() {
+ return COUNT_UNDEFINED;
+ }
+
@Override
boolean isContiguous() {
return true;
}
- void loadInitial(Key key, int pageSize, boolean enablePlaceholders,
- PageResult.Receiver<Key, Value> receiver) {
- NullPaddedList<Value> initial = loadInitial(key, pageSize, enablePlaceholders);
- if (initial != null) {
- receiver.onPageResult(new PageResult<>(
- PageResult.INIT,
- new Page<Key, Value>(initial.mList),
- initial.getLeadingNullCount(),
- initial.getTrailingNullCount(),
- initial.getPositionOffset()));
- } else {
- receiver.onPageResult(new PageResult<Key, Value>(
- PageResult.INIT, null, 0, 0, 0));
- }
- }
-
- void loadAfter(int currentEndIndex, @NonNull Value currentEndItem, int pageSize,
- PageResult.Receiver<Key, Value> receiver) {
- List<Value> list = loadAfter(currentEndIndex, currentEndItem, pageSize);
-
- Page<Key, Value> page = list != null
- ? new Page<Key, Value>(list) : null;
-
- receiver.postOnPageResult(new PageResult<>(
- PageResult.APPEND, page, 0, 0, 0));
- }
-
- void loadBefore(int currentBeginIndex, @NonNull Value currentBeginItem, int pageSize,
- PageResult.Receiver<Key, Value> receiver) {
- List<Value> list = loadBefore(currentBeginIndex, currentBeginItem, pageSize);
-
- Page<Key, Value> page = list != null
- ? new Page<Key, Value>(list) : null;
-
- receiver.postOnPageResult(new PageResult<>(
- PageResult.PREPEND, page, 0, 0, 0));
- }
-
- /**
- * Get the key from either the position, or item, or null if position/item invalid.
- * <p>
- * Position may not match passed item's position - if trying to query the key from a position
- * that isn't yet loaded, a fallback item (last loaded item accessed) will be passed.
- */
- abstract Key getKey(int position, Value item);
-
- @Nullable
- abstract List<Value> loadAfterImpl(int currentEndIndex,
- @NonNull Value currentEndItem, int pageSize);
-
- @Nullable
- abstract List<Value> loadBeforeImpl(int currentBeginIndex,
- @NonNull Value currentBeginItem, int pageSize);
-
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@WorkerThread
@@ -92,7 +48,21 @@ public abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, V
public abstract NullPaddedList<Value> loadInitial(
Key key, int initialLoadSize, boolean enablePlaceholders);
- /** @hide */
+ /**
+ * Load data after the given position / item.
+ * <p>
+ * It's valid to return a different list size than the page size, if it's easier for this data
+ * source. It is generally safer to increase number loaded than reduce.
+ *
+ * @param currentEndIndex Load items after this index, starting with currentEndIndex + 1.
+ * @param currentEndItem Load items after this item, can be used for precise querying based on
+ * item contents.
+ * @param pageSize Suggested number of items to load.
+ * @return List of items, starting at position currentEndIndex + 1. Null if the data source is
+ * no longer valid, and should not be queried again.
+ *
+ * @hide
+ */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@WorkerThread
@Nullable
@@ -108,7 +78,24 @@ public abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, V
return list;
}
- /** @hide */
+ @Nullable
+ abstract List<Value> loadAfterImpl(int currentEndIndex,
+ @NonNull Value currentEndItem, int pageSize);
+
+ /**
+ * Load data before the given position / item.
+ * <p>
+ * It's valid to return a different list size than the page size, if it's easier for this data
+ * source. It is generally safer to increase number loaded than reduce.
+ *
+ * @param currentBeginIndex Load items before this index, starting with currentBeginIndex - 1.
+ * @param currentBeginItem Load items after this item, can be used for precise querying based
+ * on item contents.
+ * @param pageSize Suggested number of items to load.
+ * @return List of items, in descending order, starting at position currentBeginIndex - 1.
+ *
+ * @hide
+ */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@WorkerThread
@Nullable
@@ -124,4 +111,15 @@ public abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, V
return list;
}
+
+ @Nullable
+ abstract List<Value> loadBeforeImpl(int currentBeginIndex,
+ @NonNull Value currentBeginItem, int pageSize);
+
+ /**
+ * Get the key from either the position, or item. Position may not match passed item's position,
+ * if trying to query the key from a position that isn't yet loaded, so a fallback item must be
+ * used.
+ */
+ abstract Key getKey(int position, Value item);
}
diff --git a/android/arch/paging/PagedStorageDiffHelper.java b/android/arch/paging/ContiguousDiffHelper.java
index 6fc70390..7dd194b2 100644
--- a/android/arch/paging/PagedStorageDiffHelper.java
+++ b/android/arch/paging/ContiguousDiffHelper.java
@@ -16,31 +16,36 @@
package android.arch.paging;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.support.v7.recyclerview.extensions.DiffCallback;
import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
-class PagedStorageDiffHelper {
- private PagedStorageDiffHelper() {
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ContiguousDiffHelper {
+ private ContiguousDiffHelper() {
}
+ @NonNull
static <T> DiffUtil.DiffResult computeDiff(
- final PagedStorage<?, T> oldList,
- final PagedStorage<?, T> newList,
- final DiffCallback<T> diffCallback) {
- final int oldOffset = oldList.computeLeadingNulls();
- final int newOffset = newList.computeLeadingNulls();
-
- final int oldSize = oldList.size() - oldOffset - oldList.computeTrailingNulls();
- final int newSize = newList.size() - newOffset - newList.computeTrailingNulls();
+ final NullPaddedList<T> oldList, final NullPaddedList<T> newList,
+ final DiffCallback<T> diffCallback, boolean detectMoves) {
+ if (!oldList.isImmutable()) {
+ throw new IllegalArgumentException("list must be immutable to safely perform diff");
+ }
+ if (!newList.isImmutable()) {
+ throw new IllegalArgumentException("list must be immutable to safely perform diff");
+ }
return DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
- T oldItem = oldList.get(oldItemPosition + oldOffset);
- T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
+ T oldItem = oldList.mList.get(oldItemPosition);
+ T newItem = newList.mList.get(newItemPosition);
if (oldItem == null || newItem == null) {
return null;
}
@@ -49,22 +54,21 @@ class PagedStorageDiffHelper {
@Override
public int getOldListSize() {
- return oldSize;
+ return oldList.mList.size();
}
@Override
public int getNewListSize() {
- return newSize;
+ return newList.mList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
- T oldItem = oldList.get(oldItemPosition + oldOffset);
- T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
+ T oldItem = oldList.mList.get(oldItemPosition);
+ T newItem = newList.mList.get(newItemPosition);
if (oldItem == newItem) {
return true;
}
- //noinspection SimplifiableIfStatement
if (oldItem == null || newItem == null) {
return false;
}
@@ -73,19 +77,18 @@ class PagedStorageDiffHelper {
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
- T oldItem = oldList.get(oldItemPosition + oldOffset);
- T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
+ T oldItem = oldList.mList.get(oldItemPosition);
+ T newItem = newList.mList.get(newItemPosition);
if (oldItem == newItem) {
return true;
}
- //noinspection SimplifiableIfStatement
if (oldItem == null || newItem == null) {
return false;
}
return diffCallback.areContentsTheSame(oldItem, newItem);
}
- }, true);
+ }, detectMoves);
}
private static class OffsettingListUpdateCallback implements ListUpdateCallback {
@@ -131,25 +134,21 @@ class PagedStorageDiffHelper {
* immediately after dispatching this diff.
*/
static <T> void dispatchDiff(ListUpdateCallback callback,
- final PagedStorage<?, T> oldList,
- final PagedStorage<?, T> newList,
+ final NullPaddedList<T> oldList, final NullPaddedList<T> newList,
final DiffUtil.DiffResult diffResult) {
- final int trailingOld = oldList.computeTrailingNulls();
- final int trailingNew = newList.computeTrailingNulls();
- final int leadingOld = oldList.computeLeadingNulls();
- final int leadingNew = newList.computeLeadingNulls();
-
- if (trailingOld == 0
- && trailingNew == 0
- && leadingOld == 0
- && leadingNew == 0) {
+ if (oldList.getLeadingNullCount() == 0
+ && oldList.getTrailingNullCount() == 0
+ && newList.getLeadingNullCount() == 0
+ && newList.getTrailingNullCount() == 0) {
// Simple case, dispatch & return
diffResult.dispatchUpdatesTo(callback);
return;
}
// First, remove or insert trailing nulls
+ final int trailingOld = oldList.getTrailingNullCount();
+ final int trailingNew = newList.getTrailingNullCount();
if (trailingOld > trailingNew) {
int count = trailingOld - trailingNew;
callback.onRemoved(oldList.size() - count, count);
@@ -158,6 +157,8 @@ class PagedStorageDiffHelper {
}
// Second, remove or insert leading nulls
+ final int leadingOld = oldList.getLeadingNullCount();
+ final int leadingNew = newList.getLeadingNullCount();
if (leadingOld > leadingNew) {
callback.onRemoved(0, leadingOld - leadingNew);
} else if (leadingOld < leadingNew) {
diff --git a/android/arch/paging/PagedStorageDiffHelperTest.java b/android/arch/paging/ContiguousDiffHelperTest.java
index 8cb92246..4f221b34 100644
--- a/android/arch/paging/PagedStorageDiffHelperTest.java
+++ b/android/arch/paging/ContiguousDiffHelperTest.java
@@ -16,9 +16,6 @@
package android.arch.paging;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -32,12 +29,11 @@ import android.support.v7.util.ListUpdateCallback;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
+import org.mockito.Mockito;
@SmallTest
@RunWith(JUnit4.class)
-public class PagedStorageDiffHelperTest {
+public class ContiguousDiffHelperTest {
private interface CallbackValidator {
void validate(ListUpdateCallback callback);
}
@@ -55,18 +51,13 @@ public class PagedStorageDiffHelperTest {
}
};
- public static Page<Integer, String> createPage(String... items) {
- return new Page<>(Arrays.asList(items));
- }
-
- private static void validateTwoListDiff(PagedStorage<?, String> oldList,
- PagedStorage<?, String> newList,
+ private void validateTwoListDiff(StringPagedList oldList, StringPagedList newList,
CallbackValidator callbackValidator) {
- DiffUtil.DiffResult diffResult = PagedStorageDiffHelper.computeDiff(
- oldList, newList, DIFF_CALLBACK);
+ DiffUtil.DiffResult diffResult = ContiguousDiffHelper.computeDiff(oldList, newList,
+ DIFF_CALLBACK, false);
- ListUpdateCallback listUpdateCallback = mock(ListUpdateCallback.class);
- PagedStorageDiffHelper.dispatchDiff(listUpdateCallback, oldList, newList, diffResult);
+ ListUpdateCallback listUpdateCallback = Mockito.mock(ListUpdateCallback.class);
+ ContiguousDiffHelper.dispatchDiff(listUpdateCallback, oldList, newList, diffResult);
callbackValidator.validate(listUpdateCallback);
}
@@ -74,35 +65,8 @@ public class PagedStorageDiffHelperTest {
@Test
public void sameListNoUpdates() {
validateTwoListDiff(
- new PagedStorage<>(5, createPage("a", "b", "c"), 5),
- new PagedStorage<>(5, createPage("a", "b", "c"), 5),
- new CallbackValidator() {
- @Override
- public void validate(ListUpdateCallback callback) {
- verifyZeroInteractions(callback);
- }
- }
- );
- }
-
- @Test
- public void sameListNoUpdatesPlaceholder() {
- PagedStorage<Integer, String> storageNoPlaceholder =
- new PagedStorage<>(0, createPage("a", "b", "c"), 10);
-
- PagedStorage<Integer, String> storageWithPlaceholder =
- new PagedStorage<>(0, createPage("a", "b", "c"), 10);
- storageWithPlaceholder.allocatePlaceholders(3, 0, 3,
- /* ignored */ mock(PagedStorage.Callback.class));
-
- // even though one has placeholders, and null counts are different...
- assertEquals(10, storageNoPlaceholder.getTrailingNullCount());
- assertEquals(7, storageWithPlaceholder.getTrailingNullCount());
-
- // ... should be no interactions, since content still same
- validateTwoListDiff(
- storageNoPlaceholder,
- storageWithPlaceholder,
+ new StringPagedList(5, 5, "a", "b", "c"),
+ new StringPagedList(5, 5, "a", "b", "c"),
new CallbackValidator() {
@Override
public void validate(ListUpdateCallback callback) {
@@ -115,8 +79,8 @@ public class PagedStorageDiffHelperTest {
@Test
public void appendFill() {
validateTwoListDiff(
- new PagedStorage<>(5, createPage("a", "b"), 5),
- new PagedStorage<>(5, createPage("a", "b", "c"), 4),
+ new StringPagedList(5, 5, "a", "b"),
+ new StringPagedList(5, 4, "a", "b", "c"),
new CallbackValidator() {
@Override
public void validate(ListUpdateCallback callback) {
@@ -132,8 +96,8 @@ public class PagedStorageDiffHelperTest {
@Test
public void prependFill() {
validateTwoListDiff(
- new PagedStorage<>(5, createPage("b", "c"), 5),
- new PagedStorage<>(4, createPage("a", "b", "c"), 5),
+ new StringPagedList(5, 5, "b", "c"),
+ new StringPagedList(4, 5, "a", "b", "c"),
new CallbackValidator() {
@Override
public void validate(ListUpdateCallback callback) {
@@ -149,8 +113,8 @@ public class PagedStorageDiffHelperTest {
@Test
public void change() {
validateTwoListDiff(
- new PagedStorage<>(5, createPage("a1", "b1", "c1"), 5),
- new PagedStorage<>(5, createPage("a2", "b1", "c2"), 5),
+ new StringPagedList(5, 5, "a1", "b1", "c1"),
+ new StringPagedList(5, 5, "a2", "b1", "c2"),
new CallbackValidator() {
@Override
public void validate(ListUpdateCallback callback) {
@@ -161,5 +125,4 @@ public class PagedStorageDiffHelperTest {
}
);
}
-
}
diff --git a/android/arch/paging/ContiguousPagedList.java b/android/arch/paging/ContiguousPagedList.java
index 7835dbe3..d8907c3b 100644
--- a/android/arch/paging/ContiguousPagedList.java
+++ b/android/arch/paging/ContiguousPagedList.java
@@ -16,136 +16,101 @@
package android.arch.paging;
-import android.support.annotation.AnyThread;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ContiguousPagedList<T> extends NullPaddedList<T> {
+
+ private final ContiguousDataSource<?, T> mDataSource;
+ private final Executor mMainThreadExecutor;
+ private final Executor mBackgroundThreadExecutor;
+ private final Config mConfig;
-class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
- private final ContiguousDataSource<K, V> mDataSource;
private boolean mPrependWorkerRunning = false;
private boolean mAppendWorkerRunning = false;
private int mPrependItemsRequested = 0;
private int mAppendItemsRequested = 0;
- @SuppressWarnings("unchecked")
- private final PagedStorage<K, V> mKeyedStorage = (PagedStorage<K, V>) mStorage;
-
- private final PageResult.Receiver<K, V> mReceiver = new PageResult.Receiver<K, V>() {
- @AnyThread
- @Override
- public void postOnPageResult(@NonNull final PageResult<K, V> pageResult) {
- // NOTE: if we're already on main thread, this can delay page receive by a frame
- mMainThreadExecutor.execute(new Runnable() {
- @Override
- public void run() {
- onPageResult(pageResult);
- }
- });
- }
+ private int mLastLoad = 0;
+ private T mLastItem = null;
- @MainThread
- @Override
- public void onPageResult(@NonNull PageResult<K, V> pageResult) {
- if (pageResult.page == null) {
- detach();
- return;
- }
+ private AtomicBoolean mDetached = new AtomicBoolean(false);
- if (isDetached()) {
- // No op, have detached
- return;
- }
+ private ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
- Page<K, V> page = pageResult.page;
- if (pageResult.type == PageResult.INIT) {
- mKeyedStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
- pageResult.positionOffset, ContiguousPagedList.this);
- notifyInserted(0, mKeyedStorage.size());
- } else if (pageResult.type == PageResult.APPEND) {
- mKeyedStorage.appendPage(page, ContiguousPagedList.this);
- } else if (pageResult.type == PageResult.PREPEND) {
- mKeyedStorage.prependPage(page, ContiguousPagedList.this);
- }
- }
- };
-
- ContiguousPagedList(
- @NonNull ContiguousDataSource<K, V> dataSource,
+ @WorkerThread
+ <K> ContiguousPagedList(@NonNull ContiguousDataSource<K, T> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
- @NonNull Config config,
- final @Nullable K key) {
- super(new PagedStorage<K, V>(), mainThreadExecutor, backgroundThreadExecutor, config);
- mDataSource = dataSource;
-
- // blocking init just triggers the initial load on the construction thread -
- // Could still be posted with callback, if desired.
- mDataSource.loadInitial(key,
- mConfig.mInitialLoadSizeHint,
- mConfig.mEnablePlaceholders,
- mReceiver);
- }
-
- @MainThread
- @Override
- void dispatchUpdatesSinceSnapshot(
- @NonNull PagedList<V> pagedListSnapshot, @NonNull Callback callback) {
-
- final PagedStorage<?, V> snapshot = pagedListSnapshot.mStorage;
-
- final int newlyAppended = mStorage.getNumberAppended() - snapshot.getNumberAppended();
- final int newlyPrepended = mStorage.getNumberPrepended() - snapshot.getNumberPrepended();
-
- final int previousTrailing = snapshot.getTrailingNullCount();
- final int previousLeading = snapshot.getLeadingNullCount();
-
- // Validate that the snapshot looks like a previous version of this list - if it's not,
- // we can't be sure we'll dispatch callbacks safely
- if (newlyAppended < 0
- || newlyPrepended < 0
- || mStorage.getTrailingNullCount() != Math.max(previousTrailing - newlyAppended, 0)
- || mStorage.getLeadingNullCount() != Math.max(previousLeading - newlyPrepended, 0)
- || (mStorage.getStorageCount()
- != snapshot.getStorageCount() + newlyAppended + newlyPrepended)) {
- throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear"
- + " to be a snapshot of this PagedList");
- }
-
- if (newlyAppended != 0) {
- final int changedCount = Math.min(previousTrailing, newlyAppended);
- final int addedCount = newlyAppended - changedCount;
+ Config config,
+ @Nullable K key) {
+ super();
- final int endPosition = snapshot.getLeadingNullCount() + snapshot.getStorageCount();
- if (changedCount != 0) {
- callback.onChanged(endPosition, changedCount);
+ mDataSource = dataSource;
+ mMainThreadExecutor = mainThreadExecutor;
+ mBackgroundThreadExecutor = backgroundThreadExecutor;
+ mConfig = config;
+ NullPaddedList<T> initialState = dataSource.loadInitial(
+ key, config.mInitialLoadSizeHint, config.mEnablePlaceholders);
+
+ if (initialState != null) {
+ mPositionOffset = initialState.getPositionOffset();
+
+ mLeadingNullCount = initialState.getLeadingNullCount();
+ mList = new ArrayList<>(initialState.mList);
+ mTrailingNullCount = initialState.getTrailingNullCount();
+
+ if (initialState.getLeadingNullCount() == 0
+ && initialState.getTrailingNullCount() == 0
+ && config.mPrefetchDistance < 1) {
+ throw new IllegalArgumentException("Null padding is required to support the 0"
+ + " prefetch case - require either null items or prefetching to fetch"
+ + " beyond initial load.");
}
- if (addedCount != 0) {
- callback.onInserted(endPosition + changedCount, addedCount);
+
+ if (initialState.size() != 0) {
+ mLastLoad = mLeadingNullCount + mList.size() / 2;
+ mLastItem = mList.get(mList.size() / 2);
}
+ } else {
+ mList = new ArrayList<>();
+ detach();
+ }
+ if (mList.size() == 0) {
+ // Empty initial state, so don't try and fetch data.
+ mPrependWorkerRunning = true;
+ mAppendWorkerRunning = true;
}
- if (newlyPrepended != 0) {
- final int changedCount = Math.min(previousLeading, newlyPrepended);
- final int addedCount = newlyPrepended - changedCount;
+ }
- if (changedCount != 0) {
- callback.onChanged(previousLeading, changedCount);
- }
- if (addedCount != 0) {
- callback.onInserted(0, addedCount);
- }
+ @Override
+ public T get(int index) {
+ T item = super.get(index);
+ if (item != null) {
+ mLastItem = item;
}
+ return item;
}
- @MainThread
@Override
- protected void loadAroundInternal(int index) {
- int prependItems = mConfig.mPrefetchDistance - (index - mStorage.getLeadingNullCount());
- int appendItems = index + mConfig.mPrefetchDistance
- - (mStorage.getLeadingNullCount() + mStorage.getStorageCount());
+ public void loadAround(int index) {
+ mLastLoad = index + mPositionOffset;
+
+ int prependItems = mConfig.mPrefetchDistance - (index - mLeadingNullCount);
+ int appendItems = index + mConfig.mPrefetchDistance - (mLeadingNullCount + mList.size());
mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
if (mPrependItemsRequested > 0) {
@@ -158,6 +123,21 @@ class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Cal
}
}
+ @Override
+ public int getLoadedCount() {
+ return mList.size();
+ }
+
+ @Override
+ public int getLeadingNullCount() {
+ return mLeadingNullCount;
+ }
+
+ @Override
+ public int getTrailingNullCount() {
+ return mTrailingNullCount;
+ }
+
@MainThread
private void schedulePrepend() {
if (mPrependWorkerRunning) {
@@ -165,17 +145,29 @@ class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Cal
}
mPrependWorkerRunning = true;
- final int position = mStorage.getLeadingNullCount() + mStorage.getPositionOffset();
-
- // safe to access first item here - mStorage can't be empty if we're prepending
- final V item = mStorage.getFirstContiguousItem();
+ final int position = mLeadingNullCount + mPositionOffset;
+ final T item = mList.get(0);
mBackgroundThreadExecutor.execute(new Runnable() {
@Override
public void run() {
- if (isDetached()) {
+ if (mDetached.get()) {
return;
}
- mDataSource.loadBefore(position, item, mConfig.mPageSize, mReceiver);
+
+ final List<T> data = mDataSource.loadBefore(position, item, mConfig.mPageSize);
+ if (data != null) {
+ mMainThreadExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (mDetached.get()) {
+ return;
+ }
+ prependImpl(data);
+ }
+ });
+ } else {
+ detach();
+ }
}
});
}
@@ -187,44 +179,56 @@ class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Cal
}
mAppendWorkerRunning = true;
- final int position = mStorage.getLeadingNullCount()
- + mStorage.getStorageCount() - 1 + mStorage.getPositionOffset();
-
- // safe to access first item here - mStorage can't be empty if we're appending
- final V item = mStorage.getLastContiguousItem();
+ final int position = mLeadingNullCount + mList.size() - 1 + mPositionOffset;
+ final T item = mList.get(mList.size() - 1);
mBackgroundThreadExecutor.execute(new Runnable() {
@Override
public void run() {
- if (isDetached()) {
+ if (mDetached.get()) {
return;
}
- mDataSource.loadAfter(position, item, mConfig.mPageSize, mReceiver);
+
+ final List<T> data = mDataSource.loadAfter(position, item, mConfig.mPageSize);
+ if (data != null) {
+ mMainThreadExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (mDetached.get()) {
+ return;
+ }
+ appendImpl(data);
+ }
+ });
+ } else {
+ detach();
+ }
}
});
}
- @Override
- boolean isContiguous() {
- return true;
- }
+ @MainThread
+ private void prependImpl(List<T> before) {
+ final int count = before.size();
+ if (count == 0) {
+ // Nothing returned from source, stop loading in this direction
+ return;
+ }
- @Nullable
- @Override
- public Object getLastKey() {
- return mDataSource.getKey(mLastLoad, mLastItem);
- }
+ Collections.reverse(before);
+ mList.addAll(0, before);
- @MainThread
- @Override
- public void onInitialized(int count) {
- notifyInserted(0, count);
- }
+ final int changedCount = Math.min(mLeadingNullCount, count);
+ final int addedCount = count - changedCount;
- @MainThread
- @Override
- public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) {
- // consider whether to post more work, now that a page is fully prepended
- mPrependItemsRequested = mPrependItemsRequested - changedCount - addedCount;
+ if (changedCount != 0) {
+ mLeadingNullCount -= changedCount;
+ }
+ mPositionOffset -= addedCount;
+ mNumberPrepended += count;
+
+
+ // only try to post more work after fully prepended (with offsets / null counts updated)
+ mPrependItemsRequested -= count;
mPrependWorkerRunning = false;
if (mPrependItemsRequested > 0) {
// not done prepending, keep going
@@ -232,16 +236,39 @@ class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Cal
}
// finally dispatch callbacks, after prepend may have already been scheduled
- notifyChanged(leadingNulls, changedCount);
- notifyInserted(0, addedCount);
+ for (WeakReference<Callback> weakRef : mCallbacks) {
+ Callback callback = weakRef.get();
+ if (callback != null) {
+ if (changedCount != 0) {
+ callback.onChanged(mLeadingNullCount, changedCount);
+ }
+ if (addedCount != 0) {
+ callback.onInserted(0, addedCount);
+ }
+ }
+ }
}
@MainThread
- @Override
- public void onPageAppended(int endPosition, int changedCount, int addedCount) {
- // consider whether to post more work, now that a page is fully appended
+ private void appendImpl(List<T> after) {
+ final int count = after.size();
+ if (count == 0) {
+ // Nothing returned from source, stop loading in this direction
+ return;
+ }
+
+ mList.addAll(after);
- mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
+ final int changedCount = Math.min(mTrailingNullCount, count);
+ final int addedCount = count - changedCount;
+
+ if (changedCount != 0) {
+ mTrailingNullCount -= changedCount;
+ }
+ mNumberAppended += count;
+
+ // only try to post more work after fully appended (with null counts updated)
+ mAppendItemsRequested -= count;
mAppendWorkerRunning = false;
if (mAppendItemsRequested > 0) {
// not done appending, keep going
@@ -249,19 +276,100 @@ class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Cal
}
// finally dispatch callbacks, after append may have already been scheduled
- notifyChanged(endPosition, changedCount);
- notifyInserted(endPosition + changedCount, addedCount);
+ for (WeakReference<Callback> weakRef : mCallbacks) {
+ Callback callback = weakRef.get();
+ if (callback != null) {
+ final int endPosition = mLeadingNullCount + mList.size() - count;
+ if (changedCount != 0) {
+ callback.onChanged(endPosition, changedCount);
+ }
+ if (addedCount != 0) {
+ callback.onInserted(endPosition + changedCount, addedCount);
+ }
+ }
+ }
}
- @MainThread
@Override
- public void onPagePlaceholderInserted(int pageIndex) {
- throw new IllegalStateException("Tiled callback on ContiguousPagedList");
+ public boolean isImmutable() {
+ // TODO: return true if had nulls, and now getLoadedCount() == size(). Is that safe?
+ // Currently we don't prevent DataSources from returning more items than their null counts
+ return isDetached();
}
- @MainThread
@Override
- public void onPageInserted(int start, int count) {
- throw new IllegalStateException("Tiled callback on ContiguousPagedList");
+ public void addWeakCallback(@Nullable PagedList<T> previousSnapshot,
+ @NonNull Callback callback) {
+ NullPaddedList<T> snapshot = (NullPaddedList<T>) previousSnapshot;
+ if (snapshot != this && snapshot != null) {
+ final int newlyAppended = mNumberAppended - snapshot.getNumberAppended();
+ final int newlyPrepended = mNumberPrepended - snapshot.getNumberPrepended();
+
+ final int previousTrailing = snapshot.getTrailingNullCount();
+ final int previousLeading = snapshot.getLeadingNullCount();
+
+ // Validate that the snapshot looks like a previous version of this list - if it's not,
+ // we can't be sure we'll dispatch callbacks safely
+ if (newlyAppended < 0
+ || newlyPrepended < 0
+ || mTrailingNullCount != Math.max(previousTrailing - newlyAppended, 0)
+ || mLeadingNullCount != Math.max(previousLeading - newlyPrepended, 0)
+ || snapshot.getLoadedCount() + newlyAppended + newlyPrepended != mList.size()) {
+ throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear"
+ + " to be a snapshot of this list");
+ }
+
+ if (newlyAppended != 0) {
+ final int changedCount = Math.min(previousTrailing, newlyAppended);
+ final int addedCount = newlyAppended - changedCount;
+
+ final int endPosition =
+ snapshot.getLeadingNullCount() + snapshot.getLoadedCount();
+ if (changedCount != 0) {
+ callback.onChanged(endPosition, changedCount);
+ }
+ if (addedCount != 0) {
+ callback.onInserted(endPosition + changedCount, addedCount);
+ }
+ }
+ if (newlyPrepended != 0) {
+ final int changedCount = Math.min(previousLeading, newlyPrepended);
+ final int addedCount = newlyPrepended - changedCount;
+
+ if (changedCount != 0) {
+ callback.onChanged(previousLeading, changedCount);
+ }
+ if (addedCount != 0) {
+ callback.onInserted(0, addedCount);
+ }
+ }
+ }
+ mCallbacks.add(new WeakReference<>(callback));
+ }
+
+ @Override
+ public void removeWeakCallback(@NonNull Callback callback) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ Callback currentCallback = mCallbacks.get(i).get();
+ if (currentCallback == null || currentCallback == callback) {
+ mCallbacks.remove(i);
+ }
+ }
+ }
+
+ @Override
+ public boolean isDetached() {
+ return mDetached.get();
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public void detach() {
+ mDetached.set(true);
+ }
+
+ @Nullable
+ @Override
+ public Object getLastKey() {
+ return mDataSource.getKey(mLastLoad, mLastItem);
}
}
diff --git a/android/arch/paging/ContiguousPagedListTest.java b/android/arch/paging/ContiguousPagedListTest.java
index 43f556a8..ee7ea6a4 100644
--- a/android/arch/paging/ContiguousPagedListTest.java
+++ b/android/arch/paging/ContiguousPagedListTest.java
@@ -16,7 +16,6 @@
package android.arch.paging;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.mock;
@@ -110,7 +109,6 @@ public class ContiguousPagedListTest {
private void verifyRange(int start, int count, NullPaddedList<Item> actual) {
if (mCounted) {
- //noinspection UnnecessaryLocalVariable
int expectedLeading = start;
int expectedTrailing = ITEMS.size() - start - count;
assertEquals(ITEMS.size(), actual.size());
@@ -134,38 +132,6 @@ public class ContiguousPagedListTest {
}
}
- @SuppressWarnings("SuspiciousSystemArraycopy")
- private void verifyRange(int start, int count, PagedStorage<?, Item> actual) {
- if (mCounted) {
- Item[] expected = new Item[ITEMS.size()];
- System.arraycopy(ITEMS.toArray(), start, expected, start, count);
- assertArrayEquals(expected, actual.toArray());
-
- //noinspection UnnecessaryLocalVariable
- int expectedLeading = start;
- int expectedTrailing = ITEMS.size() - start - count;
- assertEquals(ITEMS.size(), actual.size());
- assertEquals(ITEMS.size() - expectedLeading - expectedTrailing,
- actual.getStorageCount());
- assertEquals(expectedLeading, actual.getLeadingNullCount());
- assertEquals(expectedTrailing, actual.getTrailingNullCount());
-
- } else {
- Item[] expected = new Item[count];
- System.arraycopy(ITEMS.toArray(), start, expected, 0, count);
- assertArrayEquals(expected, actual.toArray());
-
- assertEquals(count, actual.size());
- assertEquals(actual.size(), actual.getStorageCount());
- assertEquals(0, actual.getLeadingNullCount());
- assertEquals(0, actual.getTrailingNullCount());
- }
- }
-
- private void verifyRange(int start, int count, PagedList<Item> actual) {
- verifyRange(start, count, actual.mStorage);
- }
-
private void verifyCallback(PagedList.Callback callback, int countedPosition,
int uncountedPosition) {
if (mCounted) {
@@ -188,7 +154,7 @@ public class ContiguousPagedListTest {
}
- private ContiguousPagedList<Integer, Item> createCountedPagedList(
+ private ContiguousPagedList<Item> createCountedPagedList(
PagedList.Config config, int initialPosition) {
TestSource source = new TestSource();
return new ContiguousPagedList<>(
@@ -197,7 +163,7 @@ public class ContiguousPagedListTest {
initialPosition);
}
- private ContiguousPagedList<Integer, Item> createCountedPagedList(int initialPosition) {
+ private ContiguousPagedList<Item> createCountedPagedList(int initialPosition) {
return createCountedPagedList(
new PagedList.Config.Builder()
.setInitialLoadSizeHint(40)
@@ -208,14 +174,8 @@ public class ContiguousPagedListTest {
}
@Test
- public void construct() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(0);
- verifyRange(0, 40, pagedList);
- }
-
- @Test
public void append() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(0);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(0);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
verifyRange(0, 40, pagedList);
@@ -232,7 +192,7 @@ public class ContiguousPagedListTest {
@Test
public void prepend() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(80);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(80);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
verifyRange(60, 40, pagedList);
@@ -248,7 +208,7 @@ public class ContiguousPagedListTest {
@Test
public void outwards() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(50);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(50);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
verifyRange(30, 40, pagedList);
@@ -271,7 +231,7 @@ public class ContiguousPagedListTest {
@Test
public void multiAppend() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(0);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(0);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
verifyRange(0, 40, pagedList);
@@ -288,7 +248,7 @@ public class ContiguousPagedListTest {
@Test
public void distantPrefetch() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(
new PagedList.Config.Builder()
.setInitialLoadSizeHint(10)
.setPageSize(10)
@@ -314,7 +274,7 @@ public class ContiguousPagedListTest {
@Test
public void appendCallbackAddedLate() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(0);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(0);
verifyRange(0, 40, pagedList);
pagedList.loadAround(35);
@@ -322,7 +282,7 @@ public class ContiguousPagedListTest {
verifyRange(0, 60, pagedList);
// snapshot at 60 items
- PagedList<Item> snapshot = (PagedList<Item>) pagedList.snapshot();
+ NullPaddedList<Item> snapshot = (NullPaddedList<Item>) pagedList.snapshot();
verifyRange(0, 60, snapshot);
@@ -340,7 +300,7 @@ public class ContiguousPagedListTest {
@Test
public void prependCallbackAddedLate() {
- ContiguousPagedList<Integer, Item> pagedList = createCountedPagedList(80);
+ ContiguousPagedList<Item> pagedList = createCountedPagedList(80);
verifyRange(60, 40, pagedList);
pagedList.loadAround(mCounted ? 65 : 5);
@@ -348,7 +308,7 @@ public class ContiguousPagedListTest {
verifyRange(40, 60, pagedList);
// snapshot at 60 items
- PagedList<Item> snapshot = (PagedList<Item>) pagedList.snapshot();
+ NullPaddedList<Item> snapshot = (NullPaddedList<Item>) pagedList.snapshot();
verifyRange(40, 60, snapshot);
diff --git a/android/arch/paging/DataSource.java b/android/arch/paging/DataSource.java
index 524e570a..48fbec5f 100644
--- a/android/arch/paging/DataSource.java
+++ b/android/arch/paging/DataSource.java
@@ -17,7 +17,6 @@
package android.arch.paging;
import android.support.annotation.AnyThread;
-import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -61,6 +60,15 @@ public abstract class DataSource<Key, Value> {
public static int COUNT_UNDEFINED = -1;
/**
+ * Number of items that this DataSource can provide in total, or {@link #COUNT_UNDEFINED}.
+ *
+ * @return number of items that this DataSource can provide in total, or
+ * {@link #COUNT_UNDEFINED} if expensive or undesired to compute.
+ */
+ @WorkerThread
+ public abstract int countItems();
+
+ /**
* Returns true if the data source guaranteed to produce a contiguous set of items,
* never producing gaps.
*/
@@ -103,7 +111,7 @@ public abstract class DataSource<Key, Value> {
*/
@AnyThread
@SuppressWarnings("WeakerAccess")
- public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+ public void addInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
mOnInvalidatedCallbacks.add(onInvalidatedCallback);
}
@@ -114,7 +122,7 @@ public abstract class DataSource<Key, Value> {
*/
@AnyThread
@SuppressWarnings("WeakerAccess")
- public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
+ public void removeInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
mOnInvalidatedCallbacks.remove(onInvalidatedCallback);
}
diff --git a/android/arch/paging/KeyedDataSource.java b/android/arch/paging/KeyedDataSource.java
index 0d452946..8cf6829c 100644
--- a/android/arch/paging/KeyedDataSource.java
+++ b/android/arch/paging/KeyedDataSource.java
@@ -103,6 +103,10 @@ import java.util.List;
* @param <Value> Type of items being loaded by the DataSource.
*/
public abstract class KeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
+ @Override
+ public final int countItems() {
+ return 0; // method not called, can't be overridden
+ }
@Nullable
@Override
@@ -114,14 +118,7 @@ public abstract class KeyedDataSource<Key, Value> extends ContiguousDataSource<K
@Override
List<Value> loadBeforeImpl(
int currentBeginIndex, @NonNull Value currentBeginItem, int pageSize) {
- List<Value> list = loadBefore(getKey(currentBeginItem), pageSize);
-
- if (list != null && list.size() > 1) {
- // TODO: move out of keyed entirely, into the DB DataSource.
- list = new ArrayList<>(list);
- Collections.reverse(list);
- }
- return list;
+ return loadBefore(getKey(currentBeginItem), pageSize);
}
@Nullable
@@ -194,8 +191,6 @@ public abstract class KeyedDataSource<Key, Value> extends ContiguousDataSource<K
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @WorkerThread
- @Override
public NullPaddedList<Value> loadInitial(
@Nullable Key key, int initialLoadSize, boolean enablePlaceholders) {
if (isInvalid()) {
diff --git a/android/arch/paging/LivePagedListProvider.java b/android/arch/paging/LivePagedListProvider.java
index 07dd84bf..b7c68dd6 100644
--- a/android/arch/paging/LivePagedListProvider.java
+++ b/android/arch/paging/LivePagedListProvider.java
@@ -16,133 +16,5 @@
package android.arch.paging;
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.lifecycle.ComputableLiveData;
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.AnyThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-
-/**
- * Provides a {@code LiveData<PagedList>}, given a means to construct a DataSource.
- * <p>
- * Return type for data-loading system of an application or library to produce a
- * {@code LiveData<PagedList>}, while leaving the details of the paging mechanism up to the
- * consumer.
- * <p>
- * If you're using Room, it can generate a LivePagedListProvider from a query:
- * <pre>
- * {@literal @}Dao
- * interface UserDao {
- * {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
- * public abstract LivePagedListProvider&lt;Integer, User> usersByLastName();
- * }</pre>
- * In the above sample, {@code Integer} is used because it is the {@code Key} type of
- * {@link TiledDataSource}. Currently, Room can only generate a {@code LIMIT}/{@code OFFSET},
- * position based loader that uses TiledDataSource under the hood, and specifying {@code Integer}
- * here lets you pass an initial loading position as an integer.
- * <p>
- * In the future, Room plans to offer other key types to support paging content with a
- * {@link KeyedDataSource}.
- *
- * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
- * you're using TiledDataSource.
- * @param <Value> Data type produced by the DataSource, and held by the PagedLists.
- *
- * @see PagedListAdapter
- * @see DataSource
- * @see PagedList
- */
-public abstract class LivePagedListProvider<Key, Value> {
-
- /**
- * Construct a new data source to be wrapped in a new PagedList, which will be returned
- * through the LiveData.
- *
- * @return The data source.
- */
- @WorkerThread
- protected abstract DataSource<Key, Value> createDataSource();
-
- /**
- * Creates a LiveData of PagedLists, given the page size.
- * <p>
- * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
- * {@link android.support.v7.widget.RecyclerView}.
- *
- * @param initialLoadKey Initial key used to load initial data from the data source.
- * @param pageSize Page size defining how many items are loaded from a data source at a time.
- * Recommended to be multiple times the size of item displayed at once.
- *
- * @return The LiveData of PagedLists.
- */
- @AnyThread
- @NonNull
- public LiveData<PagedList<Value>> create(@Nullable Key initialLoadKey, int pageSize) {
- return create(initialLoadKey,
- new PagedList.Config.Builder()
- .setPageSize(pageSize)
- .build());
- }
-
- /**
- * Creates a LiveData of PagedLists, given the PagedList.Config.
- * <p>
- * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
- * {@link android.support.v7.widget.RecyclerView}.
- *
- * @param initialLoadKey Initial key to pass to the data source to initialize data with.
- * @param config PagedList.Config to use with created PagedLists. This specifies how the
- * lists will load data.
- *
- * @return The LiveData of PagedLists.
- */
- @AnyThread
- @NonNull
- public LiveData<PagedList<Value>> create(@Nullable final Key initialLoadKey,
- final PagedList.Config config) {
- return new ComputableLiveData<PagedList<Value>>() {
- @Nullable
- private PagedList<Value> mList;
- @Nullable
- private DataSource<Key, Value> mDataSource;
-
- private final DataSource.InvalidatedCallback mCallback =
- new DataSource.InvalidatedCallback() {
- @Override
- public void onInvalidated() {
- invalidate();
- }
- };
-
- @Override
- protected PagedList<Value> compute() {
- @Nullable Key initializeKey = initialLoadKey;
- if (mList != null) {
- //noinspection unchecked
- initializeKey = (Key) mList.getLastKey();
- }
-
- do {
- if (mDataSource != null) {
- mDataSource.removeInvalidatedCallback(mCallback);
- }
-
- mDataSource = createDataSource();
- mDataSource.addInvalidatedCallback(mCallback);
-
- mList = new PagedList.Builder<Key, Value>()
- .setDataSource(mDataSource)
- .setMainThreadExecutor(ArchTaskExecutor.getMainThreadExecutor())
- .setBackgroundThreadExecutor(
- ArchTaskExecutor.getIOThreadExecutor())
- .setConfig(config)
- .setInitialKey(initializeKey)
- .build();
- } while (mList.isDetached());
- return mList;
- }
- }.getLiveData();
- }
-}
+abstract public class LivePagedListProvider<K, T> {
+} \ No newline at end of file
diff --git a/android/arch/paging/NullPaddedList.java b/android/arch/paging/NullPaddedList.java
index c7b0b231..43000302 100644
--- a/android/arch/paging/NullPaddedList.java
+++ b/android/arch/paging/NullPaddedList.java
@@ -16,9 +16,11 @@
package android.arch.paging;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
-import java.util.AbstractList;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -29,11 +31,18 @@ import java.util.List;
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class NullPaddedList<Type> extends AbstractList<Type> {
+public class NullPaddedList<Type> extends PagedList<Type> {
List<Type> mList;
- private int mTrailingNullCount;
- private int mLeadingNullCount;
- private int mPositionOffset;
+ int mTrailingNullCount;
+ int mLeadingNullCount;
+ int mPositionOffset;
+
+ // track the items prepended/appended since the PagedList was initialized
+ int mNumberPrepended;
+ int mNumberAppended;
+
+ NullPaddedList() {
+ }
@Override
public String toString() {
@@ -82,6 +91,20 @@ public class NullPaddedList<Type> extends AbstractList<Type> {
mPositionOffset = positionOffset;
}
+ /**
+ * Create a copy of the passed NullPaddedList.
+ *
+ * @param other Other list to copy.
+ */
+ NullPaddedList(NullPaddedList<Type> other) {
+ mLeadingNullCount = other.getLeadingNullCount();
+ mList = other.isImmutable() ? other.mList : new ArrayList<>(other.mList);
+ mTrailingNullCount = other.getTrailingNullCount();
+
+ mNumberPrepended = other.getNumberPrepended();
+ mNumberAppended = other.getNumberAppended();
+ }
+
// --------------- PagedList API ---------------
@Override
@@ -101,12 +124,46 @@ public class NullPaddedList<Type> extends AbstractList<Type> {
}
@Override
+ public void loadAround(int index) {
+ // do nothing - immutable, so no fetching will be done
+ }
+
+ @Override
public final int size() {
return getLoadedCount() + getLeadingNullCount() + getTrailingNullCount();
}
+ public boolean isImmutable() {
+ return true;
+ }
+
+ @Override
+ public PagedList<Type> snapshot() {
+ if (isImmutable()) {
+ return this;
+ }
+ return new NullPaddedList<>(this);
+ }
+
+ @Override
+ boolean isContiguous() {
+ return true;
+ }
+
+ @Override
+ public void addWeakCallback(@Nullable PagedList<Type> previousSnapshot,
+ @NonNull Callback callback) {
+ // no op, immutable
+ }
+
+ @Override
+ public void removeWeakCallback(Callback callback) {
+ // no op, immutable
+ }
+
// --------------- Contiguous API ---------------
+ @Override
public int getPositionOffset() {
return mPositionOffset;
}
@@ -137,4 +194,12 @@ public class NullPaddedList<Type> extends AbstractList<Type> {
public int getTrailingNullCount() {
return mTrailingNullCount;
}
+
+ int getNumberPrepended() {
+ return mNumberPrepended;
+ }
+
+ int getNumberAppended() {
+ return mNumberAppended;
+ }
}
diff --git a/android/arch/paging/Page.java b/android/arch/paging/Page.java
deleted file mode 100644
index e9890ed4..00000000
--- a/android/arch/paging/Page.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.List;
-
-/**
- * Immutable class representing a page of data loaded from a DataSource.
- * <p>
- * Optionally stores before/after keys for cases where they cannot be computed, but the DataSource
- * can provide them as part of loading a page.
- * <p>
- * A page's list must never be modified.
- */
-class Page<K, V> {
- @SuppressWarnings("WeakerAccess")
- @Nullable
- public final K beforeKey;
- @NonNull
- public final List<V> items;
- @SuppressWarnings("WeakerAccess")
- @Nullable
- public K afterKey;
-
- Page(@NonNull List<V> items) {
- this(null, items, null);
- }
-
- Page(@Nullable K beforeKey, @NonNull List<V> items, @Nullable K afterKey) {
- this.beforeKey = beforeKey;
- this.items = items;
- this.afterKey = afterKey;
- }
-}
diff --git a/android/arch/paging/PageArrayList.java b/android/arch/paging/PageArrayList.java
new file mode 100644
index 00000000..b90d055a
--- /dev/null
+++ b/android/arch/paging/PageArrayList.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.paging;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class PageArrayList<T> extends PagedList<T> {
+ // partial list of pages, doesn't include pages below the lowest accessed, or above the highest
+ final ArrayList<List<T>> mPages;
+
+ // to access page at index N, do mPages.get(N - mPageIndexOffset), but do bounds checking first!
+ int mPageIndexOffset;
+
+ final int mPageSize;
+ final int mCount;
+ final int mMaxPageCount;
+
+ PageArrayList(int pageSize, int count) {
+ mPages = new ArrayList<>();
+ mPageSize = pageSize;
+ mCount = count;
+ mMaxPageCount = (mCount + mPageSize - 1) / mPageSize;
+ }
+
+ private PageArrayList(PageArrayList<T> other) {
+ mPages = other.isImmutable() ? other.mPages : new ArrayList<>(other.mPages);
+ mPageIndexOffset = other.mPageIndexOffset;
+ mPageSize = other.mPageSize;
+ mCount = other.size();
+ mMaxPageCount = other.mMaxPageCount;
+ }
+
+ @Override
+ public T get(int index) {
+ if (index < 0 || index >= mCount) {
+ throw new IllegalArgumentException();
+ }
+
+ int localPageIndex = getLocalPageIndex(index);
+
+ List<T> page = getPage(localPageIndex);
+
+ if (page == null) {
+ // page empty
+ return null;
+ }
+
+ return page.get(index % mPageSize);
+ }
+
+ @Nullable
+ private List<T> getPage(int localPageIndex) {
+ if (localPageIndex < 0 || localPageIndex >= mPages.size()) {
+ // page not present
+ return null;
+ }
+
+ return mPages.get(localPageIndex);
+ }
+
+ private int getLocalPageIndex(int index) {
+ return index / mPageSize - mPageIndexOffset;
+ }
+
+ @Override
+ public void loadAround(int index) {
+ // do nothing - immutable, so no fetching will be done
+ }
+
+ @Override
+ public int size() {
+ return mCount;
+ }
+
+ @Override
+ public boolean isImmutable() {
+ return true;
+ }
+
+ boolean hasPage(int pageIndex) {
+ final int localPageIndex = pageIndex - mPageIndexOffset;
+ List<T> page = getPage(localPageIndex);
+ return page != null && page.size() != 0;
+ }
+
+ @Override
+ public PagedList<T> snapshot() {
+ if (isImmutable()) {
+ return this;
+ }
+ return new PageArrayList<>(this);
+ }
+
+ @Override
+ boolean isContiguous() {
+ return false;
+ }
+
+ @Override
+ public void addWeakCallback(@Nullable PagedList<T> previousSnapshot,
+ @NonNull Callback callback) {
+ // no op, immutable
+ }
+
+ @Override
+ public void removeWeakCallback(Callback callback) {
+ // no op, immutable
+ }
+}
diff --git a/android/arch/paging/PageArrayListTest.java b/android/arch/paging/PageArrayListTest.java
new file mode 100644
index 00000000..135e640d
--- /dev/null
+++ b/android/arch/paging/PageArrayListTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.paging;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class PageArrayListTest {
+ @Test
+ public void simple() {
+ List<String> data = Arrays.asList("A", "B", "C", "D", "E", "F");
+ PageArrayList<String> list = new PageArrayList<>(2, data.size());
+
+ assertEquals(2, list.mPageSize);
+ assertEquals(data.size(), list.size());
+ assertEquals(3, list.mMaxPageCount);
+
+ for (int i = 0; i < data.size(); i++) {
+ assertEquals(null, list.get(i));
+ }
+ for (int i = 0; i < data.size(); i += list.mPageSize) {
+ list.mPages.add(data.subList(i, i + 2));
+ }
+ for (int i = 0; i < data.size(); i++) {
+ assertEquals(data.get(i), list.get(i));
+ }
+ }
+}
diff --git a/android/arch/paging/PageResult.java b/android/arch/paging/PageResult.java
deleted file mode 100644
index a4090f61..00000000
--- a/android/arch/paging/PageResult.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.AnyThread;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-
-class PageResult<K, V> {
- static final int INIT = 0;
-
- // contiguous results
- static final int APPEND = 1;
- static final int PREPEND = 2;
-
- // non-contiguous, tile result
- static final int TILE = 3;
-
- public final int type;
- public final Page<K, V> page;
- @SuppressWarnings("WeakerAccess")
- public final int leadingNulls;
- @SuppressWarnings("WeakerAccess")
- public final int trailingNulls;
- @SuppressWarnings("WeakerAccess")
- public final int positionOffset;
-
- PageResult(int type, Page<K, V> page, int leadingNulls, int trailingNulls, int positionOffset) {
- this.type = type;
- this.page = page;
- this.leadingNulls = leadingNulls;
- this.trailingNulls = trailingNulls;
- this.positionOffset = positionOffset;
- }
-
- interface Receiver<K, V> {
- @AnyThread
- void postOnPageResult(@NonNull PageResult<K, V> pageResult);
- @MainThread
- void onPageResult(@NonNull PageResult<K, V> pageResult);
- }
-}
diff --git a/android/arch/paging/PagedList.java b/android/arch/paging/PagedList.java
index 1f07bfab..6a31b689 100644
--- a/android/arch/paging/PagedList.java
+++ b/android/arch/paging/PagedList.java
@@ -20,12 +20,9 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
-import java.lang.ref.WeakReference;
import java.util.AbstractList;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Lazy loading list that pages in content from a {@link DataSource}.
@@ -93,28 +90,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @param <T> The type of the entries in the list.
*/
public abstract class PagedList<T> extends AbstractList<T> {
- final Executor mMainThreadExecutor;
- final Executor mBackgroundThreadExecutor;
- final Config mConfig;
-
- @NonNull
- final PagedStorage<?, T> mStorage;
-
- int mLastLoad = 0;
- T mLastItem = null;
-
- private final AtomicBoolean mDetached = new AtomicBoolean(false);
-
- protected final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
-
- PagedList(@NonNull PagedStorage<?, T> storage,
- @NonNull Executor mainThreadExecutor,
- @NonNull Executor backgroundThreadExecutor,
- @NonNull Config config) {
- mStorage = storage;
- mMainThreadExecutor = mainThreadExecutor;
- mBackgroundThreadExecutor = backgroundThreadExecutor;
- mConfig = config;
+ // Since we currently rely on implementation details of two implementations,
+ // prevent external subclassing
+ PagedList() {
}
/**
@@ -302,13 +280,7 @@ public abstract class PagedList<T> extends AbstractList<T> {
*/
@Override
@Nullable
- public T get(int index) {
- T item = mStorage.get(index);
- if (item != null) {
- mLastItem = item;
- }
- return item;
- }
+ public abstract T get(int index);
/**
@@ -316,10 +288,7 @@ public abstract class PagedList<T> extends AbstractList<T> {
*
* @param index Index at which to load.
*/
- public void loadAround(int index) {
- mLastLoad = index + getPositionOffset();
- loadAroundInternal(index);
- }
+ public abstract void loadAround(int index);
/**
@@ -328,9 +297,7 @@ public abstract class PagedList<T> extends AbstractList<T> {
* @return Current total size of the list.
*/
@Override
- public int size() {
- return mStorage.size();
- }
+ public abstract int size();
/**
* Returns whether the list is immutable. Immutable lists may not become mutable again, and may
@@ -338,25 +305,15 @@ public abstract class PagedList<T> extends AbstractList<T> {
*
* @return True if the PagedList is immutable.
*/
- @SuppressWarnings("WeakerAccess")
- public boolean isImmutable() {
- return isDetached();
- }
+ public abstract boolean isImmutable();
/**
* Returns an immutable snapshot of the PagedList. If this PagedList is already
* immutable, it will be returned.
*
- * @return Immutable snapshot of PagedList data.
+ * @return Immutable snapshot of PagedList, which may be the PagedList itself.
*/
- @NonNull
- public List<T> snapshot() {
- if (isImmutable()) {
- return this;
- }
-
- return new SnapshotPagedList<>(this);
- }
+ public abstract List<T> snapshot();
abstract boolean isContiguous();
@@ -371,7 +328,9 @@ public abstract class PagedList<T> extends AbstractList<T> {
* @return Key of position most recently passed to {@link #loadAround(int)}.
*/
@Nullable
- public abstract Object getLastKey();
+ public Object getLastKey() {
+ return null;
+ }
/**
* True if the PagedList has detached the DataSource it was loading from, and will no longer
@@ -379,9 +338,8 @@ public abstract class PagedList<T> extends AbstractList<T> {
*
* @return True if the data source is detached.
*/
- @SuppressWarnings("WeakerAccess")
public boolean isDetached() {
- return mDetached.get();
+ return true;
}
/**
@@ -391,9 +349,7 @@ public abstract class PagedList<T> extends AbstractList<T> {
* signal to stop loading. The PagedList will continue to present existing data, but will not
* initiate new loads.
*/
- @SuppressWarnings("WeakerAccess")
public void detach() {
- mDetached.set(true);
}
/**
@@ -405,7 +361,7 @@ public abstract class PagedList<T> extends AbstractList<T> {
* If the DataSource is a {@link KeyedDataSource}, and thus doesn't use positions, returns 0.
*/
public int getPositionOffset() {
- return mStorage.getPositionOffset();
+ return 0;
}
/**
@@ -429,68 +385,16 @@ public abstract class PagedList<T> extends AbstractList<T> {
* @param callback Callback to dispatch to.
* @see #removeWeakCallback(Callback)
*/
- @SuppressWarnings("WeakerAccess")
- public void addWeakCallback(@Nullable List<T> previousSnapshot, @NonNull Callback callback) {
- if (previousSnapshot != null && previousSnapshot != this) {
- PagedList<T> storageSnapshot = (PagedList<T>) previousSnapshot;
- //noinspection unchecked
- dispatchUpdatesSinceSnapshot(storageSnapshot, callback);
- }
-
- // first, clean up any empty weak refs
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback currentCallback = mCallbacks.get(i).get();
- if (currentCallback == null) {
- mCallbacks.remove(i);
- }
- }
+ public abstract void addWeakCallback(@Nullable PagedList<T> previousSnapshot,
+ @NonNull Callback callback);
- // then add the new one
- mCallbacks.add(new WeakReference<>(callback));
- }
/**
* Removes a previously added callback.
*
* @param callback Callback, previously added.
- * @see #addWeakCallback(List, Callback)
+ * @see #addWeakCallback(PagedList, Callback)
*/
- @SuppressWarnings("WeakerAccess")
- public void removeWeakCallback(@NonNull Callback callback) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback currentCallback = mCallbacks.get(i).get();
- if (currentCallback == null || currentCallback == callback) {
- // found callback, or empty weak ref
- mCallbacks.remove(i);
- }
- }
- }
-
- void notifyInserted(int position, int count) {
- if (count != 0) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback callback = mCallbacks.get(i).get();
- if (callback != null) {
- callback.onInserted(position, count);
- }
- }
- }
- }
-
- void notifyChanged(int position, int count) {
- if (count != 0) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback callback = mCallbacks.get(i).get();
- if (callback != null) {
- callback.onChanged(position, count);
- }
- }
- }
- }
-
- abstract void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> snapshot,
- @NonNull Callback callback);
-
- abstract void loadAroundInternal(int index);
+ public abstract void removeWeakCallback(Callback callback);
/**
* Callback signaling when content is loaded into the list.
@@ -641,15 +545,10 @@ public abstract class PagedList<T> extends AbstractList<T> {
* Defines how many items to load when first load occurs, if you are using a
* {@link KeyedDataSource}.
* <p>
- * This value is typically larger than page size, so on first load data there's a large
- * enough range of content loaded to cover small scrolls.
- * <p>
- * If used with a {@link TiledDataSource}, this value is rounded to the nearest number
- * of pages, with a minimum of two pages, and loaded with a single call to
- * {@link TiledDataSource#loadRange(int, int)}.
- * <p>
- * If used with a {@link KeyedDataSource}, this value will be passed to
- * {@link KeyedDataSource#loadInitial(int)}.
+ * If you are using an {@link TiledDataSource}, this value is currently ignored.
+ * Otherwise, this value will be passed to
+ * {@link KeyedDataSource#loadInitial(int)} to load a (typically) larger amount
+ * of data on first load.
* <p>
* If not set, defaults to three times page size.
*
diff --git a/android/arch/paging/PagedListAdapterHelper.java b/android/arch/paging/PagedListAdapterHelper.java
index abcff415..c7b61d9f 100644
--- a/android/arch/paging/PagedListAdapterHelper.java
+++ b/android/arch/paging/PagedListAdapterHelper.java
@@ -25,6 +25,8 @@ import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
import android.support.v7.widget.RecyclerView;
+import java.util.List;
+
/**
* Helper object for mapping a {@link PagedList} into a
* {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}.
@@ -118,15 +120,15 @@ import android.support.v7.widget.RecyclerView;
* @param <T> Type of the PagedLists this helper will receive.
*/
public class PagedListAdapterHelper<T> {
- // updateCallback notifications must only be notified *after* new data and item count are stored
- // this ensures Adapter#notifyItemRangeInserted etc are accessing the new data
private final ListUpdateCallback mUpdateCallback;
private final ListAdapterConfig<T> mConfig;
+ // true if our listener is detached from mList, because it's been snapshotted
+ private boolean mUpdateScheduled;
+
private boolean mIsContiguous;
- private PagedList<T> mPagedList;
- private PagedList<T> mSnapshot;
+ private PagedList<T> mList;
// Max generation of currently scheduled runnable
private int mMaxScheduledGeneration;
@@ -180,17 +182,12 @@ public class PagedListAdapterHelper<T> {
@SuppressWarnings("WeakerAccess")
@Nullable
public T getItem(int index) {
- if (mPagedList == null) {
- if (mSnapshot == null) {
- throw new IndexOutOfBoundsException(
- "Item count is zero, getItem() call is invalid");
- } else {
- return mSnapshot.get(index);
- }
+ if (mList == null) {
+ throw new IndexOutOfBoundsException("Item count is zero, getItem() call is invalid");
}
- mPagedList.loadAround(index);
- return mPagedList.get(index);
+ mList.loadAround(index);
+ return mList.get(index);
}
/**
@@ -201,11 +198,7 @@ public class PagedListAdapterHelper<T> {
*/
@SuppressWarnings("WeakerAccess")
public int getItemCount() {
- if (mPagedList != null) {
- return mPagedList.size();
- }
-
- return mSnapshot == null ? 0 : mSnapshot.size();
+ return mList == null ? 0 : mList.size();
}
/**
@@ -219,7 +212,7 @@ public class PagedListAdapterHelper<T> {
*/
public void setList(final PagedList<T> pagedList) {
if (pagedList != null) {
- if (mPagedList == null && mSnapshot == null) {
+ if (mList == null) {
mIsContiguous = pagedList.isContiguous();
} else {
if (pagedList.isContiguous() != mIsContiguous) {
@@ -229,7 +222,7 @@ public class PagedListAdapterHelper<T> {
}
}
- if (pagedList == mPagedList) {
+ if (pagedList == mList) {
// nothing to do
return;
}
@@ -238,55 +231,49 @@ public class PagedListAdapterHelper<T> {
final int runGeneration = ++mMaxScheduledGeneration;
if (pagedList == null) {
- int removedCount = getItemCount();
- if (mPagedList != null) {
- mPagedList.removeWeakCallback(mPagedListCallback);
- mPagedList = null;
- } else if (mSnapshot != null) {
- mSnapshot = null;
- }
- // dispatch update callback after updating mPagedList/mSnapshot
- mUpdateCallback.onRemoved(0, removedCount);
+ mUpdateCallback.onRemoved(0, mList.size());
+ mList.removeWeakCallback(mPagedListCallback);
+ mList = null;
return;
}
- if (mPagedList == null && mSnapshot == null) {
+ if (mList == null) {
// fast simple first insert
- mPagedList = pagedList;
- pagedList.addWeakCallback(null, mPagedListCallback);
-
- // dispatch update callback after updating mPagedList/mSnapshot
mUpdateCallback.onInserted(0, pagedList.size());
+ mList = pagedList;
+ pagedList.addWeakCallback(null, mPagedListCallback);
return;
}
- if (mPagedList != null) {
+ if (!mList.isImmutable()) {
// first update scheduled on this list, so capture mPages as a snapshot, removing
// callbacks so we don't have resolve updates against a moving target
- mPagedList.removeWeakCallback(mPagedListCallback);
- mSnapshot = (PagedList<T>) mPagedList.snapshot();
- mPagedList = null;
- }
-
- if (mSnapshot == null || mPagedList != null) {
- throw new IllegalStateException("must be in snapshot state to diff");
+ mList.removeWeakCallback(mPagedListCallback);
+ mList = (PagedList<T>) mList.snapshot();
}
- final PagedList<T> oldSnapshot = mSnapshot;
- final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
+ final PagedList<T> oldSnapshot = mList;
+ final List<T> newSnapshot = pagedList.snapshot();
+ mUpdateScheduled = true;
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
@Override
public void run() {
final DiffUtil.DiffResult result;
- result = PagedStorageDiffHelper.computeDiff(
- oldSnapshot.mStorage,
- newSnapshot.mStorage,
- mConfig.getDiffCallback());
+ if (mIsContiguous) {
+ result = ContiguousDiffHelper.computeDiff(
+ (NullPaddedList<T>) oldSnapshot, (NullPaddedList<T>) newSnapshot,
+ mConfig.getDiffCallback(), true);
+ } else {
+ result = SparseDiffHelper.computeDiff(
+ (PageArrayList<T>) oldSnapshot, (PageArrayList<T>) newSnapshot,
+ mConfig.getDiffCallback(), true);
+ }
mConfig.getMainThreadExecutor().execute(new Runnable() {
@Override
public void run() {
if (mMaxScheduledGeneration == runGeneration) {
+ mUpdateScheduled = false;
latchPagedList(pagedList, newSnapshot, result);
}
}
@@ -296,21 +283,16 @@ public class PagedListAdapterHelper<T> {
}
private void latchPagedList(
- PagedList<T> newList, PagedList<T> diffSnapshot,
+ PagedList<T> newList, List<T> diffSnapshot,
DiffUtil.DiffResult diffResult) {
- if (mSnapshot == null || mPagedList != null) {
- throw new IllegalStateException("must be in snapshot state to apply diff");
+ if (mIsContiguous) {
+ ContiguousDiffHelper.dispatchDiff(mUpdateCallback,
+ (NullPaddedList<T>) mList, (ContiguousPagedList<T>) newList, diffResult);
+ } else {
+ SparseDiffHelper.dispatchDiff(mUpdateCallback, diffResult);
}
-
- PagedList<T> previousSnapshot = mSnapshot;
- mPagedList = newList;
- mSnapshot = null;
-
- // dispatch update callback after updating mPagedList/mSnapshot
- PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
- previousSnapshot.mStorage, newList.mStorage, diffResult);
-
- newList.addWeakCallback(diffSnapshot, mPagedListCallback);
+ mList = newList;
+ newList.addWeakCallback((PagedList<T>) diffSnapshot, mPagedListCallback);
}
/**
@@ -325,9 +307,6 @@ public class PagedListAdapterHelper<T> {
@SuppressWarnings("WeakerAccess")
@Nullable
public PagedList<T> getCurrentList() {
- if (mSnapshot != null) {
- return mSnapshot;
- }
- return mPagedList;
+ return mList;
}
}
diff --git a/android/arch/paging/PagedListAdapterHelperTest.java b/android/arch/paging/PagedListAdapterHelperTest.java
index 963d0479..3518540c 100644
--- a/android/arch/paging/PagedListAdapterHelperTest.java
+++ b/android/arch/paging/PagedListAdapterHelperTest.java
@@ -21,7 +21,6 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -290,64 +289,6 @@ public class PagedListAdapterHelperTest {
assertFalse(helper.getCurrentList().isImmutable());
}
- @Test
- public void itemCountUpdatedBeforeListUpdateCallbacks() {
- // verify that itemCount is updated in the helper before dispatching ListUpdateCallbacks
-
- final int[] expectedCount = new int[] { 0 };
- // provides access to helper, which must be constructed after callback
- final PagedListAdapterHelper[] helperAccessor = new PagedListAdapterHelper[] { null };
-
- ListUpdateCallback callback = new ListUpdateCallback() {
- @Override
- public void onInserted(int position, int count) {
- assertEquals(expectedCount[0], helperAccessor[0].getItemCount());
- }
-
- @Override
- public void onRemoved(int position, int count) {
- assertEquals(expectedCount[0], helperAccessor[0].getItemCount());
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- fail("not expected");
- }
-
- @Override
- public void onChanged(int position, int count, Object payload) {
- fail("not expected");
- }
- };
-
- PagedListAdapterHelper<String> helper = createHelper(callback, STRING_DIFF_CALLBACK);
- helperAccessor[0] = helper;
-
- PagedList.Config config = new PagedList.Config.Builder()
- .setPageSize(20)
- .build();
-
-
- // in the fast-add case...
- expectedCount[0] = 5;
- assertEquals(0, helper.getItemCount());
- helper.setList(createPagedListFromListAndPos(config, ALPHABET_LIST.subList(0, 5), 0));
- assertEquals(5, helper.getItemCount());
-
- // in the slow, diff on BG thread case...
- expectedCount[0] = 10;
- assertEquals(5, helper.getItemCount());
- helper.setList(createPagedListFromListAndPos(config, ALPHABET_LIST.subList(0, 10), 0));
- drain();
- assertEquals(10, helper.getItemCount());
-
- // and in the fast-remove case
- expectedCount[0] = 0;
- assertEquals(10, helper.getItemCount());
- helper.setList(null);
- assertEquals(0, helper.getItemCount());
- }
-
private void drainExceptDiffThread() {
boolean executed;
do {
diff --git a/android/arch/paging/PagedStorage.java b/android/arch/paging/PagedStorage.java
deleted file mode 100644
index 7f91290d..00000000
--- a/android/arch/paging/PagedStorage.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.NonNull;
-
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-final class PagedStorage<K, V> extends AbstractList<V> {
- // Always set
- private int mLeadingNullCount;
- /**
- * List of pages in storage.
- *
- * Two storage modes:
- *
- * Contiguous - all content in mPages is valid and loaded, but may return false from isTiled().
- * Safe to access any item in any page.
- *
- * Non-contiguous - mPages may have nulls or a placeholder page, isTiled() always returns true.
- * mPages may have nulls, or placeholder (empty) pages while content is loading.
- */
- private final ArrayList<Page<K, V>> mPages;
- private int mTrailingNullCount;
-
- private int mPositionOffset;
- /**
- * Number of items represented by {@link #mPages}. If tiling is enabled, unloaded items in
- * {@link #mPages} may be null, but this value still counts them.
- */
- private int mStorageCount;
-
- // If mPageSize > 0, tiling is enabled, 'mPages' may have gaps, and leadingPages is set
- private int mPageSize;
-
- private int mNumberPrepended;
- private int mNumberAppended;
-
- // only used in tiling case
- private Page<K, V> mPlaceholderPage;
-
- PagedStorage() {
- mLeadingNullCount = 0;
- mPages = new ArrayList<>();
- mTrailingNullCount = 0;
- mPositionOffset = 0;
- mStorageCount = 0;
- mPageSize = 1;
- mNumberPrepended = 0;
- mNumberAppended = 0;
- }
-
- PagedStorage(int leadingNulls, Page<K, V> page, int trailingNulls) {
- this();
- init(leadingNulls, page, trailingNulls, 0);
- }
-
- private PagedStorage(PagedStorage<K, V> other) {
- mLeadingNullCount = other.mLeadingNullCount;
- mPages = new ArrayList<>(other.mPages);
- mTrailingNullCount = other.mTrailingNullCount;
- mPositionOffset = other.mPositionOffset;
- mStorageCount = other.mStorageCount;
- mPageSize = other.mPageSize;
- mNumberPrepended = other.mNumberPrepended;
- mNumberAppended = other.mNumberAppended;
-
- // preserve placeholder page so we can locate placeholder pages if needed later
- mPlaceholderPage = other.mPlaceholderPage;
- }
-
- PagedStorage<K, V> snapshot() {
- return new PagedStorage<>(this);
- }
-
- private void init(int leadingNulls, Page<K, V> page, int trailingNulls, int positionOffset) {
- mLeadingNullCount = leadingNulls;
- mPages.clear();
- mPages.add(page);
- mTrailingNullCount = trailingNulls;
-
- mPositionOffset = positionOffset;
- mStorageCount = page.items.size();
-
- // initialized as tiled. There may be 3 nulls, 2 items, but we still call this tiled
- // even if it will break if nulls convert.
- mPageSize = page.items.size();
-
- mNumberPrepended = 0;
- mNumberAppended = 0;
- }
-
- void init(int leadingNulls, Page<K, V> page, int trailingNulls, int positionOffset,
- @NonNull Callback callback) {
- init(leadingNulls, page, trailingNulls, positionOffset);
- callback.onInitialized(size());
- }
-
- @Override
- public V get(int i) {
- if (i < 0 || i >= size()) {
- throw new IndexOutOfBoundsException("Index: " + i + ", Size: " + size());
- }
-
- // is it definitely outside 'mPages'?
- int localIndex = i - mLeadingNullCount;
- if (localIndex < 0 || localIndex >= mStorageCount) {
- return null;
- }
-
- int localPageIndex;
- int pageInternalIndex;
-
- if (isTiled()) {
- // it's inside mPages, and we're tiled. Jump to correct tile.
- localPageIndex = localIndex / mPageSize;
- pageInternalIndex = localIndex % mPageSize;
- } else {
- // it's inside mPages, but page sizes aren't regular. Walk to correct tile.
- // Pages can only be null while tiled, so accessing page count is safe.
- pageInternalIndex = localIndex;
- final int localPageCount = mPages.size();
- for (localPageIndex = 0; localPageIndex < localPageCount; localPageIndex++) {
- int pageSize = mPages.get(localPageIndex).items.size();
- if (pageSize > pageInternalIndex) {
- // stop, found the page
- break;
- }
- pageInternalIndex -= pageSize;
- }
- }
-
- Page<?, V> page = mPages.get(localPageIndex);
- if (page == null || page.items.size() == 0) {
- // can only occur in tiled case, with untouched inner/placeholder pages
- return null;
- }
- return page.items.get(pageInternalIndex);
- }
-
- /**
- * Returns true if all pages are the same size, except for the last, which may be smaller
- */
- boolean isTiled() {
- return mPageSize > 0;
- }
-
- int getLeadingNullCount() {
- return mLeadingNullCount;
- }
-
- int getTrailingNullCount() {
- return mTrailingNullCount;
- }
-
- int getStorageCount() {
- return mStorageCount;
- }
-
- int getNumberAppended() {
- return mNumberAppended;
- }
-
- int getNumberPrepended() {
- return mNumberPrepended;
- }
-
- int getPageCount() {
- return mPages.size();
- }
-
- interface Callback {
- void onInitialized(int count);
- void onPagePrepended(int leadingNulls, int changed, int added);
- void onPageAppended(int endPosition, int changed, int added);
- void onPagePlaceholderInserted(int pageIndex);
- void onPageInserted(int start, int count);
- }
-
- int getPositionOffset() {
- return mPositionOffset;
- }
-
- @Override
- public int size() {
- return mLeadingNullCount + mStorageCount + mTrailingNullCount;
- }
-
- int computeLeadingNulls() {
- int total = mLeadingNullCount;
- final int pageCount = mPages.size();
- for (int i = 0; i < pageCount; i++) {
- Page page = mPages.get(i);
- if (page != null && page != mPlaceholderPage) {
- break;
- }
- total += mPageSize;
- }
- return total;
- }
-
- int computeTrailingNulls() {
- int total = mTrailingNullCount;
- for (int i = mPages.size() - 1; i >= 0; i--) {
- Page page = mPages.get(i);
- if (page != null && page != mPlaceholderPage) {
- break;
- }
- total += mPageSize;
- }
- return total;
- }
-
- // ---------------- Contiguous API -------------------
-
- V getFirstContiguousItem() {
- // safe to access first page's first item here:
- // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
- return mPages.get(0).items.get(0);
- }
-
- V getLastContiguousItem() {
- // safe to access last page's last item here:
- // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
- Page<K, V> page = mPages.get(mPages.size() - 1);
- return page.items.get(page.items.size() - 1);
- }
-
- public void prependPage(@NonNull Page<K, V> page, @NonNull Callback callback) {
- final int count = page.items.size();
- if (count == 0) {
- // Nothing returned from source, stop loading in this direction
- return;
- }
- if (mPageSize > 0 && count != mPageSize) {
- if (mPages.size() == 1 && count > mPageSize) {
- // prepending to a single item - update current page size to that of 'inner' page
- mPageSize = count;
- } else {
- // no longer tiled
- mPageSize = -1;
- }
- }
-
- mPages.add(0, page);
- mStorageCount += count;
-
- final int changedCount = Math.min(mLeadingNullCount, count);
- final int addedCount = count - changedCount;
-
- if (changedCount != 0) {
- mLeadingNullCount -= changedCount;
- }
- mPositionOffset -= addedCount;
- mNumberPrepended += count;
-
- callback.onPagePrepended(mLeadingNullCount, changedCount, addedCount);
- }
-
- public void appendPage(@NonNull Page<K, V> page, @NonNull Callback callback) {
- final int count = page.items.size();
- if (count == 0) {
- // Nothing returned from source, stop loading in this direction
- return;
- }
-
- if (mPageSize > 0) {
- // if the previous page was smaller than mPageSize,
- // or if this page is larger than the previous, disable tiling
- if (mPages.get(mPages.size() - 1).items.size() != mPageSize
- || count > mPageSize) {
- mPageSize = -1;
- }
- }
-
- mPages.add(page);
- mStorageCount += count;
-
- final int changedCount = Math.min(mTrailingNullCount, count);
- final int addedCount = count - changedCount;
-
- if (changedCount != 0) {
- mTrailingNullCount -= changedCount;
- }
- mNumberAppended += count;
- callback.onPageAppended(mLeadingNullCount + mStorageCount - count,
- changedCount, addedCount);
- }
-
- // ------------------ Non-Contiguous API (tiling required) ----------------------
-
- public void insertPage(int position, @NonNull Page<K, V> page, Callback callback) {
- final int newPageSize = page.items.size();
- if (newPageSize != mPageSize) {
- // differing page size is OK in 2 cases, when the page is being added:
- // 1) to the end (in which case, ignore new smaller size)
- // 2) only the last page has been added so far (in which case, adopt new bigger size)
-
- int size = size();
- boolean addingLastPage = position == (size - size % mPageSize)
- && newPageSize < mPageSize;
- boolean onlyEndPagePresent = mTrailingNullCount == 0 && mPages.size() == 1
- && newPageSize > mPageSize;
-
- // OK only if existing single page, and it's the last one
- if (!onlyEndPagePresent && !addingLastPage) {
- throw new IllegalArgumentException("page introduces incorrect tiling");
- }
- if (onlyEndPagePresent) {
- mPageSize = newPageSize;
- }
- }
-
- int pageIndex = position / mPageSize;
-
- allocatePageRange(pageIndex, pageIndex);
-
- int localPageIndex = pageIndex - mLeadingNullCount / mPageSize;
-
- Page<K, V> oldPage = mPages.get(localPageIndex);
- if (oldPage != null && oldPage != mPlaceholderPage) {
- throw new IllegalArgumentException(
- "Invalid position " + position + ": data already loaded");
- }
- mPages.set(localPageIndex, page);
- callback.onPageInserted(position, page.items.size());
- }
-
- private Page<K, V> getPlaceholderPage() {
- if (mPlaceholderPage == null) {
- @SuppressWarnings("unchecked")
- List<V> list = Collections.emptyList();
- mPlaceholderPage = new Page<>(null, list, null);
- }
- return mPlaceholderPage;
- }
-
- private void allocatePageRange(final int minimumPage, final int maximumPage) {
- int leadingNullPages = mLeadingNullCount / mPageSize;
-
- if (minimumPage < leadingNullPages) {
- for (int i = 0; i < leadingNullPages - minimumPage; i++) {
- mPages.add(0, null);
- }
- int newStorageAllocated = (leadingNullPages - minimumPage) * mPageSize;
- mStorageCount += newStorageAllocated;
- mLeadingNullCount -= newStorageAllocated;
-
- leadingNullPages = minimumPage;
- }
- if (maximumPage >= leadingNullPages + mPages.size()) {
- int newStorageAllocated = Math.min(mTrailingNullCount,
- (maximumPage + 1 - (leadingNullPages + mPages.size())) * mPageSize);
- for (int i = mPages.size(); i <= maximumPage - leadingNullPages; i++) {
- mPages.add(mPages.size(), null);
- }
- mStorageCount += newStorageAllocated;
- mTrailingNullCount -= newStorageAllocated;
- }
- }
-
- public void allocatePlaceholders(int index, int prefetchDistance,
- int pageSize, Callback callback) {
- if (pageSize != mPageSize) {
- if (pageSize < mPageSize) {
- throw new IllegalArgumentException("Page size cannot be reduced");
- }
- if (mPages.size() != 1 || mTrailingNullCount != 0) {
- // not in single, last page allocated case - can't change page size
- throw new IllegalArgumentException(
- "Page size can change only if last page is only one present");
- }
- mPageSize = pageSize;
- }
-
- final int maxPageCount = (size() + mPageSize - 1) / mPageSize;
- int minimumPage = Math.max((index - prefetchDistance) / mPageSize, 0);
- int maximumPage = Math.min((index + prefetchDistance) / mPageSize, maxPageCount - 1);
-
- allocatePageRange(minimumPage, maximumPage);
- int leadingNullPages = mLeadingNullCount / mPageSize;
- for (int pageIndex = minimumPage; pageIndex <= maximumPage; pageIndex++) {
- int localPageIndex = pageIndex - leadingNullPages;
- if (mPages.get(localPageIndex) == null) {
- mPages.set(localPageIndex, getPlaceholderPage());
- callback.onPagePlaceholderInserted(pageIndex);
- }
- }
- }
-
- public boolean hasPage(int pageSize, int index) {
- // NOTE: we pass pageSize here to avoid in case mPageSize
- // not fully initialized (when last page only one loaded)
- int leadingNullPages = mLeadingNullCount / pageSize;
-
- if (index < leadingNullPages || index >= leadingNullPages + mPages.size()) {
- return false;
- }
-
- Page<K, V> page = mPages.get(index - leadingNullPages);
-
- return page != null && page != mPlaceholderPage;
- }
-
- @Override
- public String toString() {
- StringBuilder ret = new StringBuilder("leading " + mLeadingNullCount
- + ", storage " + mStorageCount
- + ", trailing " + getTrailingNullCount());
-
- for (int i = 0; i < mPages.size(); i++) {
- ret.append(" ").append(mPages.get(i));
- }
- return ret.toString();
- }
-}
diff --git a/android/arch/paging/PositionalDataSource.java b/android/arch/paging/PositionalDataSource.java
index c538cb60..deb51e94 100644
--- a/android/arch/paging/PositionalDataSource.java
+++ b/android/arch/paging/PositionalDataSource.java
@@ -42,17 +42,6 @@ import java.util.List;
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public abstract class PositionalDataSource<Value> extends ContiguousDataSource<Integer, Value> {
-
- /**
- * Number of items that this DataSource can provide in total, or COUNT_UNDEFINED.
- *
- * @return number of items that this DataSource can provide in total, or COUNT_UNDEFINED
- * if difficult or undesired to compute.
- */
- public int countItems() {
- return COUNT_UNDEFINED;
- }
-
@Nullable
@Override
List<Value> loadAfterImpl(int currentEndIndex, @NonNull Value currentEndItem, int pageSize) {
@@ -66,7 +55,16 @@ public abstract class PositionalDataSource<Value> extends ContiguousDataSource<I
return loadBefore(currentBeginIndex - 1, pageSize);
}
- /** @hide */
+
+ /**
+ * Load initial data, starting after the passed position.
+ *
+ * @param position Index just before the data to be loaded.
+ * @param initialLoadSize Suggested number of items to load.
+ * @return List of initial items, representing data starting at position. Null if the
+ * DataSource is no longer valid, and should not be queried again.
+ * @hide
+ */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@WorkerThread
@Nullable
@@ -120,9 +118,6 @@ public abstract class PositionalDataSource<Value> extends ContiguousDataSource<I
@Override
Integer getKey(int position, Value item) {
- if (position < 0) {
- return null;
- }
return position;
}
}
diff --git a/android/arch/paging/SnapshotPagedList.java b/android/arch/paging/SnapshotPagedList.java
deleted file mode 100644
index 7e965a0f..00000000
--- a/android/arch/paging/SnapshotPagedList.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-class SnapshotPagedList<T> extends PagedList<T> {
- private final boolean mContiguous;
- private final Object mLastKey;
-
- SnapshotPagedList(@NonNull PagedList<T> pagedList) {
- super(pagedList.mStorage.snapshot(),
- pagedList.mMainThreadExecutor,
- pagedList.mBackgroundThreadExecutor,
- pagedList.mConfig);
- mContiguous = pagedList.isContiguous();
- mLastKey = pagedList.getLastKey();
- }
-
- @Override
- public boolean isImmutable() {
- return true;
- }
-
- @Override
- public boolean isDetached() {
- return true;
- }
-
- @Override
- boolean isContiguous() {
- return mContiguous;
- }
-
- @Nullable
- @Override
- public Object getLastKey() {
- return mLastKey;
- }
-
- @Override
- void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> storageSnapshot,
- @NonNull Callback callback) {
- }
-
- @Override
- void loadAroundInternal(int index) {
- }
-}
diff --git a/android/arch/paging/SparseDiffHelper.java b/android/arch/paging/SparseDiffHelper.java
new file mode 100644
index 00000000..fe478973
--- /dev/null
+++ b/android/arch/paging/SparseDiffHelper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.paging;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v7.recyclerview.extensions.DiffCallback;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.util.ListUpdateCallback;
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class SparseDiffHelper {
+ private SparseDiffHelper() {
+ }
+
+ @NonNull
+ static <T> DiffUtil.DiffResult computeDiff(
+ final PageArrayList<T> oldList, final PageArrayList<T> newList,
+ final DiffCallback<T> diffCallback, boolean detectMoves) {
+
+ if (!oldList.isImmutable()) {
+ throw new IllegalArgumentException("list must be immutable to safely perform diff");
+ }
+ if (!newList.isImmutable()) {
+ throw new IllegalArgumentException("list must be immutable to safely perform diff");
+ }
+ return DiffUtil.calculateDiff(new DiffUtil.Callback() {
+ @Nullable
+ @Override
+ public Object getChangePayload(int oldItemPosition, int newItemPosition) {
+ T oldItem = oldList.get(oldItemPosition);
+ T newItem = newList.get(newItemPosition);
+ if (oldItem == null || newItem == null) {
+ return null;
+ }
+ return diffCallback.getChangePayload(oldItem, newItem);
+ }
+
+ @Override
+ public int getOldListSize() {
+ return oldList.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return newList.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ T oldItem = oldList.get(oldItemPosition);
+ T newItem = newList.get(newItemPosition);
+ if (oldItem == newItem) {
+ return true;
+ }
+ if (oldItem == null || newItem == null) {
+ return false;
+ }
+ return diffCallback.areItemsTheSame(oldItem, newItem);
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ T oldItem = oldList.get(oldItemPosition);
+ T newItem = newList.get(newItemPosition);
+ if (oldItem == newItem) {
+ return true;
+ }
+ if (oldItem == null || newItem == null) {
+ return false;
+ }
+
+ return diffCallback.areContentsTheSame(oldItem, newItem);
+ }
+ }, detectMoves);
+ }
+
+ static <T> void dispatchDiff(ListUpdateCallback callback,
+ final DiffUtil.DiffResult diffResult) {
+ // Simple case, dispatch & return
+ diffResult.dispatchUpdatesTo(callback);
+ }
+}
diff --git a/android/arch/paging/StringPagedList.java b/android/arch/paging/StringPagedList.java
index 880d5e9b..5318d38c 100644
--- a/android/arch/paging/StringPagedList.java
+++ b/android/arch/paging/StringPagedList.java
@@ -16,60 +16,10 @@
package android.arch.paging;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
import java.util.Arrays;
-public class StringPagedList extends PagedList<String> implements PagedStorage.Callback {
- StringPagedList(int leadingNulls, int trailingNulls, String... items) {
- super(new PagedStorage<Integer, String>(),
- null, null, null);
- PagedStorage<Integer, String> keyedStorage = (PagedStorage<Integer, String>) mStorage;
- keyedStorage.init(leadingNulls,
- new Page<Integer, String>(null, Arrays.asList(items), null),
- trailingNulls,
- 0,
- this);
- }
-
- @Override
- boolean isContiguous() {
- return true;
- }
-
- @Nullable
- @Override
- public Object getLastKey() {
- return null;
- }
-
- @Override
- protected void dispatchUpdatesSinceSnapshot(@NonNull PagedList<String> storageSnapshot,
- @NonNull Callback callback) {
- }
-
- @Override
- protected void loadAroundInternal(int index) {
- }
-
- @Override
- public void onInitialized(int count) {
- }
-
- @Override
- public void onPagePrepended(int leadingNulls, int changed, int added) {
- }
-
- @Override
- public void onPageAppended(int endPosition, int changed, int added) {
- }
-
- @Override
- public void onPagePlaceholderInserted(int pageIndex) {
- }
-
- @Override
- public void onPageInserted(int start, int count) {
+public class StringPagedList extends NullPaddedList<String> {
+ public StringPagedList(int leadingNulls, int trailingNulls, String... items) {
+ super(leadingNulls, Arrays.asList(items), trailingNulls);
}
}
diff --git a/android/arch/paging/TestExecutor.java b/android/arch/paging/TestExecutor.java
index 976f7df5..30809c3e 100644
--- a/android/arch/paging/TestExecutor.java
+++ b/android/arch/paging/TestExecutor.java
@@ -30,7 +30,7 @@ public class TestExecutor implements Executor {
mTasks.add(command);
}
- public boolean executeAll() {
+ boolean executeAll() {
boolean consumed = !mTasks.isEmpty();
Runnable task;
while ((task = mTasks.poll()) != null) {
diff --git a/android/arch/paging/TiledDataSource.java b/android/arch/paging/TiledDataSource.java
index 61dead3a..36be423d 100644
--- a/android/arch/paging/TiledDataSource.java
+++ b/android/arch/paging/TiledDataSource.java
@@ -19,7 +19,6 @@ package android.arch.paging;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
-import java.util.Collections;
import java.util.List;
/**
@@ -93,6 +92,7 @@ public abstract class TiledDataSource<Type> extends DataSource<Integer, Type> {
* @return Number of items this DataSource can provide. Must be <code>0</code> or greater.
*/
@WorkerThread
+ @Override
public abstract int countItems();
@Override
@@ -118,61 +118,7 @@ public abstract class TiledDataSource<Type> extends DataSource<Integer, Type> {
@WorkerThread
public abstract List<Type> loadRange(int startPosition, int count);
- /**
- * blocking, and splits pages
- */
- void loadRangeInitial(int startPosition, int count, int pageSize, int itemCount,
- PageResult.Receiver<Integer, Type> receiver) {
-
- if (itemCount == 0) {
- // no data to load, just immediately return empty
- receiver.onPageResult(new PageResult<>(
- PageResult.INIT, new Page<Integer, Type>(Collections.<Type>emptyList()),
- 0, 0, startPosition));
- return;
- }
-
-
- List<Type> list = loadRangeWrapper(startPosition, count);
-
- count = Math.min(count, itemCount - startPosition);
-
- if (list == null) {
- // invalid data, pass to receiver
- receiver.onPageResult(new PageResult<Integer, Type>(
- PageResult.INIT, null, 0, 0, startPosition));
- return;
- }
-
- if (list.size() != count) {
- throw new IllegalStateException("Invalid list, requested size: " + count
- + ", returned size: " + list.size());
- }
-
- // emit the results as multiple pages
- int pageCount = (count + (pageSize - 1)) / pageSize;
- for (int i = 0; i < pageCount; i++) {
- int beginInclusive = i * pageSize;
- int endExclusive = Math.min(count, (i + 1) * pageSize);
-
- Page<Integer, Type> page = new Page<>(list.subList(beginInclusive, endExclusive));
-
- int leadingNulls = startPosition + beginInclusive;
- int trailingNulls = itemCount - leadingNulls - page.items.size();
- receiver.onPageResult(new PageResult<>(
- PageResult.INIT, page, leadingNulls, trailingNulls, 0));
- }
- }
-
- void loadRange(int startPosition, int count, PageResult.Receiver<Integer, Type> receiver) {
- List<Type> list = loadRangeWrapper(startPosition, count);
-
- Page<Integer, Type> page = list != null ? new Page<Integer, Type>(list) : null;
- receiver.postOnPageResult(new PageResult<>(
- PageResult.TILE, page, startPosition, 0, 0));
- }
-
- private List<Type> loadRangeWrapper(int startPosition, int count) {
+ final List<Type> loadRangeWrapper(int startPosition, int count) {
if (isInvalid()) {
return null;
}
diff --git a/android/arch/paging/TiledPagedList.java b/android/arch/paging/TiledPagedList.java
index c45d029d..a2fc064b 100644
--- a/android/arch/paging/TiledPagedList.java
+++ b/android/arch/paging/TiledPagedList.java
@@ -16,173 +16,219 @@
package android.arch.paging;
-import android.support.annotation.AnyThread;
-import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.support.annotation.WorkerThread;
+import java.lang.ref.WeakReference;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
-class TiledPagedList<T> extends PagedList<T>
- implements PagedStorage.Callback {
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class TiledPagedList<T> extends PageArrayList<T> {
private final TiledDataSource<T> mDataSource;
+ private final Executor mMainThreadExecutor;
+ private final Executor mBackgroundThreadExecutor;
+ private final Config mConfig;
- @SuppressWarnings("unchecked")
- private final PagedStorage<Integer, T> mKeyedStorage = (PagedStorage<Integer, T>) mStorage;
-
- private final PageResult.Receiver<Integer, T> mReceiver =
- new PageResult.Receiver<Integer, T>() {
- @AnyThread
+ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+ private final List<T> mLoadingPlaceholder = new AbstractList<T>() {
@Override
- public void postOnPageResult(@NonNull final PageResult<Integer, T> pageResult) {
- // NOTE: if we're already on main thread, this can delay page receive by a frame
- mMainThreadExecutor.execute(new Runnable() {
- @Override
- public void run() {
- onPageResult(pageResult);
- }
- });
+ public T get(int i) {
+ return null;
}
- @MainThread
@Override
- public void onPageResult(@NonNull PageResult<Integer, T> pageResult) {
- if (pageResult.page == null) {
- detach();
- return;
- }
-
- if (isDetached()) {
- // No op, have detached
- return;
- }
-
- if (mStorage.getPageCount() == 0) {
- mKeyedStorage.init(
- pageResult.leadingNulls, pageResult.page, pageResult.trailingNulls,
- pageResult.positionOffset, TiledPagedList.this);
- } else {
- mKeyedStorage.insertPage(pageResult.leadingNulls, pageResult.page,
- TiledPagedList.this);
- }
+ public int size() {
+ return 0;
}
};
+ private int mLastLoad = -1;
+
+ private AtomicBoolean mDetached = new AtomicBoolean(false);
+
+ private ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+
@WorkerThread
TiledPagedList(@NonNull TiledDataSource<T> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
- @NonNull Config config,
+ Config config,
int position) {
- super(new PagedStorage<Integer, T>(),
- mainThreadExecutor, backgroundThreadExecutor, config);
- mDataSource = dataSource;
-
- final int pageSize = mConfig.mPageSize;
+ super(config.mPageSize, dataSource.countItems());
- final int itemCount = mDataSource.countItems();
- final int firstLoadSize = Math.min(itemCount,
- (Math.max(mConfig.mInitialLoadSizeHint / pageSize, 2)) * pageSize);
- final int firstLoadPosition = computeFirstLoadPosition(
- position, firstLoadSize, pageSize, itemCount);
+ mDataSource = dataSource;
+ mMainThreadExecutor = mainThreadExecutor;
+ mBackgroundThreadExecutor = backgroundThreadExecutor;
+ mConfig = config;
+
+ position = Math.min(Math.max(0, position), mCount);
+
+ int firstPage = position / mPageSize;
+ List<T> firstPageData = dataSource.loadRangeWrapper(firstPage * mPageSize, mPageSize);
+ if (firstPageData != null) {
+ mPageIndexOffset = firstPage;
+ mPages.add(firstPageData);
+ mLastLoad = position;
+ } else {
+ detach();
+ return;
+ }
- mDataSource.loadRangeInitial(firstLoadPosition, firstLoadSize, pageSize,
- itemCount, mReceiver);
+ int secondPage = (position % mPageSize < mPageSize / 2) ? firstPage - 1 : firstPage + 1;
+ if (secondPage < 0 || secondPage > mMaxPageCount) {
+ // no second page to load
+ return;
+ }
+ List<T> secondPageData = dataSource.loadRangeWrapper(secondPage * mPageSize, mPageSize);
+ if (secondPageData != null) {
+ boolean before = secondPage < firstPage;
+ mPages.add(before ? 0 : 1, secondPageData);
+ if (before) {
+ mPageIndexOffset--;
+ }
+ return;
+ }
+ detach();
}
- static int computeFirstLoadPosition(int position, int firstLoadSize, int pageSize, int size) {
- int idealStart = position - firstLoadSize / 2;
+ @Override
+ public void loadAround(int index) {
+ mLastLoad = index;
- int roundedPageStart = Math.round(idealStart / pageSize) * pageSize;
+ int minimumPage = Math.max((index - mConfig.mPrefetchDistance) / mPageSize, 0);
+ int maximumPage = Math.min((index + mConfig.mPrefetchDistance) / mPageSize,
+ mMaxPageCount - 1);
- // minimum start position is 0
- roundedPageStart = Math.max(0, roundedPageStart);
+ if (minimumPage < mPageIndexOffset) {
+ for (int i = 0; i < mPageIndexOffset - minimumPage; i++) {
+ mPages.add(0, null);
+ }
+ mPageIndexOffset = minimumPage;
+ }
+ if (maximumPage >= mPageIndexOffset + mPages.size()) {
+ for (int i = mPages.size(); i <= maximumPage - mPageIndexOffset; i++) {
+ mPages.add(mPages.size(), null);
+ }
+ }
+ for (int i = minimumPage; i <= maximumPage; i++) {
+ scheduleLoadPage(i);
+ }
+ }
- // maximum start pos is that which will encompass end of list
- int maximumLoadPage = ((size - firstLoadSize + pageSize - 1) / pageSize) * pageSize;
- roundedPageStart = Math.min(maximumLoadPage, roundedPageStart);
+ private void scheduleLoadPage(final int pageIndex) {
+ final int localPageIndex = pageIndex - mPageIndexOffset;
- return roundedPageStart;
- }
+ if (mPages.get(localPageIndex) != null) {
+ // page is present in list, and non-null - don't need to load
+ return;
+ }
+ mPages.set(localPageIndex, mLoadingPlaceholder);
- @Override
- boolean isContiguous() {
- return false;
- }
+ mBackgroundThreadExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (mDetached.get()) {
+ return;
+ }
+ final List<T> data = mDataSource.loadRangeWrapper(
+ pageIndex * mPageSize, mPageSize);
+ if (data != null) {
+ mMainThreadExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (mDetached.get()) {
+ return;
+ }
+ loadPageImpl(pageIndex, data);
+ }
+ });
+ } else {
+ detach();
+ }
+ }
+ });
- @Nullable
- @Override
- public Object getLastKey() {
- return mLastLoad;
}
- @Override
- protected void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> pagedListSnapshot,
- @NonNull Callback callback) {
- //noinspection UnnecessaryLocalVariable
- final PagedStorage<?, T> snapshot = pagedListSnapshot.mStorage;
-
- // loop through each page and signal the callback for any pages that are present now,
- // but not in the snapshot.
- final int pageSize = mConfig.mPageSize;
- final int leadingNullPages = mStorage.getLeadingNullCount() / pageSize;
- final int pageCount = mStorage.getPageCount();
- for (int i = 0; i < pageCount; i++) {
- int pageIndex = i + leadingNullPages;
- int updatedPages = 0;
- // count number of consecutive pages that were added since the snapshot...
- while (updatedPages < mStorage.getPageCount()
- && mStorage.hasPage(pageSize, pageIndex + updatedPages)
- && !snapshot.hasPage(pageSize, pageIndex + updatedPages)) {
- updatedPages++;
- }
- // and signal them all at once to the callback
- if (updatedPages > 0) {
- callback.onChanged(pageIndex * pageSize, pageSize * updatedPages);
- i += updatedPages - 1;
+ private void loadPageImpl(int pageIndex, List<T> data) {
+ int localPageIndex = pageIndex - mPageIndexOffset;
+
+ if (mPages.get(localPageIndex) != mLoadingPlaceholder) {
+ throw new IllegalStateException("Data inserted before requested.");
+ }
+ mPages.set(localPageIndex, data);
+ for (WeakReference<Callback> weakRef : mCallbacks) {
+ Callback callback = weakRef.get();
+ if (callback != null) {
+ callback.onChanged(pageIndex * mPageSize, data.size());
}
}
}
@Override
- protected void loadAroundInternal(int index) {
- mStorage.allocatePlaceholders(index, mConfig.mPrefetchDistance, mConfig.mPageSize, this);
+ public boolean isImmutable() {
+ // TODO: consider counting loaded pages, return true if mLoadedPages == mMaxPageCount
+ // Note: could at some point want to support growing past max count, or grow dynamically
+ return isDetached();
}
@Override
- public void onInitialized(int count) {
- notifyInserted(0, count);
+ public void addWeakCallback(@Nullable PagedList<T> previousSnapshot,
+ @NonNull Callback callback) {
+ PageArrayList<T> snapshot = (PageArrayList<T>) previousSnapshot;
+ if (snapshot != this && snapshot != null) {
+ // loop through each page and signal the callback for any pages that are present now,
+ // but not in the snapshot.
+ for (int i = 0; i < mPages.size(); i++) {
+ int pageIndex = i + mPageIndexOffset;
+ int pageCount = 0;
+ // count number of consecutive pages that were added since the snapshot...
+ while (pageCount < mPages.size()
+ && hasPage(pageIndex + pageCount)
+ && !snapshot.hasPage(pageIndex + pageCount)) {
+ pageCount++;
+ }
+ // and signal them all at once to the callback
+ if (pageCount > 0) {
+ callback.onChanged(pageIndex * mPageSize, mPageSize * pageCount);
+ i += pageCount - 1;
+ }
+ }
+ }
+ mCallbacks.add(new WeakReference<>(callback));
}
@Override
- public void onPagePrepended(int leadingNulls, int changed, int added) {
- throw new IllegalStateException("Contiguous callback on TiledPagedList");
+ public void removeWeakCallback(@NonNull Callback callback) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ Callback currentCallback = mCallbacks.get(i).get();
+ if (currentCallback == null || currentCallback == callback) {
+ mCallbacks.remove(i);
+ }
+ }
}
@Override
- public void onPageAppended(int endPosition, int changed, int added) {
- throw new IllegalStateException("Contiguous callback on TiledPagedList");
+ public boolean isDetached() {
+ return mDetached.get();
}
@Override
- public void onPagePlaceholderInserted(final int pageIndex) {
- // placeholder means initialize a load
- mBackgroundThreadExecutor.execute(new Runnable() {
- @Override
- public void run() {
- if (isDetached()) {
- return;
- }
- final int pageSize = mConfig.mPageSize;
- mDataSource.loadRange(pageIndex * pageSize, pageSize, mReceiver);
- }
- });
+ public void detach() {
+ mDetached.set(true);
}
+ @Nullable
@Override
- public void onPageInserted(int start, int count) {
- notifyChanged(start, count);
+ public Object getLastKey() {
+ return mLastLoad;
}
}
diff --git a/android/arch/paging/TiledPagedListTest.java b/android/arch/paging/TiledPagedListTest.java
index 22bfd1ff..4ad02e12 100644
--- a/android/arch/paging/TiledPagedListTest.java
+++ b/android/arch/paging/TiledPagedListTest.java
@@ -78,107 +78,75 @@ public class TiledPagedListTest {
}
}
- private void verifyRange(List<Item> list, Integer... loadedPages) {
+ private void verifyRange(PageArrayList<Item> list, Integer... loadedPages) {
List<Integer> loadedPageList = Arrays.asList(loadedPages);
assertEquals(ITEMS.size(), list.size());
for (int i = 0; i < list.size(); i++) {
if (loadedPageList.contains(i / PAGE_SIZE)) {
- assertSame("Index " + i, ITEMS.get(i), list.get(i));
+ assertSame(ITEMS.get(i), list.get(i));
} else {
- assertEquals("Index " + i, null, list.get(i));
+ assertEquals(null, list.get(i));
}
}
}
- private TiledPagedList<Item> createTiledPagedList(int loadPosition, int initPages) {
- return createTiledPagedList(loadPosition, initPages, PAGE_SIZE);
+ private TiledPagedList<Item> createTiledPagedList(int loadPosition) {
+ return createTiledPagedList(loadPosition, PAGE_SIZE);
}
- private TiledPagedList<Item> createTiledPagedList(int loadPosition, int initPages,
- int prefetchDistance) {
+ private TiledPagedList<Item> createTiledPagedList(int loadPosition, int prefetchDistance) {
TestTiledSource source = new TestTiledSource();
return new TiledPagedList<>(
source, mMainThread, mBackgroundThread,
new PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
- .setInitialLoadSizeHint(PAGE_SIZE * initPages)
.setPrefetchDistance(prefetchDistance)
.build(),
loadPosition);
}
@Test
- public void computeFirstLoadPosition_zero() {
- assertEquals(0, TiledPagedList.computeFirstLoadPosition(0, 30, 10, 100));
- }
-
- @Test
- public void computeFirstLoadPosition_requestedPositionIncluded() {
- assertEquals(0, TiledPagedList.computeFirstLoadPosition(10, 10, 10, 100));
- }
-
- @Test
- public void computeFirstLoadPosition_endAdjusted() {
- assertEquals(70, TiledPagedList.computeFirstLoadPosition(99, 30, 10, 100));
- }
-
- @Test
- public void initialLoad_onePage() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 1);
- verifyRange(pagedList, 0, 1);
- }
-
- @Test
- public void initialLoad_onePageOffset() {
- TiledPagedList<Item> pagedList = createTiledPagedList(10, 1);
- verifyRange(pagedList, 0, 1);
- }
-
- @Test
- public void initialLoad_full() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 100);
- verifyRange(pagedList, 0, 1, 2, 3, 4);
+ public void initialLoad() {
+ TiledPagedList<Item> pagedList = createTiledPagedList(0);
+ verifyRange(pagedList, 0);
}
@Test
public void initialLoad_end() {
- TiledPagedList<Item> pagedList = createTiledPagedList(44, 2);
+ TiledPagedList<Item> pagedList = createTiledPagedList(44);
verifyRange(pagedList, 3, 4);
}
@Test
public void initialLoad_multiple() {
- TiledPagedList<Item> pagedList = createTiledPagedList(9, 2);
+ TiledPagedList<Item> pagedList = createTiledPagedList(9);
verifyRange(pagedList, 0, 1);
}
@Test
public void initialLoad_offset() {
- TiledPagedList<Item> pagedList = createTiledPagedList(41, 2);
+ TiledPagedList<Item> pagedList = createTiledPagedList(41);
verifyRange(pagedList, 3, 4);
}
@Test
public void append() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 1);
+ TiledPagedList<Item> pagedList = createTiledPagedList(0);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
- verifyRange(pagedList, 0, 1);
+ verifyRange(pagedList, 0);
verifyZeroInteractions(callback);
- pagedList.loadAround(15);
-
- verifyRange(pagedList, 0, 1);
-
+ pagedList.loadAround(5);
drain();
- verifyRange(pagedList, 0, 1, 2);
- verify(callback).onChanged(20, 10);
+ verifyRange(pagedList, 0, 1);
+ verify(callback).onChanged(10, 10);
verifyNoMoreInteractions(callback);
}
@Test
public void prepend() {
- TiledPagedList<Item> pagedList = createTiledPagedList(44, 2);
+ TiledPagedList<Item> pagedList = createTiledPagedList(44);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
verifyRange(pagedList, 3, 4);
@@ -194,16 +162,16 @@ public class TiledPagedListTest {
@Test
public void loadWithGap() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 1);
+ TiledPagedList<Item> pagedList = createTiledPagedList(0);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
- verifyRange(pagedList, 0, 1);
+ verifyRange(pagedList, 0);
verifyZeroInteractions(callback);
pagedList.loadAround(44);
drain();
- verifyRange(pagedList, 0, 1, 3, 4);
+ verifyRange(pagedList, 0, 3, 4);
verify(callback).onChanged(30, 10);
verify(callback).onChanged(40, 5);
verifyNoMoreInteractions(callback);
@@ -211,56 +179,58 @@ public class TiledPagedListTest {
@Test
public void tinyPrefetchTest() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 1, 1);
+ TiledPagedList<Item> pagedList = createTiledPagedList(0, 1);
PagedList.Callback callback = mock(PagedList.Callback.class);
pagedList.addWeakCallback(null, callback);
- verifyRange(pagedList, 0, 1);
+ verifyRange(pagedList, 0); // just 4 loaded
verifyZeroInteractions(callback);
- pagedList.loadAround(33);
+ pagedList.loadAround(23);
drain();
- verifyRange(pagedList, 0, 1, 3);
- verify(callback).onChanged(30, 10);
+ verifyRange(pagedList, 0, 2);
+ verify(callback).onChanged(20, 10);
verifyNoMoreInteractions(callback);
pagedList.loadAround(44);
drain();
- verifyRange(pagedList, 0, 1, 3, 4);
+ verifyRange(pagedList, 0, 2, 4);
verify(callback).onChanged(40, 5);
verifyNoMoreInteractions(callback);
}
@Test
public void appendCallbackAddedLate() {
- TiledPagedList<Item> pagedList = createTiledPagedList(0, 1, 0);
- verifyRange(pagedList, 0, 1);
+ TiledPagedList<Item> pagedList = createTiledPagedList(0, 0);
+ verifyRange(pagedList, 0);
- pagedList.loadAround(25);
+ pagedList.loadAround(15);
drain();
- verifyRange(pagedList, 0, 1, 2);
+ verifyRange(pagedList, 0, 1);
+
+ // snapshot at 20 items
+ PageArrayList<Item> snapshot = (PageArrayList<Item>) pagedList.snapshot();
+ verifyRange(snapshot, 0, 1);
- // snapshot at 30 items
- List<Item> snapshot = pagedList.snapshot();
- verifyRange(snapshot, 0, 1, 2);
+ pagedList.loadAround(25);
pagedList.loadAround(35);
- pagedList.loadAround(44);
drain();
- verifyRange(pagedList, 0, 1, 2, 3, 4);
- verifyRange(snapshot, 0, 1, 2);
+ verifyRange(pagedList, 0, 1, 2, 3);
+ verifyRange(snapshot, 0, 1);
PagedList.Callback callback = mock(
PagedList.Callback.class);
pagedList.addWeakCallback(snapshot, callback);
- verify(callback).onChanged(30, 20);
+ verify(callback).onChanged(20, 20);
verifyNoMoreInteractions(callback);
}
+
@Test
public void prependCallbackAddedLate() {
- TiledPagedList<Item> pagedList = createTiledPagedList(44, 2, 0);
+ TiledPagedList<Item> pagedList = createTiledPagedList(44, 0);
verifyRange(pagedList, 3, 4);
pagedList.loadAround(25);
@@ -268,9 +238,10 @@ public class TiledPagedListTest {
verifyRange(pagedList, 2, 3, 4);
// snapshot at 30 items
- List<Item> snapshot = pagedList.snapshot();
+ PageArrayList<Item> snapshot = (PageArrayList<Item>) pagedList.snapshot();
verifyRange(snapshot, 2, 3, 4);
+
pagedList.loadAround(15);
pagedList.loadAround(5);
drain();
@@ -301,11 +272,10 @@ public class TiledPagedListTest {
assertTrue(pagedList.isContiguous());
- ContiguousPagedList<Integer, Item> contiguousPagedList =
- (ContiguousPagedList<Integer, Item>) pagedList;
- assertEquals(0, contiguousPagedList.mStorage.getLeadingNullCount());
- assertEquals(PAGE_SIZE, contiguousPagedList.mStorage.getStorageCount());
- assertEquals(0, contiguousPagedList.mStorage.getTrailingNullCount());
+ ContiguousPagedList<Item> contiguousPagedList = (ContiguousPagedList<Item>) pagedList;
+ assertEquals(0, contiguousPagedList.getLeadingNullCount());
+ assertEquals(PAGE_SIZE, contiguousPagedList.mList.size());
+ assertEquals(0, contiguousPagedList.getTrailingNullCount());
}
private void drain() {
diff --git a/android/arch/persistence/room/InvalidationTracker.java b/android/arch/persistence/room/InvalidationTracker.java
index b31dc13a..45ec0289 100644
--- a/android/arch/persistence/room/InvalidationTracker.java
+++ b/android/arch/persistence/room/InvalidationTracker.java
@@ -219,7 +219,7 @@ public class InvalidationTracker {
*
* @param observer The observer which listens the database for changes.
*/
- public void addObserver(@NonNull Observer observer) {
+ public void addObserver(Observer observer) {
final String[] tableNames = observer.mTables;
int[] tableIds = new int[tableNames.length];
final int size = tableNames.length;
@@ -265,7 +265,7 @@ public class InvalidationTracker {
* @param observer The observer to remove.
*/
@SuppressWarnings("WeakerAccess")
- public void removeObserver(@NonNull final Observer observer) {
+ public void removeObserver(final Observer observer) {
ObserverWrapper wrapper;
synchronized (mObserverMap) {
wrapper = mObserverMap.remove(observer);
diff --git a/android/arch/persistence/room/Relation.java b/android/arch/persistence/room/Relation.java
index d55bbfe8..72066992 100644
--- a/android/arch/persistence/room/Relation.java
+++ b/android/arch/persistence/room/Relation.java
@@ -28,8 +28,6 @@ import java.lang.annotation.Target;
* <pre>
* {@literal @}Entity
* public class Pet {
- * {@literal @} PrimaryKey
- * int id;
* int userId;
* String name;
* // other fields
@@ -43,8 +41,8 @@ import java.lang.annotation.Target;
*
* {@literal @}Dao
* public interface UserPetDao {
- * {@literal @}Query("SELECT id, name from User")
- * public List&lt;UserNameAndAllPets&gt; loadUserAndPets();
+ * {@literal @}Query("SELECT id, name from User WHERE age &gt; :minAge")
+ * public List&lt;UserNameAndAllPets&gt; loadUserAndPets(int minAge);
* }
* </pre>
* <p>
@@ -65,16 +63,16 @@ import java.lang.annotation.Target;
* {@literal @}Embedded
* public User user;
* {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
- * public List&lt;PetNameAndId&gt; pets;
+ * public List<PetNameAndId> pets;
* }
* {@literal @}Dao
* public interface UserPetDao {
- * {@literal @}Query("SELECT * from User")
- * public List&lt;UserAllPets&gt; loadUserAndPets();
+ * {@literal @}Query("SELECT * from User WHERE age &gt; :minAge")
+ * public List&lt;UserAllPets&gt; loadUserAndPets(int minAge);
* }
* </pre>
* <p>
- * In the example above, {@code PetNameAndId} is a regular Pojo but all of fields are fetched
+ * In the example above, {@code PetNameAndId} is a regular but all of fields are fetched
* from the {@code entity} defined in the {@code @Relation} annotation (<i>Pet</i>).
* {@code PetNameAndId} could also define its own relations all of which would also be fetched
* automatically.
@@ -87,7 +85,7 @@ import java.lang.annotation.Target;
* public User user;
* {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class,
* projection = {"name"})
- * public List&lt;String&gt; petNames;
+ * public List<String> petNames;
* }
* </pre>
* <p>
@@ -95,7 +93,7 @@ import java.lang.annotation.Target;
* cannot have relations. This is a design decision to avoid common pitfalls in {@link Entity}
* setups. You can read more about it in the main Room documentation. When loading data, you can
* simply work around this limitation by creating Pojo classes that extend the {@link Entity}.
- * <p>
+ *
* Note that the {@code @Relation} annotated field cannot be a constructor parameter, it must be
* public or have a public setter.
*/
diff --git a/android/arch/persistence/room/Room.java b/android/arch/persistence/room/Room.java
index 2850b55e..8ce4be0c 100644
--- a/android/arch/persistence/room/Room.java
+++ b/android/arch/persistence/room/Room.java
@@ -43,7 +43,6 @@ public class Room {
* @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
*/
@SuppressWarnings("WeakerAccess")
- @NonNull
public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
@NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
//noinspection ConstantConditions
@@ -66,7 +65,6 @@ public class Room {
* @param <T> The type of the database class.
* @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
*/
- @NonNull
public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
@NonNull Context context, @NonNull Class<T> klass) {
return new RoomDatabase.Builder<>(context, klass, null);
diff --git a/android/arch/persistence/room/RoomDatabase.java b/android/arch/persistence/room/RoomDatabase.java
index 8c940246..cdad868d 100644
--- a/android/arch/persistence/room/RoomDatabase.java
+++ b/android/arch/persistence/room/RoomDatabase.java
@@ -49,7 +49,7 @@ import java.util.concurrent.locks.ReentrantLock;
*
* @see Database
*/
-//@SuppressWarnings({"unused", "WeakerAccess"})
+@SuppressWarnings({"unused", "WeakerAccess"})
public abstract class RoomDatabase {
private static final String DB_IMPL_SUFFIX = "_Impl";
// set by the generated open helper.
@@ -153,9 +153,7 @@ public abstract class RoomDatabase {
*
* @hide
*/
- @SuppressWarnings("WeakerAccess")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- // used in generated code
public void assertNotMainThread() {
if (mAllowMainThreadQueries) {
return;
@@ -300,7 +298,6 @@ public abstract class RoomDatabase {
* @return True if there is an active transaction in current thread, false otherwise.
* @see SupportSQLiteDatabase#inTransaction()
*/
- @SuppressWarnings("WeakerAccess")
public boolean inTransaction() {
return mOpenHelper.getWritableDatabase().inTransaction();
}
@@ -310,6 +307,7 @@ public abstract class RoomDatabase {
*
* @param <T> The type of the abstract database class.
*/
+ @SuppressWarnings("unused")
public static class Builder<T extends RoomDatabase> {
private final Class<T> mDatabaseClass;
private final String mName;
@@ -339,8 +337,7 @@ public abstract class RoomDatabase {
* @param factory The factory to use to access the database.
* @return this
*/
- @NonNull
- public Builder<T> openHelperFactory(@Nullable SupportSQLiteOpenHelper.Factory factory) {
+ public Builder<T> openHelperFactory(SupportSQLiteOpenHelper.Factory factory) {
mFactory = factory;
return this;
}
@@ -364,7 +361,6 @@ public abstract class RoomDatabase {
* changes.
* @return this
*/
- @NonNull
public Builder<T> addMigrations(Migration... migrations) {
mMigrationContainer.addMigrations(migrations);
return this;
@@ -382,7 +378,6 @@ public abstract class RoomDatabase {
*
* @return this
*/
- @NonNull
public Builder<T> allowMainThreadQueries() {
mAllowMainThreadQueries = true;
return this;
@@ -405,7 +400,6 @@ public abstract class RoomDatabase {
*
* @return this
*/
- @NonNull
public Builder<T> fallbackToDestructiveMigration() {
mRequireMigration = false;
return this;
@@ -417,7 +411,6 @@ public abstract class RoomDatabase {
* @param callback The callback.
* @return this
*/
- @NonNull
public Builder<T> addCallback(@NonNull Callback callback) {
if (mCallbacks == null) {
mCallbacks = new ArrayList<>();
@@ -434,7 +427,6 @@ public abstract class RoomDatabase {
*
* @return A new database instance.
*/
- @NonNull
public T build() {
//noinspection ConstantConditions
if (mContext == null) {
@@ -501,7 +493,6 @@ public abstract class RoomDatabase {
* @return An ordered list of {@link Migration} objects that should be run to migrate
* between the given versions. If a migration path cannot be found, returns {@code null}.
*/
- @SuppressWarnings("WeakerAccess")
@Nullable
public List<Migration> findMigrationPath(int start, int end) {
if (start == end) {
diff --git a/android/arch/persistence/room/RoomWarnings.java b/android/arch/persistence/room/RoomWarnings.java
index f05e6be2..c64be967 100644
--- a/android/arch/persistence/room/RoomWarnings.java
+++ b/android/arch/persistence/room/RoomWarnings.java
@@ -125,12 +125,4 @@ public class RoomWarnings {
* annotation.
*/
public static final String DEFAULT_CONSTRUCTOR = "ROOM_DEFAULT_CONSTRUCTOR";
-
- /**
- * Reported when a @Query method returns a Pojo that has relations but the method is not
- * annotated with @Transaction. Relations are run as separate queries and if the query is not
- * run inside a transaction, it might return inconsistent results from the database.
- */
- public static final String RELATION_QUERY_WITHOUT_TRANSACTION =
- "ROOM_RELATION_QUERY_WITHOUT_TRANSACTION";
}
diff --git a/android/arch/persistence/room/Transaction.java b/android/arch/persistence/room/Transaction.java
index 3b6ede9c..914e4f41 100644
--- a/android/arch/persistence/room/Transaction.java
+++ b/android/arch/persistence/room/Transaction.java
@@ -22,10 +22,9 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Marks a method in a {@link Dao} class as a transaction method.
+ * Marks a method in an abstract {@link Dao} class as a transaction method.
* <p>
- * When used on a non-abstract method of an abstract {@link Dao} class,
- * the derived implementation of the method will execute the super method in a database transaction.
+ * The derived implementation of the method will execute the super method in a database transaction.
* All the parameters and return types are preserved. The transaction will be marked as successful
* unless an exception is thrown in the method body.
* <p>
@@ -45,38 +44,6 @@ import java.lang.annotation.Target;
* }
* }
* </pre>
- * <p>
- * When used on a {@link Query} method that has a {@code Select} statement, the generated code for
- * the Query will be run in a transaction. There are 2 main cases where you may want to do that:
- * <ol>
- * <li>If the result of the query is fairly big, it is better to run it inside a transaction
- * to receive a consistent result. Otherwise, if the query result does not fit into a single
- * {@link android.database.CursorWindow CursorWindow}, the query result may be corrupted due to
- * changes in the database in between cursor window swaps.
- * <li>If the result of the query is a Pojo with {@link Relation} fields, these fields are
- * queried separately. To receive consistent results between these queries, you probably want
- * to run them in a single transaction.
- * </ol>
- * Example:
- * <pre>
- * class ProductWithReviews extends Product {
- * {@literal @}Relation(parentColumn = "id", entityColumn = "productId", entity = Review.class)
- * public List&lt;Review> reviews;
- * }
- * {@literal @}Dao
- * public interface ProductDao {
- * {@literal @}Transaction {@literal @}Query("SELECT * from products")
- * public List&lt;ProductWithReviews> loadAll();
- * }
- * </pre>
- * If the query is an async query (e.g. returns a {@link android.arch.lifecycle.LiveData LiveData}
- * or RxJava Flowable, the transaction is properly handled when the query is run, not when the
- * method is called.
- * <p>
- * Putting this annotation on an {@link Insert}, {@link Update} or {@link Delete} method has no
- * impact because they are always run inside a transaction. Similarly, if it is annotated with
- * {@link Query} but runs an update or delete statement, it is automatically wrapped in a
- * transaction.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
diff --git a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java b/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
index cdd464e4..818c46b4 100644
--- a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
+++ b/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
@@ -86,7 +86,6 @@ public class RoomPagedListActivity extends AppCompatActivity {
@Override
protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
PagedList<Customer> list = mAdapter.getCurrentList();
if (list == null) {
// Can't find anything to restore
diff --git a/android/arch/persistence/room/integration/testapp/database/CustomerDao.java b/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
index b5df914a..9d402370 100644
--- a/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
+++ b/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
@@ -59,7 +59,7 @@ public interface CustomerDao {
// Keyed
- @Query("SELECT * from customer ORDER BY mLastName DESC LIMIT :limit")
+ @Query("SELECT * from customer ORDER BY mLastName ASC LIMIT :limit")
List<Customer> customerNameInitial(int limit);
@Query("SELECT * from customer WHERE mLastName < :key ORDER BY mLastName DESC LIMIT :limit")
diff --git a/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java b/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java
new file mode 100644
index 00000000..3cbffc8b
--- /dev/null
+++ b/android/arch/persistence/room/integration/testapp/db/JDBCOpenHelper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.persistence.room.integration.testapp.db;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+
+public class JDBCOpenHelper implements SupportSQLiteOpenHelper {
+ @Override
+ public String getDatabaseName() {
+ return null;
+ }
+
+ @Override
+ public void setWriteAheadLoggingEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public SupportSQLiteDatabase getWritableDatabase() {
+ return null;
+ }
+
+ @Override
+ public SupportSQLiteDatabase getReadableDatabase() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java b/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
index 33f40183..84f20ec5 100644
--- a/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
+++ b/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
@@ -17,17 +17,20 @@
package android.arch.persistence.room.integration.testapp.test;
import static org.hamcrest.CoreMatchers.hasItem;
-import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
-import android.arch.core.executor.testing.CountingTaskExecutorRule;
+import android.arch.core.executor.ArchTaskExecutor;
+import android.arch.core.executor.TaskExecutor;
import android.arch.persistence.room.InvalidationTracker;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.integration.testapp.TestDatabase;
import android.arch.persistence.room.integration.testapp.dao.UserDao;
import android.arch.persistence.room.integration.testapp.vo.User;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -35,13 +38,17 @@ import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
/**
* Tests invalidation tracking.
@@ -49,97 +56,138 @@ import java.util.concurrent.TimeoutException;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class InvalidationTest {
- @Rule
- public CountingTaskExecutorRule executorRule = new CountingTaskExecutorRule();
private UserDao mUserDao;
private TestDatabase mDb;
@Before
- public void createDb() throws TimeoutException, InterruptedException {
+ public void createDb() {
Context context = InstrumentationRegistry.getTargetContext();
mDb = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
mUserDao = mDb.getUserDao();
- drain();
+ }
+
+ @Before
+ public void setSingleThreadedIO() {
+ ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
+ ExecutorService mIOExecutor = Executors.newSingleThreadExecutor();
+ Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void executeOnDiskIO(Runnable runnable) {
+ mIOExecutor.execute(runnable);
+ }
+
+ @Override
+ public void postToMainThread(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
+ @Override
+ public boolean isMainThread() {
+ return Thread.currentThread() == Looper.getMainLooper().getThread();
+ }
+ });
}
@After
- public void closeDb() throws TimeoutException, InterruptedException {
- mDb.close();
- drain();
+ public void clearExecutor() {
+ ArchTaskExecutor.getInstance().setDelegate(null);
}
- private void drain() throws TimeoutException, InterruptedException {
- executorRule.drainTasks(1, TimeUnit.MINUTES);
+ private void waitUntilIOThreadIsIdle() {
+ FutureTask<Void> future = new FutureTask<>(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ return null;
+ }
+ });
+ ArchTaskExecutor.getInstance().executeOnDiskIO(future);
+ //noinspection TryWithIdenticalCatches
+ try {
+ future.get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Test
- public void testInvalidationOnUpdate() throws InterruptedException, TimeoutException {
+ public void testInvalidationOnUpdate() throws InterruptedException {
User user = TestUtil.createUser(3);
mUserDao.insert(user);
- LoggingObserver observer = new LoggingObserver("User");
+ LatchObserver observer = new LatchObserver(1, "User");
mDb.getInvalidationTracker().addObserver(observer);
- drain();
mUserDao.updateById(3, "foo2");
- drain();
+ waitUntilIOThreadIsIdle();
+ assertThat(observer.await(), is(true));
assertThat(observer.getInvalidatedTables(), hasSize(1));
assertThat(observer.getInvalidatedTables(), hasItem("User"));
}
@Test
- public void testInvalidationOnDelete() throws InterruptedException, TimeoutException {
+ public void testInvalidationOnDelete() throws InterruptedException {
User user = TestUtil.createUser(3);
mUserDao.insert(user);
- LoggingObserver observer = new LoggingObserver("User");
+ LatchObserver observer = new LatchObserver(1, "User");
mDb.getInvalidationTracker().addObserver(observer);
- drain();
mUserDao.delete(user);
- drain();
+ waitUntilIOThreadIsIdle();
+ assertThat(observer.await(), is(true));
assertThat(observer.getInvalidatedTables(), hasSize(1));
assertThat(observer.getInvalidatedTables(), hasItem("User"));
}
@Test
- public void testInvalidationOnInsert() throws InterruptedException, TimeoutException {
- LoggingObserver observer = new LoggingObserver("User");
+ public void testInvalidationOnInsert() throws InterruptedException {
+ LatchObserver observer = new LatchObserver(1, "User");
mDb.getInvalidationTracker().addObserver(observer);
- drain();
mUserDao.insert(TestUtil.createUser(3));
- drain();
+ waitUntilIOThreadIsIdle();
+ assertThat(observer.await(), is(true));
assertThat(observer.getInvalidatedTables(), hasSize(1));
assertThat(observer.getInvalidatedTables(), hasItem("User"));
}
@Test
- public void testDontInvalidateOnLateInsert() throws InterruptedException, TimeoutException {
- LoggingObserver observer = new LoggingObserver("User");
+ public void testDontInvalidateOnLateInsert() throws InterruptedException {
+ LatchObserver observer = new LatchObserver(1, "User");
mUserDao.insert(TestUtil.createUser(3));
- drain();
+ waitUntilIOThreadIsIdle();
mDb.getInvalidationTracker().addObserver(observer);
- drain();
- assertThat(observer.getInvalidatedTables(), nullValue());
+ waitUntilIOThreadIsIdle();
+ assertThat(observer.await(), is(false));
}
@Test
- public void testMultipleTables() throws InterruptedException, TimeoutException {
- LoggingObserver observer = new LoggingObserver("User", "Pet");
+ public void testMultipleTables() throws InterruptedException {
+ LatchObserver observer = new LatchObserver(1, "User", "Pet");
mDb.getInvalidationTracker().addObserver(observer);
- drain();
mUserDao.insert(TestUtil.createUser(3));
- drain();
+ waitUntilIOThreadIsIdle();
+ assertThat(observer.await(), is(true));
assertThat(observer.getInvalidatedTables(), hasSize(1));
assertThat(observer.getInvalidatedTables(), hasItem("User"));
}
- private static class LoggingObserver extends InvalidationTracker.Observer {
+ private static class LatchObserver extends InvalidationTracker.Observer {
+ CountDownLatch mLatch;
+
private Set<String> mInvalidatedTables;
- LoggingObserver(String... tables) {
+ LatchObserver(int permits, String... tables) {
super(tables);
+ mLatch = new CountDownLatch(permits);
+ }
+
+ boolean await() throws InterruptedException {
+ return mLatch.await(5, TimeUnit.SECONDS);
}
@Override
public void onInvalidated(@NonNull Set<String> tables) {
mInvalidatedTables = tables;
+ mLatch.countDown();
}
Set<String> getInvalidatedTables() {
diff --git a/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java b/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java
index 2735c05a..e11117e4 100644
--- a/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java
+++ b/android/arch/persistence/room/integration/testapp/test/QueryDataSourceTest.java
@@ -166,13 +166,17 @@ public class QueryDataSourceTest extends TestDatabaseTest {
p = dataSource.loadBefore(15, list.get(0), 10);
assertNotNull(p);
- list.addAll(0, p);
+ for (User u : p) {
+ list.add(0, u);
+ }
assertArrayEquals(Arrays.copyOfRange(expected, 5, 35), list.toArray());
p = dataSource.loadBefore(5, list.get(0), 10);
assertNotNull(p);
- list.addAll(0, p);
+ for (User u : p) {
+ list.add(0, u);
+ }
assertArrayEquals(Arrays.copyOfRange(expected, 0, 35), list.toArray());
}
diff --git a/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java b/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java
deleted file mode 100644
index 854c8627..00000000
--- a/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.core.executor.testing.CountingTaskExecutorRule;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LiveData;
-import android.arch.lifecycle.Observer;
-import android.arch.paging.LivePagedListProvider;
-import android.arch.paging.PagedList;
-import android.arch.paging.TiledDataSource;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Database;
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.Ignore;
-import android.arch.persistence.room.Insert;
-import android.arch.persistence.room.PrimaryKey;
-import android.arch.persistence.room.Query;
-import android.arch.persistence.room.Relation;
-import android.arch.persistence.room.Room;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.RoomWarnings;
-import android.arch.persistence.room.Transaction;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import io.reactivex.Flowable;
-import io.reactivex.Maybe;
-import io.reactivex.Single;
-import io.reactivex.observers.TestObserver;
-import io.reactivex.schedulers.Schedulers;
-import io.reactivex.subscribers.TestSubscriber;
-
-@SmallTest
-@RunWith(Parameterized.class)
-public class QueryTransactionTest {
- @Rule
- public CountingTaskExecutorRule countingTaskExecutorRule = new CountingTaskExecutorRule();
- private static final AtomicInteger sStartedTransactionCount = new AtomicInteger(0);
- private TransactionDb mDb;
- private final boolean mUseTransactionDao;
- private Entity1Dao mDao;
- private final LiveDataQueryTest.TestLifecycleOwner mLifecycleOwner = new LiveDataQueryTest
- .TestLifecycleOwner();
-
- @NonNull
- @Parameterized.Parameters(name = "useTransaction_{0}")
- public static Boolean[] getParams() {
- return new Boolean[]{false, true};
- }
-
- public QueryTransactionTest(boolean useTransactionDao) {
- mUseTransactionDao = useTransactionDao;
- }
-
- @Before
- public void initDb() {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mLifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
- }
- });
-
- resetTransactionCount();
- mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(),
- TransactionDb.class).build();
- mDao = mUseTransactionDao ? mDb.transactionDao() : mDb.dao();
- drain();
- }
-
- @After
- public void closeDb() {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mLifecycleOwner.handleEvent(Lifecycle.Event.ON_DESTROY);
- }
- });
- drain();
- mDb.close();
- }
-
- @Test
- public void readList() {
- mDao.insert(new Entity1(1, "foo"));
- resetTransactionCount();
-
- int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
- List<Entity1> allEntities = mDao.allEntities();
- assertTransactionCount(allEntities, expectedTransactionCount);
- }
-
- @Test
- public void liveData() {
- LiveData<List<Entity1>> listLiveData = mDao.liveData();
- observeForever(listLiveData);
- drain();
- assertThat(listLiveData.getValue(), is(Collections.<Entity1>emptyList()));
-
- resetTransactionCount();
- mDao.insert(new Entity1(1, "foo"));
- drain();
-
- //noinspection ConstantConditions
- assertThat(listLiveData.getValue().size(), is(1));
- int expectedTransactionCount = mUseTransactionDao ? 2 : 1;
- assertTransactionCount(listLiveData.getValue(), expectedTransactionCount);
- }
-
- @Test
- public void flowable() {
- Flowable<List<Entity1>> flowable = mDao.flowable();
- TestSubscriber<List<Entity1>> subscriber = observe(flowable);
- drain();
- assertThat(subscriber.values().size(), is(1));
-
- resetTransactionCount();
- mDao.insert(new Entity1(1, "foo"));
- drain();
-
- List<Entity1> allEntities = subscriber.values().get(1);
- assertThat(allEntities.size(), is(1));
- int expectedTransactionCount = mUseTransactionDao ? 2 : 1;
- assertTransactionCount(allEntities, expectedTransactionCount);
- }
-
- @Test
- public void maybe() {
- mDao.insert(new Entity1(1, "foo"));
- resetTransactionCount();
-
- int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
- Maybe<List<Entity1>> listMaybe = mDao.maybe();
- TestObserver<List<Entity1>> observer = observe(listMaybe);
- drain();
- List<Entity1> allEntities = observer.values().get(0);
- assertTransactionCount(allEntities, expectedTransactionCount);
- }
-
- @Test
- public void single() {
- mDao.insert(new Entity1(1, "foo"));
- resetTransactionCount();
-
- int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
- Single<List<Entity1>> listMaybe = mDao.single();
- TestObserver<List<Entity1>> observer = observe(listMaybe);
- drain();
- List<Entity1> allEntities = observer.values().get(0);
- assertTransactionCount(allEntities, expectedTransactionCount);
- }
-
- @Test
- public void relation() {
- mDao.insert(new Entity1(1, "foo"));
- mDao.insert(new Child(1, 1));
- mDao.insert(new Child(2, 1));
- resetTransactionCount();
-
- List<Entity1WithChildren> result = mDao.withRelation();
- int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
- assertTransactionCountWithChildren(result, expectedTransactionCount);
- }
-
- @Test
- public void pagedList() {
- LiveData<PagedList<Entity1>> pagedList = mDao.pagedList().create(null, 10);
- observeForever(pagedList);
- drain();
- assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 0 : 0));
-
- mDao.insert(new Entity1(1, "foo"));
- drain();
- //noinspection ConstantConditions
- assertThat(pagedList.getValue().size(), is(1));
- assertTransactionCount(pagedList.getValue(), mUseTransactionDao ? 2 : 1);
-
- mDao.insert(new Entity1(2, "bar"));
- drain();
- assertThat(pagedList.getValue().size(), is(2));
- assertTransactionCount(pagedList.getValue(), mUseTransactionDao ? 4 : 2);
- }
-
- @Test
- public void dataSource() {
- mDao.insert(new Entity1(2, "bar"));
- drain();
- resetTransactionCount();
- TiledDataSource<Entity1> dataSource = mDao.dataSource();
- dataSource.loadRange(0, 10);
- assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 1 : 0));
- }
-
- private void assertTransactionCount(List<Entity1> allEntities, int expectedTransactionCount) {
- assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount));
- assertThat(allEntities.isEmpty(), is(false));
- for (Entity1 entity1 : allEntities) {
- assertThat(entity1.transactionId, is(expectedTransactionCount));
- }
- }
-
- private void assertTransactionCountWithChildren(List<Entity1WithChildren> allEntities,
- int expectedTransactionCount) {
- assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount));
- assertThat(allEntities.isEmpty(), is(false));
- for (Entity1WithChildren entity1 : allEntities) {
- assertThat(entity1.transactionId, is(expectedTransactionCount));
- assertThat(entity1.children, notNullValue());
- assertThat(entity1.children.isEmpty(), is(false));
- for (Child child : entity1.children) {
- assertThat(child.transactionId, is(expectedTransactionCount));
- }
- }
- }
-
- private void resetTransactionCount() {
- sStartedTransactionCount.set(0);
- }
-
- private void drain() {
- try {
- countingTaskExecutorRule.drainTasks(30, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new AssertionError("interrupted", e);
- } catch (TimeoutException e) {
- throw new AssertionError("drain timed out", e);
- }
- }
-
- private <T> TestSubscriber<T> observe(final Flowable<T> flowable) {
- TestSubscriber<T> subscriber = new TestSubscriber<>();
- flowable.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
- .subscribeWith(subscriber);
- return subscriber;
- }
-
- private <T> TestObserver<T> observe(final Maybe<T> maybe) {
- TestObserver<T> observer = new TestObserver<>();
- maybe.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
- .subscribeWith(observer);
- return observer;
- }
-
- private <T> TestObserver<T> observe(final Single<T> single) {
- TestObserver<T> observer = new TestObserver<>();
- single.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
- .subscribeWith(observer);
- return observer;
- }
-
- private <T> void observeForever(final LiveData<T> liveData) {
- FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- liveData.observe(mLifecycleOwner, new Observer<T>() {
- @Override
- public void onChanged(@Nullable T t) {
-
- }
- });
- return null;
- }
- });
- ArchTaskExecutor.getMainThreadExecutor().execute(futureTask);
- try {
- futureTask.get();
- } catch (InterruptedException e) {
- throw new AssertionError("interrupted", e);
- } catch (ExecutionException e) {
- throw new AssertionError("execution error", e);
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- static class Entity1WithChildren extends Entity1 {
- @Relation(entity = Child.class, parentColumn = "id",
- entityColumn = "entity1Id")
- public List<Child> children;
-
- Entity1WithChildren(int id, String value) {
- super(id, value);
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- @Entity
- static class Child {
- @PrimaryKey(autoGenerate = true)
- public int id;
- public int entity1Id;
- @Ignore
- public final int transactionId = sStartedTransactionCount.get();
-
- Child(int id, int entity1Id) {
- this.id = id;
- this.entity1Id = entity1Id;
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- @Entity
- static class Entity1 {
- @PrimaryKey(autoGenerate = true)
- public int id;
- public String value;
- @Ignore
- public final int transactionId = sStartedTransactionCount.get();
-
- Entity1(int id, String value) {
- this.id = id;
- this.value = value;
- }
- }
-
- // we don't support dao inheritance for queries so for now, go with this
- interface Entity1Dao {
- String SELECT_ALL = "select * from Entity1";
-
- List<Entity1> allEntities();
-
- Flowable<List<Entity1>> flowable();
-
- Maybe<List<Entity1>> maybe();
-
- Single<List<Entity1>> single();
-
- LiveData<List<Entity1>> liveData();
-
- List<Entity1WithChildren> withRelation();
-
- LivePagedListProvider<Integer, Entity1> pagedList();
-
- TiledDataSource<Entity1> dataSource();
-
- @Insert
- void insert(Entity1 entity1);
-
- @Insert
- void insert(Child entity1);
- }
-
- @Dao
- interface EntityDao extends Entity1Dao {
- @Override
- @Query(SELECT_ALL)
- List<Entity1> allEntities();
-
- @Override
- @Query(SELECT_ALL)
- Flowable<List<Entity1>> flowable();
-
- @Override
- @Query(SELECT_ALL)
- LiveData<List<Entity1>> liveData();
-
- @Override
- @Query(SELECT_ALL)
- Maybe<List<Entity1>> maybe();
-
- @Override
- @Query(SELECT_ALL)
- Single<List<Entity1>> single();
-
- @Override
- @Query(SELECT_ALL)
- @SuppressWarnings(RoomWarnings.RELATION_QUERY_WITHOUT_TRANSACTION)
- List<Entity1WithChildren> withRelation();
-
- @Override
- @Query(SELECT_ALL)
- LivePagedListProvider<Integer, Entity1> pagedList();
-
- @Override
- @Query(SELECT_ALL)
- TiledDataSource<Entity1> dataSource();
- }
-
- @Dao
- interface TransactionDao extends Entity1Dao {
- @Override
- @Transaction
- @Query(SELECT_ALL)
- List<Entity1> allEntities();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- Flowable<List<Entity1>> flowable();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- LiveData<List<Entity1>> liveData();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- Maybe<List<Entity1>> maybe();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- Single<List<Entity1>> single();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- List<Entity1WithChildren> withRelation();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- LivePagedListProvider<Integer, Entity1> pagedList();
-
- @Override
- @Transaction
- @Query(SELECT_ALL)
- TiledDataSource<Entity1> dataSource();
- }
-
- @Database(version = 1, entities = {Entity1.class, Child.class}, exportSchema = false)
- abstract static class TransactionDb extends RoomDatabase {
- abstract EntityDao dao();
-
- abstract TransactionDao transactionDao();
-
- @Override
- public void beginTransaction() {
- super.beginTransaction();
- sStartedTransactionCount.incrementAndGet();
- }
- }
-}
diff --git a/android/arch/persistence/room/migration/Migration.java b/android/arch/persistence/room/migration/Migration.java
index d69ea0dc..907e624b 100644
--- a/android/arch/persistence/room/migration/Migration.java
+++ b/android/arch/persistence/room/migration/Migration.java
@@ -17,7 +17,6 @@
package android.arch.persistence.room.migration;
import android.arch.persistence.db.SupportSQLiteDatabase;
-import android.support.annotation.NonNull;
/**
* Base class for a database migration.
@@ -59,5 +58,5 @@ public abstract class Migration {
*
* @param database The database instance
*/
- public abstract void migrate(@NonNull SupportSQLiteDatabase database);
+ public abstract void migrate(SupportSQLiteDatabase database);
}
diff --git a/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java b/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
index d72cf8cb..1467a4f0 100644
--- a/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
+++ b/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
@@ -16,18 +16,13 @@
package android.arch.persistence.room.migration.bundle;
-import android.support.annotation.RestrictTo;
-
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
* Holds the information about a foreign key reference.
- *
- * @hide
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class ForeignKeyBundle {
@SerializedName("table")
private String mTable;
diff --git a/android/arch/persistence/room/paging/LimitOffsetDataSource.java b/android/arch/persistence/room/paging/LimitOffsetDataSource.java
index 2f9a8882..800514cc 100644
--- a/android/arch/persistence/room/paging/LimitOffsetDataSource.java
+++ b/android/arch/persistence/room/paging/LimitOffsetDataSource.java
@@ -49,13 +49,10 @@ public abstract class LimitOffsetDataSource<T> extends TiledDataSource<T> {
private final RoomDatabase mDb;
@SuppressWarnings("FieldCanBeLocal")
private final InvalidationTracker.Observer mObserver;
- private final boolean mInTransaction;
- protected LimitOffsetDataSource(RoomDatabase db, RoomSQLiteQuery query,
- boolean inTransaction, String... tables) {
+ protected LimitOffsetDataSource(RoomDatabase db, RoomSQLiteQuery query, String... tables) {
mDb = db;
mSourceQuery = query;
- mInTransaction = inTransaction;
mCountQuery = "SELECT COUNT(*) FROM ( " + mSourceQuery.getSql() + " )";
mLimitOffsetQuery = "SELECT * FROM ( " + mSourceQuery.getSql() + " ) LIMIT ? OFFSET ?";
mObserver = new InvalidationTracker.Observer(tables) {
@@ -101,30 +98,13 @@ public abstract class LimitOffsetDataSource<T> extends TiledDataSource<T> {
sqLiteQuery.copyArgumentsFrom(mSourceQuery);
sqLiteQuery.bindLong(sqLiteQuery.getArgCount() - 1, loadCount);
sqLiteQuery.bindLong(sqLiteQuery.getArgCount(), startPosition);
- if (mInTransaction) {
- mDb.beginTransaction();
- Cursor cursor = null;
- try {
- cursor = mDb.query(sqLiteQuery);
- List<T> rows = convertRows(cursor);
- mDb.setTransactionSuccessful();
- return rows;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- mDb.endTransaction();
- sqLiteQuery.release();
- }
- } else {
- Cursor cursor = mDb.query(sqLiteQuery);
- //noinspection TryFinallyCanBeTryWithResources
- try {
- return convertRows(cursor);
- } finally {
- cursor.close();
- sqLiteQuery.release();
- }
+ Cursor cursor = mDb.query(sqLiteQuery);
+
+ try {
+ return convertRows(cursor);
+ } finally {
+ cursor.close();
+ sqLiteQuery.release();
}
}
}
diff --git a/android/arch/persistence/room/util/StringUtil.java b/android/arch/persistence/room/util/StringUtil.java
index d01e3c53..bee05ddd 100644
--- a/android/arch/persistence/room/util/StringUtil.java
+++ b/android/arch/persistence/room/util/StringUtil.java
@@ -17,7 +17,6 @@
package android.arch.persistence.room.util;
import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
import android.util.Log;
import java.util.ArrayList;
@@ -25,14 +24,10 @@ import java.util.List;
import java.util.StringTokenizer;
/**
- * @hide
- *
* String utilities for Room
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@SuppressWarnings("WeakerAccess")
public class StringUtil {
-
- @SuppressWarnings("unused")
public static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
* Returns a new StringBuilder to be used while producing SQL queries.
diff --git a/android/content/ContentProvider.java b/android/content/ContentProvider.java
index cdeaea3e..5b2bf456 100644
--- a/android/content/ContentProvider.java
+++ b/android/content/ContentProvider.java
@@ -2099,7 +2099,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public static Uri maybeAddUserId(Uri uri, int userId) {
if (uri == null) return null;
if (userId != UserHandle.USER_CURRENT
- && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+ || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) {
if (!uriHasUserId(uri)) {
//We don't add the user Id if there's already one
Uri.Builder builder = uri.buildUpon();
diff --git a/android/content/ContentResolver.java b/android/content/ContentResolver.java
index 9ccc552f..02e70f55 100644
--- a/android/content/ContentResolver.java
+++ b/android/content/ContentResolver.java
@@ -47,6 +47,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.slice.Slice;
+import android.slice.SliceProvider;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -178,6 +180,8 @@ public abstract class ContentResolver {
public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
+ /** @hide */
+ public static final String SCHEME_SLICE = "slice";
public static final String SCHEME_CONTENT = "content";
public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
public static final String SCHEME_FILE = "file";
@@ -1718,6 +1722,36 @@ public abstract class ContentResolver {
}
/**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @hide
+ */
+ public final @Nullable Slice bindSlice(@NonNull Uri uri) {
+ Preconditions.checkNotNull(uri, "uri");
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(mPackageName, SliceProvider.METHOD_SLICE, null,
+ extras);
+ Bundle.setDefusable(res, true);
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
* Returns the content provider for the given content URI.
*
* @param uri The URI to a content provider
@@ -1725,7 +1759,7 @@ public abstract class ContentResolver {
* @hide
*/
public final IContentProvider acquireProvider(Uri uri) {
- if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+ if (!SCHEME_CONTENT.equals(uri.getScheme()) && !SCHEME_SLICE.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
diff --git a/android/content/Intent.java b/android/content/Intent.java
index e47de752..c9ad9519 100644
--- a/android/content/Intent.java
+++ b/android/content/Intent.java
@@ -53,7 +53,6 @@ import android.provider.OpenableColumns;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
@@ -9372,57 +9371,6 @@ public class Intent implements Parcelable, Cloneable {
}
}
- /** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId, boolean secure, boolean comp,
- boolean extras, boolean clip) {
- long token = proto.start(fieldId);
- if (mAction != null) {
- proto.write(IntentProto.ACTION, mAction);
- }
- if (mCategories != null) {
- for (String category : mCategories) {
- proto.write(IntentProto.CATEGORIES, category);
- }
- }
- if (mData != null) {
- proto.write(IntentProto.DATA, secure ? mData.toSafeString() : mData.toString());
- }
- if (mType != null) {
- proto.write(IntentProto.TYPE, mType);
- }
- if (mFlags != 0) {
- proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags));
- }
- if (mPackage != null) {
- proto.write(IntentProto.PACKAGE, mPackage);
- }
- if (comp && mComponent != null) {
- proto.write(IntentProto.COMPONENT, mComponent.flattenToShortString());
- }
- if (mSourceBounds != null) {
- proto.write(IntentProto.SOURCE_BOUNDS, mSourceBounds.toShortString());
- }
- if (mClipData != null) {
- StringBuilder b = new StringBuilder();
- if (clip) {
- mClipData.toShortString(b);
- } else {
- mClipData.toShortStringShortItems(b, false);
- }
- proto.write(IntentProto.CLIP_DATA, b.toString());
- }
- if (extras && mExtras != null) {
- proto.write(IntentProto.EXTRAS, mExtras.toShortString());
- }
- if (mContentUserHint != 0) {
- proto.write(IntentProto.CONTENT_USER_HINT, mContentUserHint);
- }
- if (mSelector != null) {
- proto.write(IntentProto.SELECTOR, mSelector.toShortString(secure, comp, extras, clip));
- }
- proto.end(token);
- }
-
/**
* Call {@link #toUri} with 0 flags.
* @deprecated Use {@link #toUri} instead.
diff --git a/android/content/IntentFilter.java b/android/content/IntentFilter.java
index a957aed8..c9bce530 100644
--- a/android/content/IntentFilter.java
+++ b/android/content/IntentFilter.java
@@ -26,7 +26,6 @@ import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Log;
import android.util.Printer;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
@@ -919,15 +918,6 @@ public class IntentFilter implements Parcelable {
dest.writeInt(mPort);
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- // The original host information is already contained in host and wild, no output now.
- proto.write(AuthorityEntryProto.HOST, mHost);
- proto.write(AuthorityEntryProto.WILD, mWild);
- proto.write(AuthorityEntryProto.PORT, mPort);
- proto.end(token);
- }
-
public String getHost() {
return mOrigHost;
}
@@ -1749,59 +1739,6 @@ public class IntentFilter implements Parcelable {
}
}
- /** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- if (mActions.size() > 0) {
- Iterator<String> it = mActions.iterator();
- while (it.hasNext()) {
- proto.write(IntentFilterProto.ACTIONS, it.next());
- }
- }
- if (mCategories != null) {
- Iterator<String> it = mCategories.iterator();
- while (it.hasNext()) {
- proto.write(IntentFilterProto.CATEGORIES, it.next());
- }
- }
- if (mDataSchemes != null) {
- Iterator<String> it = mDataSchemes.iterator();
- while (it.hasNext()) {
- proto.write(IntentFilterProto.DATA_SCHEMES, it.next());
- }
- }
- if (mDataSchemeSpecificParts != null) {
- Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
- while (it.hasNext()) {
- it.next().writeToProto(proto, IntentFilterProto.DATA_SCHEME_SPECS);
- }
- }
- if (mDataAuthorities != null) {
- Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
- while (it.hasNext()) {
- it.next().writeToProto(proto, IntentFilterProto.DATA_AUTHORITIES);
- }
- }
- if (mDataPaths != null) {
- Iterator<PatternMatcher> it = mDataPaths.iterator();
- while (it.hasNext()) {
- it.next().writeToProto(proto, IntentFilterProto.DATA_PATHS);
- }
- }
- if (mDataTypes != null) {
- Iterator<String> it = mDataTypes.iterator();
- while (it.hasNext()) {
- proto.write(IntentFilterProto.DATA_TYPES, it.next());
- }
- }
- if (mPriority != 0 || mHasPartialTypes) {
- proto.write(IntentFilterProto.PRIORITY, mPriority);
- proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, mHasPartialTypes);
- }
- proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
- proto.end(token);
- }
-
public void dump(Printer du, String prefix) {
StringBuilder sb = new StringBuilder(256);
if (mActions.size() > 0) {
diff --git a/android/content/pm/FeatureInfo.java b/android/content/pm/FeatureInfo.java
index ff9fd8ec..9ee6fa24 100644
--- a/android/content/pm/FeatureInfo.java
+++ b/android/content/pm/FeatureInfo.java
@@ -18,7 +18,6 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.proto.ProtoOutputStream;
/**
* Definition of a single optional hardware or software feature of an Android
@@ -114,18 +113,6 @@ public class FeatureInfo implements Parcelable {
dest.writeInt(flags);
}
- /** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- if (name != null) {
- proto.write(FeatureInfoProto.NAME, name);
- }
- proto.write(FeatureInfoProto.VERSION, version);
- proto.write(FeatureInfoProto.GLES_VERSION, getGlEsVersion());
- proto.write(FeatureInfoProto.FLAGS, flags);
- proto.end(token);
- }
-
public static final Creator<FeatureInfo> CREATOR = new Creator<FeatureInfo>() {
@Override
public FeatureInfo createFromParcel(Parcel source) {
diff --git a/android/content/pm/LauncherApps.java b/android/content/pm/LauncherApps.java
index b94a410b..aa9562ff 100644
--- a/android/content/pm/LauncherApps.java
+++ b/android/content/pm/LauncherApps.java
@@ -20,8 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@@ -37,10 +37,10 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -282,27 +282,12 @@ public class LauncherApps {
public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
/**
- * @hide include all pinned shortcuts by any launchers, not just by the caller,
- * in the result.
- * If the caller doesn't havve the {@link android.Manifest.permission#ACCESS_SHORTCUTS}
- * permission, this flag will be ignored.
- */
- @TestApi
- public static final int FLAG_MATCH_ALL_PINNED = 1 << 10;
-
- /**
- * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST
+ * Does not retrieve CHOOSER only shortcuts.
+ * TODO: Add another flag for MATCH_ALL_PINNED
* @hide
*/
public static final int FLAG_MATCH_ALL_KINDS =
- FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST;
-
- /**
- * FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST | FLAG_MATCH_ALL_PINNED
- * @hide
- */
- public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED =
- FLAG_MATCH_ALL_KINDS | FLAG_MATCH_ALL_PINNED;
+ FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
/** @hide kept for unit tests */
@Deprecated
@@ -334,7 +319,6 @@ public class LauncherApps {
FLAG_MATCH_PINNED,
FLAG_MATCH_MANIFEST,
FLAG_GET_KEY_FIELDS_ONLY,
- FLAG_MATCH_MANIFEST,
})
@Retention(RetentionPolicy.SOURCE)
public @interface QueryFlags {}
@@ -694,21 +678,6 @@ public class LauncherApps {
}
}
- private List<ShortcutInfo> maybeUpdateDisabledMessage(List<ShortcutInfo> shortcuts) {
- if (shortcuts == null) {
- return null;
- }
- for (int i = shortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = shortcuts.get(i);
- final String message = ShortcutInfo.getDisabledReasonForRestoreIssue(mContext,
- si.getDisabledReason());
- if (message != null) {
- si.setDisabledMessage(message);
- }
- }
- return shortcuts;
- }
-
/**
* Returns {@link ShortcutInfo}s that match {@code query}.
*
@@ -729,16 +698,10 @@ public class LauncherApps {
@NonNull UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
- // Note this is the only case we need to update the disabled message for shortcuts
- // that weren't restored.
- // The restore problem messages are only shown by the user, and publishers will never
- // see them. The only other API that the launcher gets shortcuts is the shortcut
- // changed callback, but that only returns shortcuts with the "key" information, so
- // that won't return disabled message.
- return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
+ return mService.getShortcuts(mContext.getPackageName(),
query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
query.mQueryFlags, user)
- .getList());
+ .getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/android/content/pm/PackageManagerInternal.java b/android/content/pm/PackageManagerInternal.java
index 143c51da..be7f921e 100644
--- a/android/content/pm/PackageManagerInternal.java
+++ b/android/content/pm/PackageManagerInternal.java
@@ -467,7 +467,6 @@ public abstract class PackageManagerInternal {
/** Updates the flags for the given permission. */
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
- /** Returns a PermissionGroup. */
- public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP(
- @NonNull String groupName);
+ /** temporary until mPermissionTrees is moved to PermissionManager */
+ public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
}
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
index ad36139a..6c7c8a07 100644
--- a/android/content/pm/PackageParser.java
+++ b/android/content/pm/PackageParser.java
@@ -3711,15 +3711,17 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
}
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
- false)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+ if (false) {
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+ false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
- // A heavy-weight application can not be in a custom process.
- // We can do direct compare because we intern all strings.
- if (ai.processName != null && !ai.processName.equals(ai.packageName)) {
- outError[0] = "cantSaveState applications can not use custom processes";
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (ai.processName != null && ai.processName != ai.packageName) {
+ outError[0] = "cantSaveState applications can not use custom processes";
+ }
}
}
}
@@ -6847,11 +6849,6 @@ public class PackageParser {
dest.writeParcelable(group, flags);
}
- /** @hide */
- public boolean isAppOp() {
- return info.isAppOp();
- }
-
private Permission(Parcel in) {
super(in);
final ClassLoader boot = Object.class.getClassLoader();
diff --git a/android/content/pm/PermissionInfo.java b/android/content/pm/PermissionInfo.java
index 5dd7aeda..b45c26ce 100644
--- a/android/content/pm/PermissionInfo.java
+++ b/android/content/pm/PermissionInfo.java
@@ -353,11 +353,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
return size;
}
- /** @hide */
- public boolean isAppOp() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
- }
-
public static final Creator<PermissionInfo> CREATOR =
new Creator<PermissionInfo>() {
@Override
diff --git a/android/content/pm/ShortcutInfo.java b/android/content/pm/ShortcutInfo.java
index 9ff07757..6b9c7537 100644
--- a/android/content/pm/ShortcutInfo.java
+++ b/android/content/pm/ShortcutInfo.java
@@ -18,7 +18,6 @@ package android.content.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.TaskStackBuilder;
import android.content.ComponentName;
@@ -101,13 +100,6 @@ public final class ShortcutInfo implements Parcelable {
/** @hide When this is set, the bitmap icon is waiting to be saved. */
public static final int FLAG_ICON_FILE_PENDING_SAVE = 1 << 11;
- /**
- * "Shadow" shortcuts are the ones that are restored, but the owner package hasn't been
- * installed yet.
- * @hide
- */
- public static final int FLAG_SHADOW = 1 << 12;
-
/** @hide */
@IntDef(flag = true,
value = {
@@ -166,124 +158,6 @@ public final class ShortcutInfo implements Parcelable {
public @interface CloneFlags {}
/**
- * Shortcut is not disabled.
- */
- public static final int DISABLED_REASON_NOT_DISABLED = 0;
-
- /**
- * Shortcut has been disabled by the publisher app with the
- * {@link ShortcutManager#disableShortcuts(List)} API.
- */
- public static final int DISABLED_REASON_BY_APP = 1;
-
- /**
- * Shortcut has been disabled due to changes to the publisher app. (e.g. a manifest shortcut
- * no longer exists.)
- */
- public static final int DISABLED_REASON_APP_CHANGED = 2;
-
- /**
- * A disabled reason that's equal to or bigger than this is due to backup and restore issue.
- * A shortcut with such a reason wil be visible to the launcher, but not to the publisher.
- * ({@link #isVisibleToPublisher()} will be false.)
- */
- private static final int DISABLED_REASON_RESTORE_ISSUE_START = 100;
-
- /**
- * Shortcut has been restored from the previous device, but the publisher app on the current
- * device is of a lower version. The shortcut will not be usable until the app is upgraded to
- * the same version or higher.
- */
- public static final int DISABLED_REASON_VERSION_LOWER = 100;
-
- /**
- * Shortcut has not been restored because the publisher app does not support backup and restore.
- */
- public static final int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 101;
-
- /**
- * Shortcut has not been restored because the publisher app's signature has changed.
- */
- public static final int DISABLED_REASON_SIGNATURE_MISMATCH = 102;
-
- /**
- * Shortcut has not been restored for unknown reason.
- */
- public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
-
- /** @hide */
- @IntDef(value = {
- DISABLED_REASON_NOT_DISABLED,
- DISABLED_REASON_BY_APP,
- DISABLED_REASON_APP_CHANGED,
- DISABLED_REASON_VERSION_LOWER,
- DISABLED_REASON_BACKUP_NOT_SUPPORTED,
- DISABLED_REASON_SIGNATURE_MISMATCH,
- DISABLED_REASON_OTHER_RESTORE_ISSUE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface DisabledReason{}
-
- /**
- * Return a label for disabled reasons, which are *not* supposed to be shown to the user.
- * @hide
- */
- public static String getDisabledReasonDebugString(@DisabledReason int disabledReason) {
- switch (disabledReason) {
- case DISABLED_REASON_NOT_DISABLED:
- return "[Not disabled]";
- case DISABLED_REASON_BY_APP:
- return "[Disabled: by app]";
- case DISABLED_REASON_APP_CHANGED:
- return "[Disabled: app changed]";
- case DISABLED_REASON_VERSION_LOWER:
- return "[Disabled: lower version]";
- case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
- return "[Disabled: backup not supported]";
- case DISABLED_REASON_SIGNATURE_MISMATCH:
- return "[Disabled: signature mismatch]";
- case DISABLED_REASON_OTHER_RESTORE_ISSUE:
- return "[Disabled: unknown restore issue]";
- }
- return "[Disabled: unknown reason:" + disabledReason + "]";
- }
-
- /**
- * Return a label for a disabled reason for shortcuts that are disabled due to a backup and
- * restore issue. If the reason is not due to backup & restore, then it'll return null.
- *
- * This method returns localized, user-facing strings, which will be returned by
- * {@link #getDisabledMessage()}.
- *
- * @hide
- */
- public static String getDisabledReasonForRestoreIssue(Context context,
- @DisabledReason int disabledReason) {
- final Resources res = context.getResources();
-
- switch (disabledReason) {
- case DISABLED_REASON_VERSION_LOWER:
- return res.getString(
- com.android.internal.R.string.shortcut_restored_on_lower_version);
- case DISABLED_REASON_BACKUP_NOT_SUPPORTED:
- return res.getString(
- com.android.internal.R.string.shortcut_restore_not_supported);
- case DISABLED_REASON_SIGNATURE_MISMATCH:
- return res.getString(
- com.android.internal.R.string.shortcut_restore_signature_mismatch);
- case DISABLED_REASON_OTHER_RESTORE_ISSUE:
- return res.getString(
- com.android.internal.R.string.shortcut_restore_unknown_issue);
- }
- return null;
- }
-
- /** @hide */
- public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReason) {
- return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
- }
-
- /**
* Shortcut category for messaging related actions, such as chat.
*/
public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -366,11 +240,6 @@ public final class ShortcutInfo implements Parcelable {
private final int mUserId;
- /** @hide */
- public static final int VERSION_CODE_UNKNOWN = -1;
-
- private int mDisabledReason;
-
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -483,7 +352,6 @@ public final class ShortcutInfo implements Parcelable {
mActivity = source.mActivity;
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
- mDisabledReason = source.mDisabledReason;
// Just always keep it since it's cheep.
mIconResId = source.mIconResId;
@@ -747,23 +615,13 @@ public final class ShortcutInfo implements Parcelable {
/**
* @hide
- *
- * @isUpdating set true if it's "update", as opposed to "replace".
*/
- public void ensureUpdatableWith(ShortcutInfo source, boolean isUpdating) {
- if (isUpdating) {
- Preconditions.checkState(isVisibleToPublisher(),
- "[Framework BUG] Invisible shortcuts can't be updated");
- }
+ public void ensureUpdatableWith(ShortcutInfo source) {
Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
Preconditions.checkState(mId.equals(source.mId), "ID must match");
Preconditions.checkState(mPackageName.equals(source.mPackageName),
"Package name must match");
-
- if (isVisibleToPublisher()) {
- // Don't do this check for restore-blocked shortcuts.
- Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
- }
+ Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
}
/**
@@ -780,7 +638,7 @@ public final class ShortcutInfo implements Parcelable {
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
- ensureUpdatableWith(source, /*isUpdating=*/ true);
+ ensureUpdatableWith(source);
if (source.mActivity != null) {
mActivity = source.mActivity;
@@ -1311,19 +1169,6 @@ public final class ShortcutInfo implements Parcelable {
return mDisabledMessageResId;
}
- /** @hide */
- public void setDisabledReason(@DisabledReason int reason) {
- mDisabledReason = reason;
- }
-
- /**
- * Returns why a shortcut has been disabled.
- */
- @DisabledReason
- public int getDisabledReason() {
- return mDisabledReason;
- }
-
/**
* Return the shortcut's categories.
*
@@ -1558,21 +1403,6 @@ public final class ShortcutInfo implements Parcelable {
return hasFlags(FLAG_IMMUTABLE);
}
- /** @hide */
- public boolean isDynamicVisible() {
- return isDynamic() && isVisibleToPublisher();
- }
-
- /** @hide */
- public boolean isPinnedVisible() {
- return isPinned() && isVisibleToPublisher();
- }
-
- /** @hide */
- public boolean isManifestVisible() {
- return isDeclaredInManifest() && isVisibleToPublisher();
- }
-
/**
* Return if a shortcut is immutable, in which case it cannot be modified with any of
* {@link ShortcutManager} APIs.
@@ -1661,18 +1491,6 @@ public final class ShortcutInfo implements Parcelable {
}
/**
- * When the system wasn't able to restore a shortcut, it'll still be registered to the system
- * but disabled, and such shortcuts will not be visible to the publisher. They're still visible
- * to launchers though.
- *
- * @hide
- */
- @TestApi
- public boolean isVisibleToPublisher() {
- return !isDisabledForRestoreIssue(mDisabledReason);
- }
-
- /**
* Return whether a shortcut only contains "key" information only or not. If true, only the
* following fields are available.
* <ul>
@@ -1850,7 +1668,6 @@ public final class ShortcutInfo implements Parcelable {
mFlags = source.readInt();
mIconResId = source.readInt();
mLastChangedTimestamp = source.readLong();
- mDisabledReason = source.readInt();
if (source.readInt() == 0) {
return; // key information only.
@@ -1894,7 +1711,6 @@ public final class ShortcutInfo implements Parcelable {
dest.writeInt(mFlags);
dest.writeInt(mIconResId);
dest.writeLong(mLastChangedTimestamp);
- dest.writeInt(mDisabledReason);
if (hasKeyFieldsOnly()) {
dest.writeInt(0);
@@ -1992,11 +1808,6 @@ public final class ShortcutInfo implements Parcelable {
sb.append(", flags=0x");
sb.append(Integer.toHexString(mFlags));
sb.append(" [");
- if ((mFlags & FLAG_SHADOW) != 0) {
- // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
- // we don't have an isXxx for this.
- sb.append("Sdw");
- }
if (!isEnabled()) {
sb.append("Dis");
}
@@ -2037,9 +1848,7 @@ public final class ShortcutInfo implements Parcelable {
sb.append("packageName=");
sb.append(mPackageName);
- addIndentOrComma(sb, indent);
-
- sb.append("activity=");
+ sb.append(", activity=");
sb.append(mActivity);
addIndentOrComma(sb, indent);
@@ -2074,11 +1883,6 @@ public final class ShortcutInfo implements Parcelable {
addIndentOrComma(sb, indent);
- sb.append("disabledReason=");
- sb.append(getDisabledReasonDebugString(mDisabledReason));
-
- addIndentOrComma(sb, indent);
-
sb.append("categories=");
sb.append(mCategories);
@@ -2149,7 +1953,7 @@ public final class ShortcutInfo implements Parcelable {
CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
- int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) {
+ int flags, int iconResId, String iconResName, String bitmapPath) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2174,6 +1978,5 @@ public final class ShortcutInfo implements Parcelable {
mIconResId = iconResId;
mIconResName = iconResName;
mBitmapPath = bitmapPath;
- mDisabledReason = disabledReason;
}
}
diff --git a/android/content/pm/ShortcutServiceInternal.java b/android/content/pm/ShortcutServiceInternal.java
index 7fc25d82..7b7d8ae4 100644
--- a/android/content/pm/ShortcutServiceInternal.java
+++ b/android/content/pm/ShortcutServiceInternal.java
@@ -46,7 +46,7 @@ public abstract class ShortcutServiceInternal {
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
- int userId, int callingPid, int callingUid);
+ int userId);
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@@ -58,8 +58,7 @@ public abstract class ShortcutServiceInternal {
public abstract Intent[] createShortcutIntents(
int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- int callingPid, int callingUid);
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
@@ -71,7 +70,7 @@ public abstract class ShortcutServiceInternal {
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract boolean hasShortcutHostPermission(int launcherUserId,
- @NonNull String callingPackage, int callingPid, int callingUid);
+ @NonNull String callingPackage);
public abstract boolean requestPinAppWidget(@NonNull String callingPackage,
@NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
diff --git a/android/content/res/ResourcesImpl.java b/android/content/res/ResourcesImpl.java
index 386239cf..a8b8c4b5 100644
--- a/android/content/res/ResourcesImpl.java
+++ b/android/content/res/ResourcesImpl.java
@@ -796,7 +796,7 @@ public class ResourcesImpl {
dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
is.close();
}
- } catch (Exception | StackOverflowError e) {
+ } catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
diff --git a/android/database/SQLiteDatabaseIoPerfTest.java b/android/database/SQLiteDatabaseIoPerfTest.java
deleted file mode 100644
index 7c5316d2..00000000
--- a/android/database/SQLiteDatabaseIoPerfTest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.database;
-
-import android.app.Activity;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.util.Preconditions;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Performance tests for measuring amount of data written during typical DB operations
- *
- * <p>To run: bit CorePerfTests:android.database.SQLiteDatabaseIoPerfTest
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class SQLiteDatabaseIoPerfTest {
- private static final String TAG = "SQLiteDatabaseIoPerfTest";
- private static final String DB_NAME = "db_io_perftest";
- private static final int DEFAULT_DATASET_SIZE = 500;
-
- private Long mWriteBytes;
-
- private SQLiteDatabase mDatabase;
- private Context mContext;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mContext.deleteDatabase(DB_NAME);
- mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
- mDatabase.execSQL("CREATE TABLE T1 "
- + "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)");
- }
-
- @After
- public void tearDown() {
- mDatabase.close();
- mContext.deleteDatabase(DB_NAME);
- }
-
- @Test
- public void testDatabaseModifications() {
- startMeasuringWrites();
- ContentValues cv = new ContentValues();
- String[] whereArg = new String[1];
- for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
- cv.put("_ID", i);
- cv.put("COL_A", i);
- cv.put("COL_B", "NewValue");
- cv.put("COL_C", 1.0);
- assertEquals(i, mDatabase.insert("T1", null, cv));
- }
- cv = new ContentValues();
- for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
- cv.put("COL_B", "UpdatedValue");
- cv.put("COL_C", 1.1);
- whereArg[0] = String.valueOf(i);
- assertEquals(1, mDatabase.update("T1", cv, "_ID=?", whereArg));
- }
- for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
- whereArg[0] = String.valueOf(i);
- assertEquals(1, mDatabase.delete("T1", "_ID=?", whereArg));
- }
- // Make sure all changes are written to disk
- mDatabase.close();
- long bytes = endMeasuringWrites();
- sendResults("testDatabaseModifications" , bytes);
- }
-
- @Test
- public void testInsertsWithTransactions() {
- startMeasuringWrites();
- final int txSize = 10;
- ContentValues cv = new ContentValues();
- for (int i = 0; i < DEFAULT_DATASET_SIZE * 5; i++) {
- if (i % txSize == 0) {
- mDatabase.beginTransaction();
- }
- if (i % txSize == txSize-1) {
- mDatabase.setTransactionSuccessful();
- mDatabase.endTransaction();
-
- }
- cv.put("_ID", i);
- cv.put("COL_A", i);
- cv.put("COL_B", "NewValue");
- cv.put("COL_C", 1.0);
- assertEquals(i, mDatabase.insert("T1", null, cv));
- }
- // Make sure all changes are written to disk
- mDatabase.close();
- long bytes = endMeasuringWrites();
- sendResults("testInsertsWithTransactions" , bytes);
- }
-
- private void startMeasuringWrites() {
- Preconditions.checkState(mWriteBytes == null, "Measurement already started");
- mWriteBytes = getIoStats().get("write_bytes");
- }
-
- private long endMeasuringWrites() {
- Preconditions.checkState(mWriteBytes != null, "Measurement wasn't started");
- Long newWriteBytes = getIoStats().get("write_bytes");
- return newWriteBytes - mWriteBytes;
- }
-
- private void sendResults(String testName, long writeBytes) {
- Log.i(TAG, testName + " write_bytes: " + writeBytes);
- Bundle status = new Bundle();
- status.putLong("write_bytes", writeBytes);
- InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
- }
-
- private static Map<String, Long> getIoStats() {
- String ioStat = "/proc/self/io";
- Map<String, Long> results = new ArrayMap<>();
- try {
- List<String> lines = Files.readAllLines(new File(ioStat).toPath());
- for (String line : lines) {
- line = line.trim();
- String[] split = line.split(":");
- if (split.length == 2) {
- try {
- String key = split[0].trim();
- Long value = Long.valueOf(split[1].trim());
- results.put(key, value);
- } catch (NumberFormatException e) {
- Log.e(TAG, "Cannot parse number from " + line);
- }
- } else if (line.isEmpty()) {
- Log.e(TAG, "Cannot parse line " + line);
- }
- }
- } catch (IOException e) {
- Log.e(TAG, "Can't read: " + ioStat, e);
- }
- return results;
- }
-
-}
diff --git a/android/database/SQLiteDatabasePerfTest.java b/android/database/SQLiteDatabasePerfTest.java
deleted file mode 100644
index 7a32c0cc..00000000
--- a/android/database/SQLiteDatabasePerfTest.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.database;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Random;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Performance tests for typical CRUD operations and loading rows into the Cursor
- *
- * <p>To run: bit CorePerfTests:android.database.SQLiteDatabasePerfTest
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class SQLiteDatabasePerfTest {
- // TODO b/64262688 Add Concurrency tests to compare WAL vs DELETE read/write
- private static final String DB_NAME = "dbperftest";
- private static final int DEFAULT_DATASET_SIZE = 1000;
-
- @Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- private SQLiteDatabase mDatabase;
- private Context mContext;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mContext.deleteDatabase(DB_NAME);
- mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
- mDatabase.execSQL("CREATE TABLE T1 "
- + "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)");
- mDatabase.execSQL("CREATE TABLE T2 ("
- + "_ID INTEGER PRIMARY KEY, COL_A VARCHAR(100), T1_ID INTEGER,"
- + "FOREIGN KEY(T1_ID) REFERENCES T1 (_ID))");
- }
-
- @After
- public void tearDown() {
- mDatabase.close();
- mContext.deleteDatabase(DB_NAME);
- }
-
- @Test
- public void testSelect() {
- insertT1TestDataSet();
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-
- Random rnd = new Random(0);
- while (state.keepRunning()) {
- int index = rnd.nextInt(DEFAULT_DATASET_SIZE);
- try (Cursor cursor = mDatabase.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 "
- + "WHERE _ID=?", new String[]{String.valueOf(index)})) {
- assertTrue(cursor.moveToNext());
- assertEquals(index, cursor.getInt(0));
- assertEquals(index, cursor.getInt(1));
- assertEquals("T1Value" + index, cursor.getString(2));
- assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
- }
- }
- }
-
- @Test
- public void testSelectMultipleRows() {
- insertT1TestDataSet();
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- Random rnd = new Random(0);
- final int querySize = 50;
- while (state.keepRunning()) {
- int index = rnd.nextInt(DEFAULT_DATASET_SIZE - querySize - 1);
- try (Cursor cursor = mDatabase.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 "
- + "WHERE _ID BETWEEN ? and ? ORDER BY _ID",
- new String[]{String.valueOf(index), String.valueOf(index + querySize - 1)})) {
- int i = 0;
- while(cursor.moveToNext()) {
- assertEquals(index, cursor.getInt(0));
- assertEquals(index, cursor.getInt(1));
- assertEquals("T1Value" + index, cursor.getString(2));
- assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
- index++;
- i++;
- }
- assertEquals(querySize, i);
- }
- }
- }
-
- @Test
- public void testInnerJoin() {
- mDatabase.setForeignKeyConstraintsEnabled(true);
- mDatabase.beginTransaction();
- insertT1TestDataSet();
- insertT2TestDataSet();
- mDatabase.setTransactionSuccessful();
- mDatabase.endTransaction();
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-
- Random rnd = new Random(0);
- while (state.keepRunning()) {
- int index = rnd.nextInt(1000);
- try (Cursor cursor = mDatabase.rawQuery(
- "SELECT T1._ID, T1.COL_A, T1.COL_B, T1.COL_C, T2.COL_A FROM T1 "
- + "INNER JOIN T2 on T2.T1_ID=T1._ID WHERE T1._ID = ?",
- new String[]{String.valueOf(index)})) {
- assertTrue(cursor.moveToNext());
- assertEquals(index, cursor.getInt(0));
- assertEquals(index, cursor.getInt(1));
- assertEquals("T1Value" + index, cursor.getString(2));
- assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
- assertEquals("T2Value" + index, cursor.getString(4));
- }
- }
- }
-
- @Test
- public void testInsert() {
- insertT1TestDataSet();
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-
- ContentValues cv = new ContentValues();
- cv.put("_ID", DEFAULT_DATASET_SIZE);
- cv.put("COL_B", "NewValue");
- cv.put("COL_C", 1.1);
- String[] deleteArgs = new String[]{String.valueOf(DEFAULT_DATASET_SIZE)};
- while (state.keepRunning()) {
- assertEquals(DEFAULT_DATASET_SIZE, mDatabase.insert("T1", null, cv));
- state.pauseTiming();
- assertEquals(1, mDatabase.delete("T1", "_ID=?", deleteArgs));
- state.resumeTiming();
- }
- }
-
- @Test
- public void testDelete() {
- insertT1TestDataSet();
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- String[] deleteArgs = new String[]{String.valueOf(DEFAULT_DATASET_SIZE)};
- Object[] insertsArgs = new Object[]{DEFAULT_DATASET_SIZE, DEFAULT_DATASET_SIZE,
- "ValueToDelete", 1.1};
-
- while (state.keepRunning()) {
- state.pauseTiming();
- mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)", insertsArgs);
- state.resumeTiming();
- assertEquals(1, mDatabase.delete("T1", "_ID=?", deleteArgs));
- }
- }
-
- @Test
- public void testUpdate() {
- insertT1TestDataSet();
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-
- Random rnd = new Random(0);
- int i = 0;
- ContentValues cv = new ContentValues();
- String[] argArray = new String[1];
- while (state.keepRunning()) {
- int id = rnd.nextInt(DEFAULT_DATASET_SIZE);
- cv.put("COL_A", i);
- cv.put("COL_B", "UpdatedValue");
- cv.put("COL_C", i);
- argArray[0] = String.valueOf(id);
- assertEquals(1, mDatabase.update("T1", cv, "_ID=?", argArray));
- i++;
- }
- }
-
- private void insertT1TestDataSet() {
- mDatabase.beginTransaction();
- for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
- mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)",
- new Object[]{i, i, "T1Value" + i, i * 1.1});
- }
- mDatabase.setTransactionSuccessful();
- mDatabase.endTransaction();
- }
-
- private void insertT2TestDataSet() {
- mDatabase.beginTransaction();
- for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
- mDatabase.execSQL("INSERT INTO T2 VALUES (?, ?, ?)",
- new Object[]{i, "T2Value" + i, i});
- }
- mDatabase.setTransactionSuccessful();
- mDatabase.endTransaction();
- }
-}
-
diff --git a/android/graphics/Bitmap.java b/android/graphics/Bitmap.java
index 0072012f..57c75490 100644
--- a/android/graphics/Bitmap.java
+++ b/android/graphics/Bitmap.java
@@ -21,7 +21,6 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1234,7 +1233,6 @@ public final class Bitmap implements Parcelable {
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
- @WorkerThread
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
checkRecycled("Can't compress a recycled bitmap");
// do explicit check before calling the native method
diff --git a/android/graphics/BitmapFactory.java b/android/graphics/BitmapFactory.java
index f5bf754a..ffb39e33 100644
--- a/android/graphics/BitmapFactory.java
+++ b/android/graphics/BitmapFactory.java
@@ -354,7 +354,6 @@ public class BitmapFactory {
* decode, in the case of which a more accurate, but slightly slower,
* IDCT method will be used instead.
*/
- @Deprecated
public boolean inPreferQualityOverSpeed;
/**
@@ -413,7 +412,6 @@ public class BitmapFactory {
* can check, inbetween the bounds decode and the image decode, to see
* if the operation is canceled.
*/
- @Deprecated
public boolean mCancel;
/**
@@ -428,7 +426,6 @@ public class BitmapFactory {
* or if inJustDecodeBounds is true, will set outWidth/outHeight
* to -1
*/
- @Deprecated
public void requestCancelDecode() {
mCancel = true;
}
diff --git a/android/media/AudioAttributes.java b/android/media/AudioAttributes.java
index 20405d3b..26ead3d1 100644
--- a/android/media/AudioAttributes.java
+++ b/android/media/AudioAttributes.java
@@ -202,22 +202,6 @@ public final class AudioAttributes implements Parcelable {
* @see #SUPPRESSIBLE_USAGES
*/
public final static int SUPPRESSIBLE_NEVER = 3;
- /**
- * @hide
- * Denotes a usage for alarms,
- * will be muted when the Zen mode doesn't allow alarms
- * @see #SUPPRESSIBLE_USAGES
- */
- public final static int SUPPRESSIBLE_ALARM = 4;
- /**
- * @hide
- * Denotes a usage for all other sounds not caught in SUPPRESSIBLE_NOTIFICATION,
- * SUPPRESSIBLE_CALL,SUPPRESSIBLE_NEVER or SUPPRESSIBLE_ALARM.
- * This includes media, system, game, navigation, the assistant, and more.
- * These will be muted when the Zen mode doesn't allow media/system/other.
- * @see #SUPPRESSIBLE_USAGES
- */
- public final static int SUPPRESSIBLE_MEDIA_SYSTEM_OTHER = 5;
/**
* @hide
@@ -237,13 +221,6 @@ public final class AudioAttributes implements Parcelable {
SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT, SUPPRESSIBLE_NOTIFICATION);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_ACCESSIBILITY, SUPPRESSIBLE_NEVER);
SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION, SUPPRESSIBLE_NEVER);
- SUPPRESSIBLE_USAGES.put(USAGE_ALARM, SUPPRESSIBLE_ALARM);
- SUPPRESSIBLE_USAGES.put(USAGE_MEDIA, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
- SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
- SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
- SUPPRESSIBLE_USAGES.put(USAGE_GAME, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
- SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
- SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
}
/**
diff --git a/android/media/MediaMetadataRetriever.java b/android/media/MediaMetadataRetriever.java
index 760cc49b..4ea4e381 100644
--- a/android/media/MediaMetadataRetriever.java
+++ b/android/media/MediaMetadataRetriever.java
@@ -395,7 +395,7 @@ public class MediaMetadataRetriever
* @see #getFrameAtTime(long, int)
*/
/* Do not change these option values without updating their counterparts
- * in include/media/MediaSource.h!
+ * in include/media/stagefright/MediaSource.h!
*/
/**
* This option is used with {@link #getFrameAtTime(long, int)} to retrieve
diff --git a/android/media/MediaRecorder.java b/android/media/MediaRecorder.java
index 76784904..59a124fa 100644
--- a/android/media/MediaRecorder.java
+++ b/android/media/MediaRecorder.java
@@ -917,7 +917,7 @@ public class MediaRecorder
*/
public void setNextOutputFile(File file) throws IOException
{
- RandomAccessFile f = new RandomAccessFile(file, "rw");
+ RandomAccessFile f = new RandomAccessFile(file, "rws");
try {
_setNextOutputFile(f.getFD());
} finally {
@@ -942,7 +942,7 @@ public class MediaRecorder
public void prepare() throws IllegalStateException, IOException
{
if (mPath != null) {
- RandomAccessFile file = new RandomAccessFile(mPath, "rw");
+ RandomAccessFile file = new RandomAccessFile(mPath, "rws");
try {
_setOutputFile(file.getFD());
} finally {
@@ -951,7 +951,7 @@ public class MediaRecorder
} else if (mFd != null) {
_setOutputFile(mFd);
} else if (mFile != null) {
- RandomAccessFile file = new RandomAccessFile(mFile, "rw");
+ RandomAccessFile file = new RandomAccessFile(mFile, "rws");
try {
_setOutputFile(file.getFD());
} finally {
diff --git a/android/media/tv/TvInputManager.java b/android/media/tv/TvInputManager.java
index fd1f2cf6..d7a9edef 100644
--- a/android/media/tv/TvInputManager.java
+++ b/android/media/tv/TvInputManager.java
@@ -2590,9 +2590,12 @@ public final class TvInputManager {
}
}
- /** @removed */
public boolean dispatchKeyEventToHdmi(KeyEvent event) {
- return false;
+ try {
+ return mInterface.dispatchKeyEventToHdmi(event);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
}
public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
diff --git a/android/net/LinkProperties.java b/android/net/LinkProperties.java
index 4e474c8e..2c9fb23e 100644
--- a/android/net/LinkProperties.java
+++ b/android/net/LinkProperties.java
@@ -683,9 +683,9 @@ public final class LinkProperties implements Parcelable {
*/
public boolean hasIPv4Address() {
for (LinkAddress address : mLinkAddresses) {
- if (address.getAddress() instanceof Inet4Address) {
- return true;
- }
+ if (address.getAddress() instanceof Inet4Address) {
+ return true;
+ }
}
return false;
}
@@ -725,9 +725,9 @@ public final class LinkProperties implements Parcelable {
*/
public boolean hasIPv4DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv4Default()) {
- return true;
- }
+ if (r.isIPv4Default()) {
+ return true;
+ }
}
return false;
}
@@ -740,9 +740,9 @@ public final class LinkProperties implements Parcelable {
*/
public boolean hasIPv6DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv6Default()) {
- return true;
- }
+ if (r.isIPv6Default()) {
+ return true;
+ }
}
return false;
}
@@ -755,9 +755,9 @@ public final class LinkProperties implements Parcelable {
*/
public boolean hasIPv4DnsServer() {
for (InetAddress ia : mDnses) {
- if (ia instanceof Inet4Address) {
- return true;
- }
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
}
return false;
}
@@ -770,9 +770,9 @@ public final class LinkProperties implements Parcelable {
*/
public boolean hasIPv6DnsServer() {
for (InetAddress ia : mDnses) {
- if (ia instanceof Inet6Address) {
- return true;
- }
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
}
return false;
}
diff --git a/android/net/ip/ConnectivityPacketTracker.java b/android/net/ip/ConnectivityPacketTracker.java
index 1925c39e..0230f36b 100644
--- a/android/net/ip/ConnectivityPacketTracker.java
+++ b/android/net/ip/ConnectivityPacketTracker.java
@@ -25,7 +25,6 @@ import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
-import android.text.TextUtils;
import android.util.Log;
import android.util.LocalLog;
@@ -60,14 +59,11 @@ public class ConnectivityPacketTracker {
private static final boolean DBG = false;
private static final String MARK_START = "--- START ---";
private static final String MARK_STOP = "--- STOP ---";
- private static final String MARK_NAMED_START = "--- START (%s) ---";
- private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
private final String mTag;
private final LocalLog mLog;
private final BlockingSocketReader mPacketListener;
private boolean mRunning;
- private String mDisplayName;
public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
final String ifname;
@@ -89,16 +85,14 @@ public class ConnectivityPacketTracker {
mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
}
- public void start(String displayName) {
+ public void start() {
mRunning = true;
- mDisplayName = displayName;
mPacketListener.start();
}
public void stop() {
mPacketListener.stop();
mRunning = false;
- mDisplayName = null;
}
private final class PacketListener extends BlockingSocketReader {
@@ -139,19 +133,16 @@ public class ConnectivityPacketTracker {
@Override
protected void onStart() {
- final String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_START
- : String.format(MARK_NAMED_START, mDisplayName);
- mLog.log(msg);
+ mLog.log(MARK_START);
}
@Override
protected void onStop() {
- String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_STOP
- : String.format(MARK_NAMED_STOP, mDisplayName);
- if (!mRunning) msg += " (packet listener stopped unexpectedly)";
- mLog.log(msg);
+ if (mRunning) {
+ mLog.log(MARK_STOP);
+ } else {
+ mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)");
+ }
}
@Override
diff --git a/android/net/ip/IpManager.java b/android/net/ip/IpManager.java
index e33f6c99..bc07b810 100644
--- a/android/net/ip/IpManager.java
+++ b/android/net/ip/IpManager.java
@@ -26,7 +26,6 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties.ProvisioningChange;
import android.net.LinkProperties;
-import android.net.Network;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
@@ -349,16 +348,6 @@ public class IpManager extends StateMachine {
return this;
}
- public Builder withNetwork(Network network) {
- mConfig.mNetwork = network;
- return this;
- }
-
- public Builder withDisplayName(String displayName) {
- mConfig.mDisplayName = displayName;
- return this;
- }
-
public ProvisioningConfiguration build() {
return new ProvisioningConfiguration(mConfig);
}
@@ -373,8 +362,6 @@ public class IpManager extends StateMachine {
/* package */ ApfCapabilities mApfCapabilities;
/* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
/* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
- /* package */ Network mNetwork = null;
- /* package */ String mDisplayName = null;
public ProvisioningConfiguration() {} // used by Builder
@@ -387,9 +374,6 @@ public class IpManager extends StateMachine {
mStaticIpConfig = other.mStaticIpConfig;
mApfCapabilities = other.mApfCapabilities;
mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
- mIPv6AddrGenMode = other.mIPv6AddrGenMode;
- mNetwork = other.mNetwork;
- mDisplayName = other.mDisplayName;
}
@Override
@@ -404,8 +388,6 @@ public class IpManager extends StateMachine {
.add("mApfCapabilities: " + mApfCapabilities)
.add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
.add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
- .add("mNetwork: " + mNetwork)
- .add("mDisplayName: " + mDisplayName)
.toString();
}
@@ -1459,10 +1441,10 @@ public class IpManager extends StateMachine {
@Override
public void enter() {
// Get the Configuration for ApfFilter from Context
- final boolean filter802_3Frames =
+ boolean filter802_3Frames =
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
- final int[] ethTypeBlackList = mContext.getResources().getIntArray(
+ int[] ethTypeBlackList = mContext.getResources().getIntArray(
R.array.config_apfEthTypeBlackList);
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
@@ -1474,7 +1456,7 @@ public class IpManager extends StateMachine {
}
mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
+ if (mPacketTracker != null) mPacketTracker.start();
if (mConfiguration.mEnableIPv6 && !startIPv6()) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
@@ -1488,7 +1470,7 @@ public class IpManager extends StateMachine {
return;
}
- final InitialConfiguration config = mConfiguration.mInitialConfig;
+ InitialConfiguration config = mConfiguration.mInitialConfig;
if ((config != null) && !applyInitialConfig(config)) {
// TODO introduce a new IpManagerEvent constant to distinguish this error case.
doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
diff --git a/android/net/util/SharedLog.java b/android/net/util/SharedLog.java
index bbd3d13e..343d237f 100644
--- a/android/net/util/SharedLog.java
+++ b/android/net/util/SharedLog.java
@@ -106,10 +106,6 @@ public class SharedLog {
record(Category.NONE, msg);
}
- public void logf(String fmt, Object... args) {
- log(String.format(fmt, args));
- }
-
public void mark(String msg) {
record(Category.MARK, msg);
}
diff --git a/android/os/BatteryStats.java b/android/os/BatteryStats.java
index 59956964..98819279 100644
--- a/android/os/BatteryStats.java
+++ b/android/os/BatteryStats.java
@@ -1911,13 +1911,6 @@ public abstract class BatteryStats implements Parcelable {
long elapsedRealtimeUs, int which);
/**
- * Returns the {@link Timer} object that tracks the given screen brightness.
- *
- * {@hide}
- */
- public abstract Timer getScreenBrightnessTimer(int brightnessBin);
-
- /**
* Returns the time in microseconds that power save mode has been enabled while the device was
* running on battery.
*
@@ -2026,14 +2019,6 @@ public abstract class BatteryStats implements Parcelable {
long elapsedRealtimeUs, int which);
/**
- * Returns the {@link Timer} object that tracks how much the phone has been trying to
- * acquire a signal.
- *
- * {@hide}
- */
- public abstract Timer getPhoneSignalScanningTimer();
-
- /**
* Returns the number of times the phone has entered the given signal strength.
*
* {@hide}
@@ -2041,12 +2026,6 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getPhoneSignalStrengthCount(int strengthBin, int which);
/**
- * Return the {@link Timer} object used to track the given signal strength's duration and
- * counts.
- */
- protected abstract Timer getPhoneSignalStrengthTimer(int strengthBin);
-
- /**
* Returns the time in microseconds that the mobile network has been active
* (in a high power state).
*
@@ -2129,11 +2108,6 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract int getPhoneDataConnectionCount(int dataType, int which);
- /**
- * Returns the {@link Timer} object that tracks the phone's data connection type stats.
- */
- public abstract Timer getPhoneDataConnectionTimer(int dataType);
-
public static final int WIFI_SUPPL_STATE_INVALID = 0;
public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1;
public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2;
@@ -2293,13 +2267,6 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getWifiStateCount(int wifiState, int which);
/**
- * Returns the {@link Timer} object that tracks the given WiFi state.
- *
- * {@hide}
- */
- public abstract Timer getWifiStateTimer(int wifiState);
-
- /**
* Returns the time in microseconds that the wifi supplicant has been
* in a given state.
*
@@ -2315,13 +2282,6 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract int getWifiSupplStateCount(int state, int which);
- /**
- * Returns the {@link Timer} object that tracks the given wifi supplicant state.
- *
- * {@hide}
- */
- public abstract Timer getWifiSupplStateTimer(int state);
-
public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5;
/**
@@ -2341,13 +2301,6 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
/**
- * Returns the {@link Timer} object that tracks the given WIFI signal strength.
- *
- * {@hide}
- */
- public abstract Timer getWifiSignalStrengthTimer(int strengthBin);
-
- /**
* Returns the time in microseconds that the flashlight has been on while the device was
* running on battery.
*
@@ -2534,13 +2487,13 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getDischargeAmountScreenOffSinceCharge();
/**
- * Get the amount the battery has discharged while the screen was dozing,
+ * Get the amount the battery has discharged while the screen was doze,
* since the last time power was unplugged.
*/
public abstract int getDischargeAmountScreenDoze();
/**
- * Get the amount the battery has discharged while the screen was dozing,
+ * Get the amount the battery has discharged while the screen was doze,
* since the last time the device was charged.
*/
public abstract int getDischargeAmountScreenDozeSinceCharge();
@@ -2673,20 +2626,20 @@ public abstract class BatteryStats implements Parcelable {
* micro-Ampere-hours. This will be non-zero only if the device's battery has
* a coulomb counter.
*/
- public abstract long getUahDischargeScreenOff(int which);
+ public abstract long getMahDischargeScreenOff(int which);
/**
* Return the amount of battery discharge while the screen was in doze mode, measured in
* micro-Ampere-hours. This will be non-zero only if the device's battery has
* a coulomb counter.
*/
- public abstract long getUahDischargeScreenDoze(int which);
+ public abstract long getMahDischargeScreenDoze(int which);
/**
* Return the amount of battery discharge measured in micro-Ampere-hours. This will be
* non-zero only if the device's battery has a coulomb counter.
*/
- public abstract long getUahDischarge(int which);
+ public abstract long getMahDischarge(int which);
/**
* Returns the estimated real battery capacity, which may be less than the capacity
@@ -3031,7 +2984,7 @@ public abstract class BatteryStats implements Parcelable {
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
/ 1000;
final int count = timer.getCountLocked(which);
- if (totalTime != 0 || count != 0) {
+ if (totalTime != 0) {
dumpLine(pw, uid, category, type, totalTime, count);
}
}
@@ -3047,12 +3000,12 @@ public abstract class BatteryStats implements Parcelable {
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
*/
private static void dumpTimer(ProtoOutputStream proto, long fieldId,
- Timer timer, long rawRealtimeUs, int which) {
+ Timer timer, long rawRealtime, int which) {
if (timer == null) {
return;
}
// Convert from microseconds to milliseconds with rounding
- final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
+ final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
if (totalTimeMs != 0 || count != 0) {
final long token = proto.start(fieldId);
@@ -3161,104 +3114,71 @@ public abstract class BatteryStats implements Parcelable {
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
- // Battery real time
- final long totalControllerActivityTimeMs
- = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
long totalTxTimeMs = 0;
for (LongCounter txState : counter.getTxTimeCounters()) {
totalTxTimeMs += txState.getCountLocked(which);
}
- final long sleepTimeMs
- = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" ");
- sb.append(controllerName);
- sb.append(" Sleep time: ");
- formatTimeMs(sb, sleepTimeMs);
- sb.append("(");
- sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
- sb.append(")");
- pw.println(sb.toString());
+ final long totalTimeMs = idleTimeMs + rxTimeMs + totalTxTimeMs;
sb.setLength(0);
sb.append(prefix);
- sb.append(" ");
+ sb.append(" ");
sb.append(controllerName);
sb.append(" Idle time: ");
formatTimeMs(sb, idleTimeMs);
sb.append("(");
- sb.append(formatRatioLocked(idleTimeMs, totalControllerActivityTimeMs));
+ sb.append(formatRatioLocked(idleTimeMs, totalTimeMs));
sb.append(")");
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
- sb.append(" ");
+ sb.append(" ");
sb.append(controllerName);
sb.append(" Rx time: ");
formatTimeMs(sb, rxTimeMs);
sb.append("(");
- sb.append(formatRatioLocked(rxTimeMs, totalControllerActivityTimeMs));
+ sb.append(formatRatioLocked(rxTimeMs, totalTimeMs));
sb.append(")");
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
- sb.append(" ");
+ sb.append(" ");
sb.append(controllerName);
sb.append(" Tx time: ");
+ formatTimeMs(sb, totalTxTimeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(totalTxTimeMs, totalTimeMs));
+ sb.append(")");
+ pw.println(sb.toString());
- String [] powerLevel;
- switch(controllerName) {
- case "Cellular":
- powerLevel = new String[] {
- " less than 0dBm: ",
- " 0dBm to 8dBm: ",
- " 8dBm to 15dBm: ",
- " 15dBm to 20dBm: ",
- " above 20dBm: "};
- break;
- default:
- powerLevel = new String[] {"[0]", "[1]", "[2]", "[3]", "[4]"};
- break;
- }
- final int numTxLvls = Math.min(counter.getTxTimeCounters().length, powerLevel.length);
+ final int numTxLvls = counter.getTxTimeCounters().length;
if (numTxLvls > 1) {
- pw.println(sb.toString());
for (int lvl = 0; lvl < numTxLvls; lvl++) {
final long txLvlTimeMs = counter.getTxTimeCounters()[lvl].getCountLocked(which);
sb.setLength(0);
sb.append(prefix);
- sb.append(" ");
- sb.append(powerLevel[lvl]);
- sb.append(" ");
+ sb.append(" [");
+ sb.append(lvl);
+ sb.append("] ");
formatTimeMs(sb, txLvlTimeMs);
sb.append("(");
- sb.append(formatRatioLocked(txLvlTimeMs, totalControllerActivityTimeMs));
+ sb.append(formatRatioLocked(txLvlTimeMs, totalTxTimeMs));
sb.append(")");
pw.println(sb.toString());
}
- } else {
- final long txLvlTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
- formatTimeMs(sb, txLvlTimeMs);
- sb.append("(");
- sb.append(formatRatioLocked(txLvlTimeMs, totalControllerActivityTimeMs));
- sb.append(")");
- pw.println(sb.toString());
}
- if (powerDrainMaMs > 0) {
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" ");
- sb.append(controllerName);
- sb.append(" Battery drain: ").append(
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(controllerName);
+ sb.append(" Power drain: ").append(
BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60)));
- sb.append("mAh");
- pw.println(sb.toString());
- }
+ sb.append("mAh");
+ pw.println(sb.toString());
}
/**
@@ -3271,13 +3191,13 @@ public abstract class BatteryStats implements Parcelable {
/**
* Checkin server version of dump to produce more compact, computer-readable log.
*
- * NOTE: all times are expressed in microseconds, unless specified otherwise.
+ * NOTE: all times are expressed in 'ms'.
*/
public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
boolean wifiOnly) {
final long rawUptime = SystemClock.uptimeMillis() * 1000;
- final long rawRealtimeMs = SystemClock.elapsedRealtime();
- final long rawRealtime = rawRealtimeMs * 1000;
+ final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
+ final long rawRealtimeMs = (rawRealtime + 500) / 1000;
final long batteryUptime = getBatteryUptime(rawUptime);
final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
@@ -3300,9 +3220,9 @@ public abstract class BatteryStats implements Parcelable {
rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
- final long dischargeCount = getUahDischarge(which);
- final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
- final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
+ final long dischargeCount = getMahDischarge(which);
+ final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
+ final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
final StringBuilder sb = new StringBuilder(128);
@@ -3540,9 +3460,9 @@ public abstract class BatteryStats implements Parcelable {
BatteryStatsHelper.makemAh(helper.getComputedPower()),
BatteryStatsHelper.makemAh(helper.getMinDrainedPower()),
BatteryStatsHelper.makemAh(helper.getMaxDrainedPower()));
- int uid = 0;
for (int i=0; i<sippers.size(); i++) {
final BatterySipper bs = sippers.get(i);
+ int uid = 0;
String label;
switch (bs.drainType) {
case IDLE:
@@ -3583,9 +3503,6 @@ public abstract class BatteryStats implements Parcelable {
case CAMERA:
label = "camera";
break;
- case MEMORY:
- label = "memory";
- break;
default:
label = "???";
}
@@ -3606,7 +3523,6 @@ public abstract class BatteryStats implements Parcelable {
dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
}
- // Dump stats per UID.
for (int iu = 0; iu < NU; iu++) {
final int uid = uidStats.keyAt(iu);
if (reqUid >= 0 && uid != reqUid) {
@@ -4104,7 +4020,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- final long dischargeCount = getUahDischarge(which);
+ final long dischargeCount = getMahDischarge(which);
if (dischargeCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -4114,7 +4030,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
+ final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
if (dischargeScreenOffCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -4124,7 +4040,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
+ final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
if (dischargeScreenDozeCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -4330,116 +4246,126 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
- pw.println("");
- pw.print(prefix);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" CONNECTIVITY POWER SUMMARY START");
- pw.println(sb.toString());
-
- pw.print(prefix);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Logging duration for connectivity statistics: ");
- formatTimeMs(sb, whichBatteryRealtime / 1000);
- pw.println(sb.toString());
-
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Cellular Statistics:");
- pw.println(sb.toString());
-
pw.print(prefix);
+ pw.print(" Mobile total received: "); pw.print(formatBytesLocked(mobileRxTotalBytes));
+ pw.print(", sent: "); pw.print(formatBytesLocked(mobileTxTotalBytes));
+ pw.print(" (packets received "); pw.print(mobileRxTotalPackets);
+ pw.print(", sent "); pw.print(mobileTxTotalPackets); pw.println(")");
sb.setLength(0);
sb.append(prefix);
- sb.append(" Cellular kernel active time: ");
- final long mobileActiveTime = getMobileRadioActiveTime(rawRealtime, which);
- formatTimeMs(sb, mobileActiveTime / 1000);
- sb.append("("); sb.append(formatRatioLocked(mobileActiveTime, whichBatteryRealtime));
- sb.append(")");
- pw.println(sb.toString());
-
- pw.print(" Cellular data received: "); pw.println(formatBytesLocked(mobileRxTotalBytes));
- pw.print(" Cellular data sent: "); pw.println(formatBytesLocked(mobileTxTotalBytes));
- pw.print(" Cellular packets received: "); pw.println(mobileRxTotalPackets);
- pw.print(" Cellular packets sent: "); pw.println(mobileTxTotalPackets);
-
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Cellular Radio Access Technology:");
+ sb.append(" Phone signal levels:");
didOne = false;
- for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- final long time = getPhoneDataConnectionTime(i, rawRealtime, which);
+ for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
+ final long time = getPhoneSignalStrengthTime(i, rawRealtime, which);
if (time == 0) {
continue;
}
- sb.append("\n ");
+ sb.append("\n ");
sb.append(prefix);
didOne = true;
- sb.append(DATA_CONNECTION_NAMES[i]);
+ sb.append(SignalStrength.SIGNAL_STRENGTH_NAMES[i]);
sb.append(" ");
formatTimeMs(sb, time/1000);
sb.append("(");
sb.append(formatRatioLocked(time, whichBatteryRealtime));
sb.append(") ");
+ sb.append(getPhoneSignalStrengthCount(i, which));
+ sb.append("x");
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
- sb.append(" Cellular Rx signal strength (RSRP):");
- final String[] cellularRxSignalStrengthDescription = new String[]{
- "very poor (less than -128dBm): ",
- "poor (-128dBm to -118dBm): ",
- "moderate (-118dBm to -108dBm): ",
- "good (-108dBm to -98dBm): ",
- "great (greater than -98dBm): "};
+ sb.append(" Signal scanning time: ");
+ formatTimeMsNoSpace(sb, getPhoneSignalScanningTime(rawRealtime, which) / 1000);
+ pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Radio types:");
didOne = false;
- final int numCellularRxBins = Math.min(SignalStrength.NUM_SIGNAL_STRENGTH_BINS,
- cellularRxSignalStrengthDescription.length);
- for (int i=0; i<numCellularRxBins; i++) {
- final long time = getPhoneSignalStrengthTime(i, rawRealtime, which);
+ for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+ final long time = getPhoneDataConnectionTime(i, rawRealtime, which);
if (time == 0) {
continue;
}
- sb.append("\n ");
+ sb.append("\n ");
sb.append(prefix);
didOne = true;
- sb.append(cellularRxSignalStrengthDescription[i]);
+ sb.append(DATA_CONNECTION_NAMES[i]);
sb.append(" ");
formatTimeMs(sb, time/1000);
sb.append("(");
sb.append(formatRatioLocked(time, whichBatteryRealtime));
sb.append(") ");
+ sb.append(getPhoneDataConnectionCount(i, which));
+ sb.append("x");
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
- printControllerActivity(pw, sb, prefix, "Cellular",
- getModemControllerActivity(), which);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Mobile radio active time: ");
+ final long mobileActiveTime = getMobileRadioActiveTime(rawRealtime, which);
+ formatTimeMs(sb, mobileActiveTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(mobileActiveTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getMobileRadioActiveCount(which));
+ sb.append("x");
+ pw.println(sb.toString());
+
+ final long mobileActiveUnknownTime = getMobileRadioActiveUnknownTime(which);
+ if (mobileActiveUnknownTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Mobile radio active unknown time: ");
+ formatTimeMs(sb, mobileActiveUnknownTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(mobileActiveUnknownTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getMobileRadioActiveUnknownCount(which));
+ sb.append("x");
+ pw.println(sb.toString());
+ }
+
+ final long mobileActiveAdjustedTime = getMobileRadioActiveAdjustedTime(which);
+ if (mobileActiveAdjustedTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Mobile radio active adjusted time: ");
+ formatTimeMs(sb, mobileActiveAdjustedTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(mobileActiveAdjustedTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+ }
+
+ printControllerActivity(pw, sb, prefix, "Radio", getModemControllerActivity(), which);
pw.print(prefix);
+ pw.print(" Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
+ pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
+ pw.print(" (packets received "); pw.print(wifiRxTotalPackets);
+ pw.print(", sent "); pw.print(wifiTxTotalPackets); pw.println(")");
sb.setLength(0);
sb.append(prefix);
- sb.append(" Wifi Statistics:");
+ sb.append(" Wifi on: "); formatTimeMs(sb, wifiOnTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(wifiOnTime, whichBatteryRealtime));
+ sb.append("), Wifi running: "); formatTimeMs(sb, wifiRunningTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(wifiRunningTime, whichBatteryRealtime));
+ sb.append(")");
pw.println(sb.toString());
- pw.print(" Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes));
- pw.print(" Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes));
- pw.print(" Wifi packets received: "); pw.println(wifiRxTotalPackets);
- pw.print(" Wifi packets sent: "); pw.println(wifiTxTotalPackets);
-
sb.setLength(0);
sb.append(prefix);
- sb.append(" Wifi states:");
+ sb.append(" Wifi states:");
didOne = false;
for (int i=0; i<NUM_WIFI_STATES; i++) {
final long time = getWifiStateTime(i, rawRealtime, which);
if (time == 0) {
continue;
}
- sb.append("\n ");
+ sb.append("\n ");
didOne = true;
sb.append(WIFI_STATE_NAMES[i]);
sb.append(" ");
@@ -4447,20 +4373,22 @@ public abstract class BatteryStats implements Parcelable {
sb.append("(");
sb.append(formatRatioLocked(time, whichBatteryRealtime));
sb.append(") ");
+ sb.append(getWifiStateCount(i, which));
+ sb.append("x");
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
- sb.append(" Wifi supplicant states:");
+ sb.append(" Wifi supplicant states:");
didOne = false;
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
final long time = getWifiSupplStateTime(i, rawRealtime, which);
if (time == 0) {
continue;
}
- sb.append("\n ");
+ sb.append("\n ");
didOne = true;
sb.append(WIFI_SUPPL_STATE_NAMES[i]);
sb.append(" ");
@@ -4468,23 +4396,17 @@ public abstract class BatteryStats implements Parcelable {
sb.append("(");
sb.append(formatRatioLocked(time, whichBatteryRealtime));
sb.append(") ");
+ sb.append(getWifiSupplStateCount(i, which));
+ sb.append("x");
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
sb.setLength(0);
sb.append(prefix);
- sb.append(" Wifi Rx signal strength (RSSI):");
- final String[] wifiRxSignalStrengthDescription = new String[]{
- "very poor (less than -88.75dBm): ",
- "poor (-88.75 to -77.5dBm): ",
- "moderate (-77.5dBm to -66.25dBm): ",
- "good (-66.25dBm to -55dBm): ",
- "great (greater than -55dBm): "};
+ sb.append(" Wifi signal levels:");
didOne = false;
- final int numWifiRxBins = Math.min(NUM_WIFI_SIGNAL_STRENGTH_BINS,
- wifiRxSignalStrengthDescription.length);
- for (int i=0; i<numWifiRxBins; i++) {
+ for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
final long time = getWifiSignalStrengthTime(i, rawRealtime, which);
if (time == 0) {
continue;
@@ -4492,12 +4414,15 @@ public abstract class BatteryStats implements Parcelable {
sb.append("\n ");
sb.append(prefix);
didOne = true;
- sb.append(" ");
- sb.append(wifiRxSignalStrengthDescription[i]);
+ sb.append("level(");
+ sb.append(i);
+ sb.append(") ");
formatTimeMs(sb, time/1000);
sb.append("(");
sb.append(formatRatioLocked(time, whichBatteryRealtime));
sb.append(") ");
+ sb.append(getWifiSignalStrengthCount(i, which));
+ sb.append("x");
}
if (!didOne) sb.append(" (no activity)");
pw.println(sb.toString());
@@ -4505,13 +4430,6 @@ public abstract class BatteryStats implements Parcelable {
printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which);
pw.print(prefix);
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" CONNECTIVITY POWER SUMMARY END");
- pw.println(sb.toString());
- pw.println("");
-
- pw.print(prefix);
pw.print(" Bluetooth total received: "); pw.print(formatBytesLocked(btRxTotalBytes));
pw.print(", sent: "); pw.println(formatBytesLocked(btTxTotalBytes));
@@ -6120,61 +6038,6 @@ public abstract class BatteryStats implements Parcelable {
return true;
}
- private static void dumpDurationSteps(ProtoOutputStream proto, long fieldId,
- LevelStepTracker steps) {
- if (steps == null) {
- return;
- }
- int count = steps.mNumStepDurations;
- long token;
- for (int i = 0; i < count; ++i) {
- token = proto.start(fieldId);
- proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
- proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
-
- final long initMode = steps.getInitModeAt(i);
- final long modMode = steps.getModModeAt(i);
-
- int ds = SystemProto.BatteryLevelStep.DS_MIXED;
- if ((modMode & STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
- switch ((int) (initMode & STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
- case Display.STATE_OFF:
- ds = SystemProto.BatteryLevelStep.DS_OFF;
- break;
- case Display.STATE_ON:
- ds = SystemProto.BatteryLevelStep.DS_ON;
- break;
- case Display.STATE_DOZE:
- ds = SystemProto.BatteryLevelStep.DS_DOZE;
- break;
- case Display.STATE_DOZE_SUSPEND:
- ds = SystemProto.BatteryLevelStep.DS_DOZE_SUSPEND;
- break;
- default:
- ds = SystemProto.BatteryLevelStep.DS_ERROR;
- break;
- }
- }
- proto.write(SystemProto.BatteryLevelStep.DISPLAY_STATE, ds);
-
- int psm = SystemProto.BatteryLevelStep.PSM_MIXED;
- if ((modMode & STEP_LEVEL_MODE_POWER_SAVE) == 0) {
- psm = (initMode & STEP_LEVEL_MODE_POWER_SAVE) != 0
- ? SystemProto.BatteryLevelStep.PSM_ON : SystemProto.BatteryLevelStep.PSM_OFF;
- }
- proto.write(SystemProto.BatteryLevelStep.POWER_SAVE_MODE, psm);
-
- int im = SystemProto.BatteryLevelStep.IM_MIXED;
- if ((modMode & STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
- im = (initMode & STEP_LEVEL_MODE_DEVICE_IDLE) != 0
- ? SystemProto.BatteryLevelStep.IM_ON : SystemProto.BatteryLevelStep.IM_OFF;
- }
- proto.write(SystemProto.BatteryLevelStep.IDLE_MODE, im);
-
- proto.end(token);
- }
- }
-
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_DAILY_ONLY = 1<<2;
public static final int DUMP_HISTORY_ONLY = 1<<3;
@@ -6600,7 +6463,7 @@ public abstract class BatteryStats implements Parcelable {
}
}
- /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */
+ /** Dump batterystats data to a proto. @hide */
public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
int flags, long historyStart) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -6622,376 +6485,10 @@ public abstract class BatteryStats implements Parcelable {
if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
// TODO: implement dumpProtoAppsLocked(proto, apps);
- dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+ // TODO: implement dumpProtoSystemLocked(proto);
}
proto.end(bToken);
proto.flush();
}
-
- private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
- final long sToken = proto.start(BatteryStatsProto.SYSTEM);
- final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
- final long rawRealtimeMs = SystemClock.elapsedRealtime();
- final long rawRealtimeUs = rawRealtimeMs * 1000;
- final int which = STATS_SINCE_CHARGED;
-
- // Battery data (BATTERY_DATA)
- long token = proto.start(SystemProto.BATTERY);
- proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
- proto.write(SystemProto.Battery.START_COUNT, getStartCount());
- proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
- computeRealtime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Battery.TOTAL_UPTIME_MS,
- computeUptime(rawUptimeUs, which) / 1000);
- proto.write(SystemProto.Battery.BATTERY_REALTIME_MS,
- computeBatteryRealtime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Battery.BATTERY_UPTIME_MS,
- computeBatteryUptime(rawUptimeUs, which) / 1000);
- proto.write(SystemProto.Battery.SCREEN_OFF_REALTIME_MS,
- computeBatteryScreenOffRealtime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Battery.SCREEN_OFF_UPTIME_MS,
- computeBatteryScreenOffUptime(rawUptimeUs, which) / 1000);
- proto.write(SystemProto.Battery.SCREEN_DOZE_DURATION_MS,
- getScreenDozeTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Battery.ESTIMATED_BATTERY_CAPACITY_MAH,
- getEstimatedBatteryCapacity());
- proto.write(SystemProto.Battery.MIN_LEARNED_BATTERY_CAPACITY_UAH,
- getMinLearnedBatteryCapacity());
- proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
- getMaxLearnedBatteryCapacity());
- proto.end(token);
-
- // Battery discharge (BATTERY_DISCHARGE_DATA)
- token = proto.start(SystemProto.BATTERY_DISCHARGE);
- proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
- getLowDischargeAmountSinceCharge());
- proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
- getHighDischargeAmountSinceCharge());
- proto.write(SystemProto.BatteryDischarge.SCREEN_ON_SINCE_CHARGE,
- getDischargeAmountScreenOnSinceCharge());
- proto.write(SystemProto.BatteryDischarge.SCREEN_OFF_SINCE_CHARGE,
- getDischargeAmountScreenOffSinceCharge());
- proto.write(SystemProto.BatteryDischarge.SCREEN_DOZE_SINCE_CHARGE,
- getDischargeAmountScreenDozeSinceCharge());
- proto.write(SystemProto.BatteryDischarge.TOTAL_MAH,
- getUahDischarge(which) / 1000);
- proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_OFF,
- getUahDischargeScreenOff(which) / 1000);
- proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
- getUahDischargeScreenDoze(which) / 1000);
- proto.end(token);
-
- // Time remaining
- long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
- if (timeRemainingUs >= 0) {
- // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
- proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
- } else {
- timeRemainingUs = computeBatteryTimeRemaining(rawRealtimeUs);
- // Discharge time remaining (DISCHARGE_TIME_REMAIN_DATA)
- if (timeRemainingUs >= 0) {
- proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
- } else {
- proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, -1);
- }
- }
-
- // Charge step (CHARGE_STEP_DATA)
- dumpDurationSteps(proto, SystemProto.CHARGE_STEP, getChargeLevelStepTracker());
-
- // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
- for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
- token = proto.start(SystemProto.DATA_CONNECTION);
- proto.write(SystemProto.DataConnection.NAME, i);
- dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Discharge step (DISCHARGE_STEP_DATA)
- dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());
-
- // CPU frequencies (GLOBAL_CPU_FREQ_DATA)
- final long[] cpuFreqs = getCpuFreqs();
- if (cpuFreqs != null) {
- for (long i : cpuFreqs) {
- proto.write(SystemProto.CPU_FREQUENCY, i);
- }
- }
-
- // Bluetooth controller (GLOBAL_BLUETOOTH_CONTROLLER_DATA)
- dumpControllerActivityProto(proto, SystemProto.GLOBAL_BLUETOOTH_CONTROLLER,
- getBluetoothControllerActivity(), which);
-
- // Modem controller (GLOBAL_MODEM_CONTROLLER_DATA)
- dumpControllerActivityProto(proto, SystemProto.GLOBAL_MODEM_CONTROLLER,
- getModemControllerActivity(), which);
-
- // Global network data (GLOBAL_NETWORK_DATA)
- token = proto.start(SystemProto.GLOBAL_NETWORK);
- proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
- getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
- getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_RX,
- getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_TX,
- getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_RX,
- getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_TX,
- getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_RX,
- getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_TX,
- getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.BT_BYTES_RX,
- getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
- proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
- getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
- proto.end(token);
-
- // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
- dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
- getWifiControllerActivity(), which);
-
-
- // Global wifi (GLOBAL_WIFI_DATA)
- token = proto.start(SystemProto.GLOBAL_WIFI);
- proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
- getWifiOnTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
- getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
- proto.end(token);
-
- // Kernel wakelock (KERNEL_WAKELOCK_DATA)
- final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
- for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
- token = proto.start(SystemProto.KERNEL_WAKELOCK);
- proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
- dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Misc (MISC_DATA)
- // Calculate wakelock times across all uids.
- long fullWakeLockTimeTotalUs = 0;
- long partialWakeLockTimeTotalUs = 0;
-
- final SparseArray<? extends Uid> uidStats = getUidStats();
- for (int iu = 0; iu < uidStats.size(); iu++) {
- final Uid u = uidStats.valueAt(iu);
-
- final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks =
- u.getWakelockStats();
- for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
- final Uid.Wakelock wl = wakelocks.valueAt(iw);
-
- final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
- if (fullWakeTimer != null) {
- fullWakeLockTimeTotalUs += fullWakeTimer.getTotalTimeLocked(rawRealtimeUs,
- which);
- }
-
- final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
- if (partialWakeTimer != null) {
- partialWakeLockTimeTotalUs += partialWakeTimer.getTotalTimeLocked(
- rawRealtimeUs, which);
- }
- }
- }
- token = proto.start(SystemProto.MISC);
- proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
- getScreenOnTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
- getPhoneOnTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.FULL_WAKELOCK_TOTAL_DURATION_MS,
- fullWakeLockTimeTotalUs / 1000);
- proto.write(SystemProto.Misc.PARTIAL_WAKELOCK_TOTAL_DURATION_MS,
- partialWakeLockTimeTotalUs / 1000);
- proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_DURATION_MS,
- getMobileRadioActiveTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_ADJUSTED_TIME_MS,
- getMobileRadioActiveAdjustedTime(which) / 1000);
- proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_COUNT,
- getMobileRadioActiveCount(which));
- proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_UNKNOWN_DURATION_MS,
- getMobileRadioActiveUnknownTime(which) / 1000);
- proto.write(SystemProto.Misc.INTERACTIVE_DURATION_MS,
- getInteractiveTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.BATTERY_SAVER_MODE_ENABLED_DURATION_MS,
- getPowerSaveModeEnabledTime(rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.NUM_CONNECTIVITY_CHANGES,
- getNumConnectivityChange(which));
- proto.write(SystemProto.Misc.DEEP_DOZE_ENABLED_DURATION_MS,
- getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.DEEP_DOZE_COUNT,
- getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which));
- proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_DURATION_MS,
- getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_COUNT,
- getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which));
- proto.write(SystemProto.Misc.LONGEST_DEEP_DOZE_DURATION_MS,
- getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
- proto.write(SystemProto.Misc.LIGHT_DOZE_ENABLED_DURATION_MS,
- getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.LIGHT_DOZE_COUNT,
- getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
- proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_DURATION_MS,
- getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
- proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_COUNT,
- getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
- proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
- getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
- proto.end(token);
-
- final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
- helper.create(this);
- helper.refreshStats(which, UserHandle.USER_ALL);
-
- // Power use item (POWER_USE_ITEM_DATA)
- final List<BatterySipper> sippers = helper.getUsageList();
- if (sippers != null) {
- for (int i = 0; i < sippers.size(); ++i) {
- final BatterySipper bs = sippers.get(i);
- int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
- int uid = 0;
- switch (bs.drainType) {
- case IDLE:
- n = SystemProto.PowerUseItem.IDLE;
- break;
- case CELL:
- n = SystemProto.PowerUseItem.CELL;
- break;
- case PHONE:
- n = SystemProto.PowerUseItem.PHONE;
- break;
- case WIFI:
- n = SystemProto.PowerUseItem.WIFI;
- break;
- case BLUETOOTH:
- n = SystemProto.PowerUseItem.BLUETOOTH;
- break;
- case SCREEN:
- n = SystemProto.PowerUseItem.SCREEN;
- break;
- case FLASHLIGHT:
- n = SystemProto.PowerUseItem.FLASHLIGHT;
- break;
- case APP:
- // dumpProtoAppLocked will handle this.
- continue;
- case USER:
- n = SystemProto.PowerUseItem.USER;
- uid = UserHandle.getUid(bs.userId, 0);
- break;
- case UNACCOUNTED:
- n = SystemProto.PowerUseItem.UNACCOUNTED;
- break;
- case OVERCOUNTED:
- n = SystemProto.PowerUseItem.OVERCOUNTED;
- break;
- case CAMERA:
- n = SystemProto.PowerUseItem.CAMERA;
- break;
- case MEMORY:
- n = SystemProto.PowerUseItem.MEMORY;
- break;
- }
- token = proto.start(SystemProto.POWER_USE_ITEM);
- proto.write(SystemProto.PowerUseItem.NAME, n);
- proto.write(SystemProto.PowerUseItem.UID, uid);
- proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
- proto.write(SystemProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
- proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
- proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
- bs.proportionalSmearMah);
- proto.end(token);
- }
- }
-
- // Power use summary (POWER_USE_SUMMARY_DATA)
- token = proto.start(SystemProto.POWER_USE_SUMMARY);
- proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
- helper.getPowerProfile().getBatteryCapacity());
- proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
- proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
- proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
- proto.end(token);
-
- // RPM stats (RESOURCE_POWER_MANAGER_DATA)
- final Map<String, ? extends Timer> rpmStats = getRpmStats();
- final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
- for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
- token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
- proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
- dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
- ent.getValue(), rawRealtimeUs, which);
- dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
- screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Screen brightness (SCREEN_BRIGHTNESS_DATA)
- for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
- token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
- proto.write(SystemProto.ScreenBrightness.NAME, i);
- dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
- dumpTimer(proto, SystemProto.SIGNAL_SCANNING, getPhoneSignalScanningTimer(), rawRealtimeUs,
- which);
-
- // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
- for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
- token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
- proto.write(SystemProto.PhoneSignalStrength.NAME, i);
- dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Wakeup reasons (WAKEUP_REASON_DATA)
- final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
- for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
- token = proto.start(SystemProto.WAKEUP_REASON);
- proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
- dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
- for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
- token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
- proto.write(SystemProto.WifiSignalStrength.NAME, i);
- dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
- for (int i = 0; i < NUM_WIFI_STATES; ++i) {
- token = proto.start(SystemProto.WIFI_STATE);
- proto.write(SystemProto.WifiState.NAME, i);
- dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
- for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
- token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
- proto.write(SystemProto.WifiSupplicantState.NAME, i);
- dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
- rawRealtimeUs, which);
- proto.end(token);
- }
-
- proto.end(sToken);
- }
}
diff --git a/android/os/Debug.java b/android/os/Debug.java
index 017c2134..b46c6b16 100644
--- a/android/os/Debug.java
+++ b/android/os/Debug.java
@@ -1748,26 +1748,22 @@ public final class Debug
public static final int MEMINFO_SHMEM = 4;
/** @hide */
public static final int MEMINFO_SLAB = 5;
- /** @hide */
- public static final int MEMINFO_SLAB_RECLAIMABLE = 6;
- /** @hide */
- public static final int MEMINFO_SLAB_UNRECLAIMABLE = 7;
/** @hide */
- public static final int MEMINFO_SWAP_TOTAL = 8;
+ public static final int MEMINFO_SWAP_TOTAL = 6;
/** @hide */
- public static final int MEMINFO_SWAP_FREE = 9;
+ public static final int MEMINFO_SWAP_FREE = 7;
/** @hide */
- public static final int MEMINFO_ZRAM_TOTAL = 10;
+ public static final int MEMINFO_ZRAM_TOTAL = 8;
/** @hide */
- public static final int MEMINFO_MAPPED = 11;
+ public static final int MEMINFO_MAPPED = 9;
/** @hide */
- public static final int MEMINFO_VM_ALLOC_USED = 12;
+ public static final int MEMINFO_VM_ALLOC_USED = 10;
/** @hide */
- public static final int MEMINFO_PAGE_TABLES = 13;
+ public static final int MEMINFO_PAGE_TABLES = 11;
/** @hide */
- public static final int MEMINFO_KERNEL_STACK = 14;
+ public static final int MEMINFO_KERNEL_STACK = 12;
/** @hide */
- public static final int MEMINFO_COUNT = 15;
+ public static final int MEMINFO_COUNT = 13;
/**
* Retrieves /proc/meminfo. outSizes is filled with fields
diff --git a/android/os/ParcelFileDescriptor.java b/android/os/ParcelFileDescriptor.java
index 7f588adb..c091420a 100644
--- a/android/os/ParcelFileDescriptor.java
+++ b/android/os/ParcelFileDescriptor.java
@@ -737,9 +737,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
private void closeWithStatus(int status, String msg) {
if (mClosed) return;
mClosed = true;
- if (mGuard != null) {
- mGuard.close();
- }
+ mGuard.close();
// Status MUST be sent before closing actual descriptor
writeCommStatusAndClose(status, msg);
IoUtils.closeQuietly(mFd);
diff --git a/android/os/PatternMatcher.java b/android/os/PatternMatcher.java
index 76b21426..1f3a1e68 100644
--- a/android/os/PatternMatcher.java
+++ b/android/os/PatternMatcher.java
@@ -16,7 +16,7 @@
package android.os;
-import android.util.proto.ProtoOutputStream;
+import android.util.Log;
import java.util.Arrays;
@@ -131,17 +131,7 @@ public class PatternMatcher implements Parcelable {
}
return "PatternMatcher{" + type + mPattern + "}";
}
-
- /** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(PatternMatcherProto.PATTERN, mPattern);
- proto.write(PatternMatcherProto.TYPE, mType);
- // PatternMatcherProto.PARSED_PATTERN is too much to dump, but the field is reserved to
- // match the current data structure.
- proto.end(token);
- }
-
+
public int describeContents() {
return 0;
}
@@ -151,7 +141,7 @@ public class PatternMatcher implements Parcelable {
dest.writeInt(mType);
dest.writeIntArray(mParsedPattern);
}
-
+
public PatternMatcher(Parcel src) {
mPattern = src.readString();
mType = src.readInt();
diff --git a/android/os/ServiceManager.java b/android/os/ServiceManager.java
index 34c78455..f41848fa 100644
--- a/android/os/ServiceManager.java
+++ b/android/os/ServiceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -16,9 +16,29 @@
package android.os;
+import android.util.Log;
+
+import com.android.internal.os.BinderInternal;
+
+import java.util.HashMap;
import java.util.Map;
+/** @hide */
public final class ServiceManager {
+ private static final String TAG = "ServiceManager";
+ private static IServiceManager sServiceManager;
+ private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
+
+ private static IServiceManager getIServiceManager() {
+ if (sServiceManager != null) {
+ return sServiceManager;
+ }
+
+ // Find the service manager
+ sServiceManager = ServiceManagerNative
+ .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
+ return sServiceManager;
+ }
/**
* Returns a reference to a service with the given name.
@@ -27,14 +47,32 @@ public final class ServiceManager {
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
+ try {
+ IBinder service = sCache.get(name);
+ if (service != null) {
+ return service;
+ } else {
+ return Binder.allowBlocking(getIServiceManager().getService(name));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getService", e);
+ }
return null;
}
/**
- * Is not supposed to return null, but that is fine for layoutlib.
+ * Returns a reference to a service with the given name, or throws
+ * {@link NullPointerException} if none is found.
+ *
+ * @hide
*/
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
- throw new ServiceNotFoundException(name);
+ final IBinder binder = getService(name);
+ if (binder != null) {
+ return binder;
+ } else {
+ throw new ServiceNotFoundException(name);
+ }
}
/**
@@ -45,7 +83,39 @@ public final class ServiceManager {
* @param service the service object
*/
public static void addService(String name, IBinder service) {
- // pass
+ addService(name, service, false, IServiceManager.DUMP_PRIORITY_NORMAL);
+ }
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ * @param allowIsolated set to true to allow isolated sandboxed processes
+ * to access this service
+ */
+ public static void addService(String name, IBinder service, boolean allowIsolated) {
+ addService(name, service, allowIsolated, IServiceManager.DUMP_PRIORITY_NORMAL);
+ }
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ * @param allowIsolated set to true to allow isolated sandboxed processes
+ * @param dumpPriority supported dump priority levels as a bitmask
+ * to access this service
+ */
+ public static void addService(String name, IBinder service, boolean allowIsolated,
+ int dumpPriority) {
+ try {
+ getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in addService", e);
+ }
}
/**
@@ -53,7 +123,17 @@ public final class ServiceManager {
* service manager. Non-blocking.
*/
public static IBinder checkService(String name) {
- return null;
+ try {
+ IBinder service = sCache.get(name);
+ if (service != null) {
+ return service;
+ } else {
+ return Binder.allowBlocking(getIServiceManager().checkService(name));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in checkService", e);
+ return null;
+ }
}
/**
@@ -62,9 +142,12 @@ public final class ServiceManager {
* case of an exception
*/
public static String[] listServices() {
- // actual implementation returns null sometimes, so it's ok
- // to return null instead of an empty list.
- return null;
+ try {
+ return getIServiceManager().listServices(IServiceManager.DUMP_PRIORITY_ALL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in listServices", e);
+ return null;
+ }
}
/**
@@ -76,7 +159,10 @@ public final class ServiceManager {
* @hide
*/
public static void initServiceCache(Map<String, IBinder> cache) {
- // pass
+ if (sCache.size() != 0) {
+ throw new IllegalStateException("setServiceCache may only be called once");
+ }
+ sCache.putAll(cache);
}
/**
@@ -87,7 +173,6 @@ public final class ServiceManager {
* @hide
*/
public static class ServiceNotFoundException extends Exception {
- // identical to the original implementation
public ServiceNotFoundException(String name) {
super("No service published for: " + name);
}
diff --git a/android/os/SystemProperties.java b/android/os/SystemProperties.java
index 84111fbf..560b4b31 100644
--- a/android/os/SystemProperties.java
+++ b/android/os/SystemProperties.java
@@ -84,6 +84,9 @@ public class SystemProperties {
/**
* Get the String value for the given {@code key}.
*
+ * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This
+ * method will crash in native code.
+ *
* @param key the key to lookup
* @return an empty string if the {@code key} isn't found
*/
@@ -96,6 +99,9 @@ public class SystemProperties {
/**
* Get the String value for the given {@code key}.
*
+ * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This
+ * method will crash in native code.
+ *
* @param key the key to lookup
* @param def the default value in case the property is not set or empty
* @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
diff --git a/android/os/UserManager.java b/android/os/UserManager.java
index 8c688713..430a5e3e 100644
--- a/android/os/UserManager.java
+++ b/android/os/UserManager.java
@@ -574,25 +574,6 @@ public class UserManager {
public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
/**
- * Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
- * In this case, the system will force-stop the app as if the user chooses the "close app"
- * option on the UI. No feedback report will be collected as there is no way for the user to
- * provide explicit consent.
- *
- * When this user restriction is set by device owners, it's applied to all users; when it's set
- * by profile owners, it's only applied to the relevant profiles.
- * The default value is <code>false</code>.
- *
- * <p>This user restriction has no effect on managed profiles.
- * <p>Key for user restrictions.
- * <p>Type: Boolean
- * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
- * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
- * @see #getUserRestrictions()
- */
- public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
-
- /**
* Specifies if what is copied in the clipboard of this profile can
* be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
* pasted in this profile.
diff --git a/android/preference/SeekBarVolumizer.java b/android/preference/SeekBarVolumizer.java
index 3d2e1d1f..ee8eed19 100644
--- a/android/preference/SeekBarVolumizer.java
+++ b/android/preference/SeekBarVolumizer.java
@@ -206,7 +206,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
try {
mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
.getAudioAttributes())
- .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
+ .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
+ AudioAttributes.FLAG_BYPASS_MUTE)
.build());
mRingtone.play();
} catch (Throwable e) {
diff --git a/android/provider/Settings.java b/android/provider/Settings.java
index b4350746..a062db43 100644
--- a/android/provider/Settings.java
+++ b/android/provider/Settings.java
@@ -5708,7 +5708,6 @@ public final class Settings {
*
* @hide
*/
- @TestApi
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
"accessibility_display_magnification_enabled";
@@ -6793,6 +6792,14 @@ public final class Settings {
"lock_screen_show_notifications";
/**
+ * This preference stores the last stack active task time for each user, which affects what
+ * tasks will be visible in Overview.
+ * @hide
+ */
+ public static final String OVERVIEW_LAST_STACK_ACTIVE_TIME =
+ "overview_last_stack_active_time";
+
+ /**
* List of TV inputs that are currently hidden. This is a string
* containing the IDs of all hidden TV inputs. Each ID is encoded by
* {@link android.net.Uri#encode(String)} and separated by ':'.
@@ -9568,22 +9575,6 @@ public final class Settings {
public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants";
/**
- * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
- * </pre>
- *
- * <p>
- * Type: string
- * @hide
- * see also android.view.textclassifier.TextClassifierConstants
- */
- public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
-
- /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/android/service/autofill/AutofillService.java b/android/service/autofill/AutofillService.java
index 953501c7..2e59f6c5 100644
--- a/android/service/autofill/AutofillService.java
+++ b/android/service/autofill/AutofillService.java
@@ -65,7 +65,7 @@ import com.android.internal.os.SomeArgs;
* <li>The service replies through {@link FillCallback#onSuccess(FillResponse)}.
* <li>The Android System calls {@link #onDisconnected()} and unbinds from the
* {@code AutofillService}.
- * <li>The Android System displays an autofill UI with the options sent by the service.
+ * <li>The Android System displays an UI affordance with the options sent by the service.
* <li>The user picks an option.
* <li>The proper views are autofilled.
* </ol>
@@ -365,81 +365,6 @@ import com.android.internal.os.SomeArgs;
* <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
* verifications above, as long as the service can verify the authenticity of the browser app by
* checking its signing certificate.
- *
- * <a name="MultipleStepsSave"></a>
- * <h3>Saving when data is split in multiple screens</h3>
- *
- * Apps often split the user data in multiple screens in the same activity, specially in
- * activities used to create a new user account. For example, the first screen asks for a username,
- * and if the username is available, it moves to a second screen, which asks for a password.
- *
- * <p>It's tricky to handle save for autofill in these situations, because the autofill service must
- * wait until the user enters both fields before the autofill save UI can be shown. But it can be
- * done by following the steps below:
- *
- * <ol>
- * <li>In the first
- * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
- * adds a {@link FillResponse.Builder#setClientState(android.os.Bundle) client state bundle} in
- * the response, containing the autofill ids of the partial fields present in the screen.
- * <li>In the second
- * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
- * retrieves the {@link FillRequest#getClientState() client state bundle}, gets the autofill ids
- * set in the previous request from the client state, and adds these ids and the
- * {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} to the {@link SaveInfo} used in the second
- * response.
- * <li>In the {@link #onSaveRequest(SaveRequest, SaveCallback) save request}, the service uses the
- * proper {@link FillContext fill contexts} to get the value of each field (there is one fill
- * context per fill request).
- * </ol>
- *
- * <p>For example, in an app that uses 2 steps for the username and password fields, the workflow
- * would be:
- * <pre class="prettyprint">
- * // On first fill request
- * AutofillId usernameId = // parse from AssistStructure;
- * Bundle clientState = new Bundle();
- * clientState.putParcelable("usernameId", usernameId);
- * fillCallback.onSuccess(
- * new FillResponse.Builder()
- * .setClientState(clientState)
- * .setSaveInfo(new SaveInfo
- * .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME, new AutofillId[] {usernameId})
- * .build())
- * .build());
- *
- * // On second fill request
- * Bundle clientState = fillRequest.getClientState();
- * AutofillId usernameId = clientState.getParcelable("usernameId");
- * AutofillId passwordId = // parse from AssistStructure
- * clientState.putParcelable("passwordId", passwordId);
- * fillCallback.onSuccess(
- * new FillResponse.Builder()
- * .setClientState(clientState)
- * .setSaveInfo(new SaveInfo
- * .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
- * new AutofillId[] {usernameId, passwordId})
- * .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
- * .build())
- * .build());
- *
- * // On save request
- * Bundle clientState = saveRequest.getClientState();
- * AutofillId usernameId = clientState.getParcelable("usernameId");
- * AutofillId passwordId = clientState.getParcelable("passwordId");
- * List<FillContext> fillContexts = saveRequest.getFillContexts();
- *
- * FillContext usernameContext = fillContexts.get(0);
- * ViewNode usernameNode = findNodeByAutofillId(usernameContext.getStructure(), usernameId);
- * AutofillValue username = usernameNode.getAutofillValue().getTextValue().toString();
- *
- * FillContext passwordContext = fillContexts.get(1);
- * ViewNode passwordNode = findNodeByAutofillId(passwordContext.getStructure(), passwordId);
- * AutofillValue password = passwordNode.getAutofillValue().getTextValue().toString();
- *
- * save(username, password);
- *
- * </pre>
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/android/service/autofill/SaveInfo.java b/android/service/autofill/SaveInfo.java
index fde2416f..1b9240cc 100644
--- a/android/service/autofill/SaveInfo.java
+++ b/android/service/autofill/SaveInfo.java
@@ -68,7 +68,7 @@ import java.util.Arrays;
* .build();
* </pre>
*
- * <p>The save type flags are used to display the appropriate strings in the autofill save UI.
+ * <p>The save type flags are used to display the appropriate strings in the save UI affordance.
* You can pass multiple values, but try to keep it short if possible. In the above example, just
* {@code SaveInfo.SAVE_DATA_TYPE_PASSWORD} would be enough.
*
@@ -103,17 +103,13 @@ import java.util.Arrays;
* .build();
* </pre>
*
- * <a name="TriggeringSaveRequest"></a>
- * <h3>Triggering a save request</h3>
- *
* <p>The {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} can be triggered after
* any of the following events:
* <ul>
* <li>The {@link Activity} finishes.
- * <li>The app explicitly calls {@link AutofillManager#commit()}.
- * <li>All required views become invisible (if the {@link SaveInfo} was created with the
+ * <li>The app explicitly called {@link AutofillManager#commit()}.
+ * <li>All required views became invisible (if the {@link SaveInfo} was created with the
* {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag).
- * <li>The user clicks a specific view (defined by {@link Builder#setTriggerId(AutofillId)}.
* </ul>
*
* <p>But it is only triggered when all conditions below are met:
@@ -127,13 +123,10 @@ import java.util.Arrays;
* <li>There is no {@link Dataset} in the last {@link FillResponse} that completely matches the
* screen state (i.e., all required and optional fields in the dataset have the same value as
* the fields in the screen).
- * <li>The user explicitly tapped the autofill save UI asking to save data for autofill.
+ * <li>The user explicitly tapped the UI affordance asking to save data for autofill.
* </ul>
*
- * <a name="CustomizingSaveUI"></a>
- * <h3>Customizing the autofill save UI</h3>
- *
- * <p>The service can also customize some aspects of the autofill save UI:
+ * <p>The service can also customize some aspects of the save UI affordance:
* <ul>
* <li>Add a simple subtitle by calling {@link Builder#setDescription(CharSequence)}.
* <li>Add a customized subtitle by calling
@@ -219,25 +212,16 @@ public final class SaveInfo implements Parcelable {
@interface SaveDataType{}
/**
- * Usually, a save request is only automatically <a href="#TriggeringSaveRequest">triggered</a>
- * once the {@link Activity} finishes. If this flag is set, it is triggered once all saved views
- * become invisible.
+ * Usually {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
+ * is called once the {@link Activity} finishes. If this flag is set it is called once all
+ * saved views become invisible.
*/
public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
- /**
- * By default, a save request is automatically <a href="#TriggeringSaveRequest">triggered</a>
- * once the {@link Activity} finishes. If this flag is set, finishing the activity doesn't
- * trigger a save request.
- *
- * <p>This flag is typically used in conjunction with {@link Builder#setTriggerId(AutofillId)}.
- */
- public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
-
/** @hide */
@IntDef(
flag = true,
- value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE, FLAG_DONT_SAVE_ON_FINISH})
+ value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE})
@Retention(RetentionPolicy.SOURCE)
@interface SaveInfoFlags{}
@@ -252,7 +236,6 @@ public final class SaveInfo implements Parcelable {
private final InternalValidator mValidator;
private final InternalSanitizer[] mSanitizerKeys;
private final AutofillId[][] mSanitizerValues;
- private final AutofillId mTriggerId;
private SaveInfo(Builder builder) {
mType = builder.mType;
@@ -276,7 +259,6 @@ public final class SaveInfo implements Parcelable {
mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
}
}
- mTriggerId = builder.mTriggerId;
}
/** @hide */
@@ -338,12 +320,6 @@ public final class SaveInfo implements Parcelable {
return mSanitizerValues;
}
- /** @hide */
- @Nullable
- public AutofillId getTriggerId() {
- return mTriggerId;
- }
-
/**
* A builder for {@link SaveInfo} objects.
*/
@@ -362,7 +338,6 @@ public final class SaveInfo implements Parcelable {
private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
// Set used to validate against duplicate ids.
private ArraySet<AutofillId> mSanitizerIds;
- private AutofillId mTriggerId;
/**
* Creates a new builder.
@@ -419,15 +394,13 @@ public final class SaveInfo implements Parcelable {
/**
* Sets flags changing the save behavior.
*
- * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE},
- * {@link #FLAG_DONT_SAVE_ON_FINISH}, or {@code 0}.
+ * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or {@code 0}.
* @return This builder.
*/
public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
throwIfDestroyed();
- mFlags = Preconditions.checkFlagsArgument(flags,
- FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH);
+ mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE);
return this;
}
@@ -520,8 +493,8 @@ public final class SaveInfo implements Parcelable {
}
/**
- * Sets an object used to validate the user input - if the input is not valid, the
- * autofill save UI is not shown.
+ * Sets an object used to validate the user input - if the input is not valid, the Save UI
+ * affordance is not shown.
*
* <p>Typically used to validate credit card numbers. Examples:
*
@@ -547,7 +520,7 @@ public final class SaveInfo implements Parcelable {
* );
* </pre>
*
- * <p><b>Note:</b> the example above is just for illustrative purposes; the same validator
+ * <p><b>NOTE: </b>the example above is just for illustrative purposes; the same validator
* could be created using a single regex for the {@code OR} part:
*
* <pre class="prettyprint">
@@ -642,27 +615,6 @@ public final class SaveInfo implements Parcelable {
return this;
}
- /**
- * Explicitly defines the view that should commit the autofill context when clicked.
- *
- * <p>Usually, the save request is only automatically
- * <a href="#TriggeringSaveRequest">triggered</a> after the activity is
- * finished or all relevant views become invisible, but there are scenarios where the
- * autofill context is automatically commited too late
- * &mdash;for example, when the activity manually clears the autofillable views when a
- * button is tapped. This method can be used to trigger the autofill save UI earlier in
- * these scenarios.
- *
- * <p><b>Note:</b> This method should only be used in scenarios where the automatic workflow
- * is not enough, otherwise it could trigger the autofill save UI when it should not&mdash;
- * for example, when the user entered invalid credentials for the autofillable views.
- */
- public @NonNull Builder setTriggerId(@NonNull AutofillId id) {
- throwIfDestroyed();
- mTriggerId = Preconditions.checkNotNull(id);
- return this;
- }
-
/**
* Builds a new {@link SaveInfo} instance.
*
@@ -700,14 +652,13 @@ public final class SaveInfo implements Parcelable {
.append(", description=").append(mDescription)
.append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
mNegativeButtonStyle))
- .append(", flags=").append(mFlags)
- .append(", customDescription=").append(mCustomDescription)
- .append(", validator=").append(mValidator)
+ .append(", mFlags=").append(mFlags)
+ .append(", mCustomDescription=").append(mCustomDescription)
+ .append(", validation=").append(mValidator)
.append(", sanitizerKeys=")
.append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
.append(", sanitizerValues=")
.append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
- .append(", triggerId=").append(mTriggerId)
.append("]").toString();
}
@@ -736,7 +687,6 @@ public final class SaveInfo implements Parcelable {
parcel.writeParcelableArray(mSanitizerValues[i], flags);
}
}
- parcel.writeParcelable(mTriggerId, flags);
parcel.writeInt(mFlags);
}
@@ -777,10 +727,6 @@ public final class SaveInfo implements Parcelable {
builder.addSanitizer(sanitizers[i], autofillIds);
}
}
- final AutofillId triggerId = parcel.readParcelable(null);
- if (triggerId != null) {
- builder.setTriggerId(triggerId);
- }
builder.setFlags(parcel.readInt());
return builder.build();
}
diff --git a/android/service/autofill/SaveRequest.java b/android/service/autofill/SaveRequest.java
index f53967bd..65fdb5c4 100644
--- a/android/service/autofill/SaveRequest.java
+++ b/android/service/autofill/SaveRequest.java
@@ -19,6 +19,7 @@ package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,14 +60,9 @@ public final class SaveRequest implements Parcelable {
}
/**
- * Gets the latest client state extra returned from the service.
- *
- * <p><b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state
- * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} where considered. On
- * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of
- * an authenticated request through the
- * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are
- * also considered (and take precedence when set).
+ * Gets the extra client state returned from the last {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}
+ * fill request}.
*
* @return The client state.
*/
diff --git a/android/service/notification/ZenModeConfig.java b/android/service/notification/ZenModeConfig.java
index c5615ae6..7bec898a 100644
--- a/android/service/notification/ZenModeConfig.java
+++ b/android/service/notification/ZenModeConfig.java
@@ -76,13 +76,10 @@ public class ZenModeConfig implements Parcelable {
private static final int DAY_MINUTES = 24 * 60;
private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
- // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
- private static final boolean DEFAULT_ALLOW_ALARMS = true;
- private static final boolean DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER = true;
- private static final boolean DEFAULT_ALLOW_CALLS = false;
+ private static final boolean DEFAULT_ALLOW_CALLS = true;
private static final boolean DEFAULT_ALLOW_MESSAGES = false;
- private static final boolean DEFAULT_ALLOW_REMINDERS = false;
- private static final boolean DEFAULT_ALLOW_EVENTS = false;
+ private static final boolean DEFAULT_ALLOW_REMINDERS = true;
+ private static final boolean DEFAULT_ALLOW_EVENTS = true;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
@@ -92,8 +89,6 @@ public class ZenModeConfig implements Parcelable {
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
private static final String ALLOW_TAG = "allow";
- private static final String ALLOW_ATT_ALARMS = "alarms";
- private static final String ALLOW_ATT_MEDIA = "media_system_other";
private static final String ALLOW_ATT_CALLS = "calls";
private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
private static final String ALLOW_ATT_MESSAGES = "messages";
@@ -105,6 +100,8 @@ public class ZenModeConfig implements Parcelable {
private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
+ private static final String CONDITION_TAG = "condition";
+ private static final String CONDITION_ATT_COMPONENT = "component";
private static final String CONDITION_ATT_ID = "id";
private static final String CONDITION_ATT_SUMMARY = "summary";
private static final String CONDITION_ATT_LINE1 = "line1";
@@ -126,8 +123,6 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_CREATION_TIME = "creationTime";
private static final String RULE_ATT_ENABLER = "enabler";
- public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
- public boolean allowMediaSystemOther = DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER;
public boolean allowCalls = DEFAULT_ALLOW_CALLS;
public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
@@ -166,8 +161,6 @@ public class ZenModeConfig implements Parcelable {
}
allowWhenScreenOff = source.readInt() == 1;
allowWhenScreenOn = source.readInt() == 1;
- allowAlarms = source.readInt() == 1;
- allowMediaSystemOther = source.readInt() == 1;
}
@Override
@@ -197,23 +190,19 @@ public class ZenModeConfig implements Parcelable {
}
dest.writeInt(allowWhenScreenOff ? 1 : 0);
dest.writeInt(allowWhenScreenOn ? 1 : 0);
- dest.writeInt(allowAlarms ? 1 : 0);
- dest.writeInt(allowMediaSystemOther ? 1 : 0);
}
@Override
public String toString() {
return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
.append("user=").append(user)
- .append(",allowAlarms=").append(allowAlarms)
- .append(",allowMediaSystemOther=").append(allowMediaSystemOther)
- .append(",allowReminders=").append(allowReminders)
- .append(",allowEvents=").append(allowEvents)
.append(",allowCalls=").append(allowCalls)
.append(",allowRepeatCallers=").append(allowRepeatCallers)
.append(",allowMessages=").append(allowMessages)
.append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
.append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+ .append(",allowReminders=").append(allowReminders)
+ .append(",allowEvents=").append(allowEvents)
.append(",allowWhenScreenOff=").append(allowWhenScreenOff)
.append(",allowWhenScreenOn=").append(allowWhenScreenOn)
.append(",automaticRules=").append(automaticRules)
@@ -229,21 +218,9 @@ public class ZenModeConfig implements Parcelable {
if (user != to.user) {
d.addLine("user", user, to.user);
}
- if (allowAlarms != to.allowAlarms) {
- d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
- }
- if (allowMediaSystemOther != to.allowMediaSystemOther) {
- d.addLine("allowMediaSystemOther", allowMediaSystemOther, to.allowMediaSystemOther);
- }
if (allowCalls != to.allowCalls) {
d.addLine("allowCalls", allowCalls, to.allowCalls);
}
- if (allowReminders != to.allowReminders) {
- d.addLine("allowReminders", allowReminders, to.allowReminders);
- }
- if (allowEvents != to.allowEvents) {
- d.addLine("allowEvents", allowEvents, to.allowEvents);
- }
if (allowRepeatCallers != to.allowRepeatCallers) {
d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
}
@@ -256,6 +233,12 @@ public class ZenModeConfig implements Parcelable {
if (allowMessagesFrom != to.allowMessagesFrom) {
d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
}
+ if (allowReminders != to.allowReminders) {
+ d.addLine("allowReminders", allowReminders, to.allowReminders);
+ }
+ if (allowEvents != to.allowEvents) {
+ d.addLine("allowEvents", allowEvents, to.allowEvents);
+ }
if (allowWhenScreenOff != to.allowWhenScreenOff) {
d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
}
@@ -352,9 +335,7 @@ public class ZenModeConfig implements Parcelable {
if (!(o instanceof ZenModeConfig)) return false;
if (o == this) return true;
final ZenModeConfig other = (ZenModeConfig) o;
- return other.allowAlarms == allowAlarms
- && other.allowMediaSystemOther == allowMediaSystemOther
- && other.allowCalls == allowCalls
+ return other.allowCalls == allowCalls
&& other.allowRepeatCallers == allowRepeatCallers
&& other.allowMessages == allowMessages
&& other.allowCallsFrom == allowCallsFrom
@@ -370,10 +351,10 @@ public class ZenModeConfig implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(allowAlarms, allowMediaSystemOther, allowCalls,
- allowRepeatCallers, allowMessages,
- allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
- allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule);
+ return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
+ allowMessagesFrom, allowReminders, allowEvents, allowWhenScreenOff,
+ allowWhenScreenOn,
+ user, automaticRules, manualRule);
}
private static String toDayList(int[] days) {
@@ -432,12 +413,10 @@ public class ZenModeConfig implements Parcelable {
}
if (type == XmlPullParser.START_TAG) {
if (ALLOW_TAG.equals(tag)) {
- rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
- DEFAULT_ALLOW_CALLS);
+ rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
DEFAULT_ALLOW_REPEAT_CALLERS);
- rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
- DEFAULT_ALLOW_MESSAGES);
+ rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
DEFAULT_ALLOW_REMINDERS);
rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
@@ -459,9 +438,6 @@ public class ZenModeConfig implements Parcelable {
safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
rt.allowWhenScreenOn =
safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
- rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
- rt.allowMediaSystemOther = safeBoolean(parser, ALLOW_ATT_MEDIA,
- DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER);
} else if (MANUAL_TAG.equals(tag)) {
rt.manualRule = readRuleXml(parser);
} else if (AUTOMATIC_TAG.equals(tag)) {
@@ -492,8 +468,6 @@ public class ZenModeConfig implements Parcelable {
out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
- out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
- out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowMediaSystemOther));
out.endTag(null, ALLOW_TAG);
if (manualRule != null) {
@@ -680,12 +654,6 @@ public class ZenModeConfig implements Parcelable {
if (!allowWhenScreenOn) {
suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
}
- if (allowAlarms) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
- }
- if (allowMediaSystemOther) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER;
- }
priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
@@ -712,13 +680,10 @@ public class ZenModeConfig implements Parcelable {
public void applyNotificationPolicy(Policy policy) {
if (policy == null) return;
- allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
- allowMediaSystemOther = (policy.priorityCategories
- & Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0;
- allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
- allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+ allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
+ allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
!= 0;
allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
diff --git a/android/app/slice/Slice.java b/android/slice/Slice.java
index 7f9f74b4..57686548 100644
--- a/android/app/slice/Slice.java
+++ b/android/slice/Slice.java
@@ -14,35 +14,38 @@
* limitations under the License.
*/
-package android.app.slice;
+package android.slice;
+
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_COLOR;
+import static android.slice.SliceItem.TYPE_IMAGE;
+import static android.slice.SliceItem.TYPE_REMOTE_INPUT;
+import static android.slice.SliceItem.TYPE_REMOTE_VIEW;
+import static android.slice.SliceItem.TYPE_SLICE;
+import static android.slice.SliceItem.TYPE_TEXT;
+import static android.slice.SliceItem.TYPE_TIMESTAMP;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentResolver;
-import android.content.IContentProvider;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.widget.RemoteViews;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
/**
* A slice is a piece of app content and actions that can be surfaced outside of the app.
*
* <p>They are constructed using {@link Builder} in a tree structure
* that provides the OS some information about how the content should be displayed.
+ * @hide
*/
public final class Slice implements Parcelable {
@@ -50,7 +53,7 @@ public final class Slice implements Parcelable {
* @hide
*/
@StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
- HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL})
+ HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT})
public @interface SliceHint{ }
/**
@@ -99,12 +102,6 @@ public final class Slice implements Parcelable {
* Hint to indicate that this content should not be tinted.
*/
public static final String HINT_NO_TINT = "no_tint";
- /**
- * Hint to indicate that this slice is incomplete and an update will be sent once
- * loading is complete. Slices which contain HINT_PARTIAL will not be cached by the
- * OS and should not be cached by apps.
- */
- public static final String HINT_PARTIAL = "partial";
// These two are coming over from prototyping, but we probably don't want in
// public API, at least not right now.
@@ -112,12 +109,19 @@ public final class Slice implements Parcelable {
* @hide
*/
public static final String HINT_ALT = "alt";
+ /**
+ * @hide
+ */
+ public static final String HINT_PARTIAL = "partial";
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
private Uri mUri;
- Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+ /**
+ * @hide
+ */
+ public Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
mHints = hints;
mItems = items.toArray(new SliceItem[items.size()]);
mUri = uri;
@@ -143,15 +147,15 @@ public final class Slice implements Parcelable {
/**
* @return All child {@link SliceItem}s that this Slice contains.
*/
- public List<SliceItem> getItems() {
- return Arrays.asList(mItems);
+ public SliceItem[] getItems() {
+ return mItems;
}
/**
* @return All hints associated with this Slice.
*/
- public @SliceHint List<String> getHints() {
- return Arrays.asList(mHints);
+ public @SliceHint String[] getHints() {
+ return mHints;
}
/**
@@ -159,14 +163,14 @@ public final class Slice implements Parcelable {
*/
public SliceItem getPrimaryIcon() {
for (SliceItem item : getItems()) {
- if (item.getType() == SliceItem.TYPE_IMAGE) {
+ if (item.getType() == TYPE_IMAGE) {
return item;
}
- if (!(item.getType() == SliceItem.TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
+ if (!(item.getType() == TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
&& !item.hasHint(Slice.HINT_ACTIONS)
&& !item.hasHint(Slice.HINT_LIST_ITEM)
- && (item.getType() != SliceItem.TYPE_ACTION)) {
- SliceItem icon = SliceQuery.find(item, SliceItem.TYPE_IMAGE);
+ && (item.getType() != TYPE_ACTION)) {
+ SliceItem icon = SliceQuery.find(item, TYPE_IMAGE);
if (icon != null) return icon;
}
}
@@ -231,18 +235,10 @@ public final class Slice implements Parcelable {
}
/**
- * Add hints to the Slice being constructed
- */
- public Builder addHints(@SliceHint List<String> hints) {
- return addHints(hints.toArray(new String[hints.size()]));
- }
-
- /**
* Add a sub-slice to the slice being constructed
*/
public Builder addSubSlice(@NonNull Slice slice) {
- mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray(
- new String[slice.getHints().size()])));
+ mItems.add(new SliceItem(slice, TYPE_SLICE, slice.getHints()));
return this;
}
@@ -250,7 +246,7 @@ public final class Slice implements Parcelable {
* Add an action to the slice being constructed
*/
public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
- mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0]));
+ mItems.add(new SliceItem(action, s, TYPE_ACTION, new String[0]));
return this;
}
@@ -258,53 +254,31 @@ public final class Slice implements Parcelable {
* Add text to the slice being constructed
*/
public Builder addText(CharSequence text, @SliceHint String... hints) {
- mItems.add(new SliceItem(text, SliceItem.TYPE_TEXT, hints));
+ mItems.add(new SliceItem(text, TYPE_TEXT, hints));
return this;
}
/**
- * Add text to the slice being constructed
- */
- public Builder addText(CharSequence text, @SliceHint List<String> hints) {
- return addText(text, hints.toArray(new String[hints.size()]));
- }
-
- /**
* Add an image to the slice being constructed
*/
public Builder addIcon(Icon icon, @SliceHint String... hints) {
- mItems.add(new SliceItem(icon, SliceItem.TYPE_IMAGE, hints));
+ mItems.add(new SliceItem(icon, TYPE_IMAGE, hints));
return this;
}
/**
- * Add an image to the slice being constructed
- */
- public Builder addIcon(Icon icon, @SliceHint List<String> hints) {
- return addIcon(icon, hints.toArray(new String[hints.size()]));
- }
-
- /**
* @hide This isn't final
*/
public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) {
- mItems.add(new SliceItem(remoteView, SliceItem.TYPE_REMOTE_VIEW, hints));
+ mItems.add(new SliceItem(remoteView, TYPE_REMOTE_VIEW, hints));
return this;
}
/**
* Add remote input to the slice being constructed
*/
- public Slice.Builder addRemoteInput(RemoteInput remoteInput,
- @SliceHint List<String> hints) {
- return addRemoteInput(remoteInput, hints.toArray(new String[hints.size()]));
- }
-
- /**
- * Add remote input to the slice being constructed
- */
public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) {
- mItems.add(new SliceItem(remoteInput, SliceItem.TYPE_REMOTE_INPUT, hints));
+ mItems.add(new SliceItem(remoteInput, TYPE_REMOTE_INPUT, hints));
return this;
}
@@ -312,33 +286,19 @@ public final class Slice implements Parcelable {
* Add a color to the slice being constructed
*/
public Builder addColor(int color, @SliceHint String... hints) {
- mItems.add(new SliceItem(color, SliceItem.TYPE_COLOR, hints));
+ mItems.add(new SliceItem(color, TYPE_COLOR, hints));
return this;
}
/**
- * Add a color to the slice being constructed
- */
- public Builder addColor(int color, @SliceHint List<String> hints) {
- return addColor(color, hints.toArray(new String[hints.size()]));
- }
-
- /**
* Add a timestamp to the slice being constructed
*/
public Slice.Builder addTimestamp(long time, @SliceHint String... hints) {
- mItems.add(new SliceItem(time, SliceItem.TYPE_TIMESTAMP, hints));
+ mItems.add(new SliceItem(time, TYPE_TIMESTAMP, hints));
return this;
}
/**
- * Add a timestamp to the slice being constructed
- */
- public Slice.Builder addTimestamp(long time, @SliceHint List<String> hints) {
- return addTimestamp(time, hints.toArray(new String[hints.size()]));
- }
-
- /**
* Construct the slice.
*/
public Slice build() {
@@ -362,18 +322,18 @@ public final class Slice implements Parcelable {
* @hide
* @return A string representation of this slice.
*/
- public String toString() {
- return toString("");
+ public String getString() {
+ return getString("");
}
- private String toString(String indent) {
+ private String getString(String indent) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mItems.length; i++) {
sb.append(indent);
- if (mItems[i].getType() == SliceItem.TYPE_SLICE) {
+ if (mItems[i].getType() == TYPE_SLICE) {
sb.append("slice:\n");
- sb.append(mItems[i].getSlice().toString(indent + " "));
- } else if (mItems[i].getType() == SliceItem.TYPE_TEXT) {
+ sb.append(mItems[i].getSlice().getString(indent + " "));
+ } else if (mItems[i].getType() == TYPE_TEXT) {
sb.append("text: ");
sb.append(mItems[i].getText());
sb.append("\n");
@@ -384,34 +344,4 @@ public final class Slice implements Parcelable {
}
return sb.toString();
}
-
- /**
- * Turns a slice Uri into slice content.
- *
- * @param resolver ContentResolver to be used.
- * @param uri The URI to a slice provider
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- */
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) {
- Preconditions.checkNotNull(uri, "uri");
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
- null, extras);
- Bundle.setDefusable(res, true);
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
- }
}
diff --git a/android/app/slice/SliceItem.java b/android/slice/SliceItem.java
index 6e69b051..2827ab9d 100644
--- a/android/app/slice/SliceItem.java
+++ b/android/slice/SliceItem.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.slice;
+package android.slice;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -23,15 +23,13 @@ import android.app.RemoteInput;
import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
+import android.slice.Slice.SliceHint;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.RemoteViews;
import com.android.internal.util.ArrayUtils;
-import java.util.Arrays;
-import java.util.List;
-
/**
* A SliceItem is a single unit in the tree structure of a {@link Slice}.
@@ -49,6 +47,7 @@ import java.util.List;
* The hints that a {@link SliceItem} are a set of strings which annotate
* the content. The hints that are guaranteed to be understood by the system
* are defined on {@link Slice}.
+ * @hide
*/
public final class SliceItem implements Parcelable {
@@ -98,15 +97,14 @@ public final class SliceItem implements Parcelable {
/**
* @hide
*/
- protected @Slice.SliceHint
- String[] mHints;
+ protected @SliceHint String[] mHints;
private final int mType;
private final Object mObj;
/**
* @hide
*/
- public SliceItem(Object obj, @SliceType int type, @Slice.SliceHint String[] hints) {
+ public SliceItem(Object obj, @SliceType int type, @SliceHint String[] hints) {
mHints = hints;
mType = type;
mObj = obj;
@@ -115,7 +113,7 @@ public final class SliceItem implements Parcelable {
/**
* @hide
*/
- public SliceItem(PendingIntent intent, Slice slice, int type, @Slice.SliceHint String[] hints) {
+ public SliceItem(PendingIntent intent, Slice slice, int type, @SliceHint String[] hints) {
this(new Pair<>(intent, slice), type, hints);
}
@@ -123,14 +121,14 @@ public final class SliceItem implements Parcelable {
* Gets all hints associated with this SliceItem.
* @return Array of hints.
*/
- public @NonNull @Slice.SliceHint List<String> getHints() {
- return Arrays.asList(mHints);
+ public @NonNull @SliceHint String[] getHints() {
+ return mHints;
}
/**
* @hide
*/
- public void addHint(@Slice.SliceHint String hint) {
+ public void addHint(@SliceHint String hint) {
mHints = ArrayUtils.appendElement(String.class, mHints, hint);
}
@@ -208,7 +206,7 @@ public final class SliceItem implements Parcelable {
* @param hint The hint to check for
* @return true if this item contains the given hint
*/
- public boolean hasHint(@Slice.SliceHint String hint) {
+ public boolean hasHint(@SliceHint String hint) {
return ArrayUtils.contains(mHints, hint);
}
@@ -236,7 +234,7 @@ public final class SliceItem implements Parcelable {
/**
* @hide
*/
- public boolean hasHints(@Slice.SliceHint String[] hints) {
+ public boolean hasHints(@SliceHint String[] hints) {
if (hints == null) return true;
for (String hint : hints) {
if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
@@ -249,7 +247,7 @@ public final class SliceItem implements Parcelable {
/**
* @hide
*/
- public boolean hasAnyHints(@Slice.SliceHint String[] hints) {
+ public boolean hasAnyHints(@SliceHint String[] hints) {
if (hints == null) return false;
for (String hint : hints) {
if (ArrayUtils.contains(mHints, hint)) {
diff --git a/android/app/slice/SliceProvider.java b/android/slice/SliceProvider.java
index df87b455..4e21371b 100644
--- a/android/app/slice/SliceProvider.java
+++ b/android/slice/SliceProvider.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.slice;
+package android.slice;
import android.Manifest.permission;
import android.content.ContentProvider;
@@ -26,8 +26,6 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
-import android.os.StrictMode;
-import android.os.StrictMode.ThreadPolicy;
import android.util.Log;
import java.util.concurrent.CountDownLatch;
@@ -53,15 +51,10 @@ import java.util.concurrent.CountDownLatch;
* </pre>
*
* @see Slice
+ * @hide
*/
public abstract class SliceProvider extends ContentProvider {
- /**
- * This is the Android platform's MIME type for a slice: URI
- * containing a slice implemented through {@link SliceProvider}.
- */
- public static final String SLICE_TYPE = "vnd.android.slice";
-
private static final String TAG = "SliceProvider";
/**
* @hide
@@ -80,18 +73,8 @@ public abstract class SliceProvider extends ContentProvider {
/**
* Implemented to create a slice. Will be called on the main thread.
- * <p>
- * onBindSlice should return as quickly as possible so that the UI tied
- * to this slice can be responsive. No network or other IO will be allowed
- * during onBindSlice. Any loading that needs to be done should happen
- * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
- * when the app is ready to provide the complete data in onBindSlice.
- * <p>
- *
* @see {@link Slice}.
- * @see {@link Slice#HINT_PARTIAL}
*/
- // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
public abstract Slice onBindSlice(Uri sliceUri);
@Override
@@ -137,11 +120,11 @@ public abstract class SliceProvider extends ContentProvider {
@Override
public final String getType(Uri uri) {
if (DEBUG) Log.d(TAG, "getType " + uri);
- return SLICE_TYPE;
+ return null;
}
@Override
- public Bundle call(String method, String arg, Bundle extras) {
+ public final Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
getContext().enforceCallingPermission(permission.BIND_SLICE,
"Slice binding requires the permission BIND_SLICE");
@@ -160,17 +143,8 @@ public abstract class SliceProvider extends ContentProvider {
CountDownLatch latch = new CountDownLatch(1);
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> {
- ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- try {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyDeath()
- .build());
- output[0] = onBindSlice(sliceUri);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- latch.countDown();
- }
+ output[0] = onBindSlice(sliceUri);
+ latch.countDown();
});
try {
latch.await();
diff --git a/android/app/slice/SliceQuery.java b/android/slice/SliceQuery.java
index d1fe2c90..d99b26a5 100644
--- a/android/app/slice/SliceQuery.java
+++ b/android/slice/SliceQuery.java
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package android.app.slice;
+package android.slice;
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_SLICE;
+
+import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -110,9 +114,7 @@ public class SliceQuery {
* @hide
*/
public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) {
- List<String> h = s.getHints();
- return find(new SliceItem(s, SliceItem.TYPE_SLICE, h.toArray(new String[h.size()])), type,
- hints, nonHints);
+ return find(new SliceItem(s, TYPE_SLICE, s.getHints()), type, hints, nonHints);
}
/**
@@ -138,9 +140,8 @@ public class SliceQuery {
@Override
public SliceItem next() {
SliceItem item = items.poll();
- if (item.getType() == SliceItem.TYPE_SLICE
- || item.getType() == SliceItem.TYPE_ACTION) {
- items.addAll(item.getSlice().getItems());
+ if (item.getType() == TYPE_SLICE || item.getType() == TYPE_ACTION) {
+ items.addAll(Arrays.asList(item.getSlice().getItems()));
}
return item;
}
diff --git a/android/app/slice/views/ActionRow.java b/android/slice/views/ActionRow.java
index c7d99f7f..93e9c035 100644
--- a/android/app/slice/views/ActionRow.java
+++ b/android/slice/views/ActionRow.java
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewParent;
diff --git a/android/app/slice/views/GridView.java b/android/slice/views/GridView.java
index 6f30c507..18a90f7d 100644
--- a/android/app/slice/views/GridView.java
+++ b/android/slice/views/GridView.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.views.LargeSliceAdapter.SliceListView;
import android.content.Context;
import android.graphics.Color;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.views.LargeSliceAdapter.SliceListView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
@@ -38,7 +38,7 @@ import android.widget.TextView;
import com.android.internal.R;
import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
/**
* @hide
@@ -76,10 +76,10 @@ public class GridView extends LinearLayout implements SliceListView {
removeAllViews();
int total = 1;
if (slice.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> items = slice.getSlice().getItems();
- total = items.size();
+ SliceItem[] items = slice.getSlice().getItems();
+ total = items.length;
for (int i = 0; i < total; i++) {
- SliceItem item = items.get(i);
+ SliceItem item = items[i];
if (isFull()) {
continue;
}
@@ -142,7 +142,7 @@ public class GridView extends LinearLayout implements SliceListView {
// TODO: Unify sporadic inflates that happen throughout the code.
ArrayList<SliceItem> items = new ArrayList<>();
if (item.getType() == SliceItem.TYPE_SLICE) {
- items.addAll(item.getSlice().getItems());
+ items.addAll(Arrays.asList(item.getSlice().getItems()));
}
items.forEach(i -> {
Context context = getContext();
diff --git a/android/app/slice/views/LargeSliceAdapter.java b/android/slice/views/LargeSliceAdapter.java
index 6794ff98..e77a1b2a 100644
--- a/android/app/slice/views/LargeSliceAdapter.java
+++ b/android/slice/views/LargeSliceAdapter.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.views.LargeSliceAdapter.SliceViewHolder;
import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceViewHolder;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
diff --git a/android/app/slice/views/LargeTemplateView.java b/android/slice/views/LargeTemplateView.java
index 9e225162..d53e8fcb 100644
--- a/android/app/slice/views/LargeTemplateView.java
+++ b/android/slice/views/LargeTemplateView.java
@@ -14,21 +14,22 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.views.SliceView.SliceModeView;
import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
import android.util.TypedValue;
import com.android.internal.widget.LinearLayoutManager;
import com.android.internal.widget.RecyclerView;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -85,7 +86,7 @@ public class LargeTemplateView extends SliceModeView {
if (slice.hasHint(Slice.HINT_LIST)) {
addList(slice, items);
} else {
- slice.getItems().forEach(item -> {
+ Arrays.asList(slice.getItems()).forEach(item -> {
if (item.hasHint(Slice.HINT_ACTIONS)) {
return;
} else if (item.getType() == SliceItem.TYPE_COLOR) {
@@ -108,7 +109,7 @@ public class LargeTemplateView extends SliceModeView {
}
private void addList(Slice slice, List<SliceItem> items) {
- List<SliceItem> sliceItems = slice.getItems();
+ List<SliceItem> sliceItems = Arrays.asList(slice.getItems());
sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM));
items.addAll(sliceItems);
}
diff --git a/android/app/slice/views/MessageView.java b/android/slice/views/MessageView.java
index 77252bf2..7b03e0bd 100644
--- a/android/app/slice/views/MessageView.java
+++ b/android/slice/views/MessageView.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.views.LargeSliceAdapter.SliceListView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.util.TypedValue;
diff --git a/android/app/slice/views/RemoteInputView.java b/android/slice/views/RemoteInputView.java
index e53cb1ea..a29bb5c0 100644
--- a/android/app/slice/views/RemoteInputView.java
+++ b/android/slice/views/RemoteInputView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.animation.Animator;
import android.app.Notification;
diff --git a/android/app/slice/views/ShortcutView.java b/android/slice/views/ShortcutView.java
index b6790c7d..8fe2f1ac 100644
--- a/android/app/slice/views/ShortcutView.java
+++ b/android/slice/views/ShortcutView.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.views.SliceView.SliceModeView;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
import android.view.ViewGroup;
import com.android.internal.R;
diff --git a/android/app/slice/views/SliceView.java b/android/slice/views/SliceView.java
index 32484fca..f3792481 100644
--- a/android/app/slice/views/SliceView.java
+++ b/android/slice/views/SliceView.java
@@ -14,25 +14,23 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.annotation.StringDef;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
-import java.util.List;
-
/**
* A view that can display a {@link Slice} in different {@link SliceMode}'s.
*
@@ -122,7 +120,7 @@ public class SliceView extends LinearLayout {
*/
public void bindSlice(Uri sliceUri) {
validate(sliceUri);
- Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri);
+ Slice s = mContext.getContentResolver().bindSlice(sliceUri);
bindSlice(s);
}
@@ -203,7 +201,7 @@ public class SliceView extends LinearLayout {
}
// TODO: Smarter mapping here from one state to the next.
SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
- List<SliceItem> items = mCurrentSlice.getItems();
+ SliceItem[] items = mCurrentSlice.getItems();
SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
Slice.HINT_ACTIONS,
Slice.HINT_ALT);
@@ -214,7 +212,7 @@ public class SliceView extends LinearLayout {
addView(mCurrentView);
addView(mActions);
}
- if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
+ if (items.length > 1 || (items.length != 0 && items[0] != actionRow)) {
mCurrentView.setVisibility(View.VISIBLE);
mCurrentView.setSlice(mCurrentSlice);
} else {
@@ -241,7 +239,7 @@ public class SliceView extends LinearLayout {
}
private static void validate(Uri sliceUri) {
- if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
+ if (!ContentResolver.SCHEME_SLICE.equals(sliceUri.getScheme())) {
throw new RuntimeException("Invalid uri " + sliceUri);
}
if (sliceUri.getPathSegments().size() == 0) {
diff --git a/android/app/slice/views/SliceViewUtil.java b/android/slice/views/SliceViewUtil.java
index 19e8e7c9..1b5a6d1e 100644
--- a/android/app/slice/views/SliceViewUtil.java
+++ b/android/slice/views/SliceViewUtil.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.annotation.ColorInt;
import android.content.Context;
diff --git a/android/app/slice/views/SmallTemplateView.java b/android/slice/views/SmallTemplateView.java
index 42b2d213..b0b181ed 100644
--- a/android/app/slice/views/SmallTemplateView.java
+++ b/android/slice/views/SmallTemplateView.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package android.app.slice.views;
+package android.slice.views;
import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.views.LargeSliceAdapter.SliceListView;
-import android.app.slice.views.SliceView.SliceModeView;
import android.content.Context;
import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.slice.views.SliceView.SliceModeView;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -34,6 +34,7 @@ import com.android.internal.R;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -116,7 +117,7 @@ public class SmallTemplateView extends SliceModeView implements SliceListView {
int itemCount = 0;
boolean hasSummary = false;
ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
- slice.getSlice().getItems());
+ Arrays.asList(slice.getSlice().getItems()));
for (int i = 0; i < sliceItems.size(); i++) {
SliceItem item = sliceItems.get(i);
if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
@@ -139,9 +140,9 @@ public class SmallTemplateView extends SliceModeView implements SliceListView {
mEndContainer.addView(tv);
itemCount++;
} else if (item.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> subItems = item.getSlice().getItems();
- for (int j = 0; j < subItems.size(); j++) {
- sliceItems.add(subItems.get(j));
+ SliceItem[] subItems = item.getSlice().getItems();
+ for (int j = 0; j < subItems.length; j++) {
+ sliceItems.add(subItems[j]);
}
}
}
@@ -150,8 +151,7 @@ public class SmallTemplateView extends SliceModeView implements SliceListView {
@Override
public void setSlice(Slice slice) {
- setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE,
- slice.getHints().toArray(new String[slice.getHints().size()])));
+ setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints()));
}
/**
diff --git a/android/support/LibraryVersions.java b/android/support/LibraryVersions.java
index 72f7fb18..a046d95e 100644
--- a/android/support/LibraryVersions.java
+++ b/android/support/LibraryVersions.java
@@ -45,17 +45,15 @@ public class LibraryVersions {
*/
public static final Version PAGING = new Version("1.0.0-alpha3");
- private static final Version LIFECYCLES = new Version("1.0.2");
-
/**
* Version code for Lifecycle libs that are required by the support library
*/
- public static final Version LIFECYCLES_CORE = LIFECYCLES;
+ public static final Version LIFECYCLES_CORE = new Version("1.0.2");
/**
* Version code for Lifecycle runtime libs that are required by the support library
*/
- public static final Version LIFECYCLES_RUNTIME = LIFECYCLES;
+ public static final Version LIFECYCLES_RUNTIME = new Version("1.0.0");
/**
* Version code for shared code of flatfoot
diff --git a/android/support/car/drawer/CarDrawerActivity.java b/android/support/car/drawer/CarDrawerActivity.java
deleted file mode 100644
index 7100218a..00000000
--- a/android/support/car/drawer/CarDrawerActivity.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.drawer;
-
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Common base Activity for car apps that need to present a Drawer.
- *
- * <p>This Activity manages the overall layout. To use it, sub-classes need to:
- *
- * <ul>
- * <li>Provide the root-items for the Drawer by implementing {@link #getRootAdapter()}.
- * <li>Add their main content using {@link #setMainContent(int)} or {@link #setMainContent(View)}.
- * They can also add fragments to the main-content container by obtaining its id using
- * {@link #getContentContainerId()}
- * </ul>
- *
- * <p>This class will take care of drawer toggling and display.
- *
- * <p>The rootAdapter can implement nested-navigation, in its click-handling, by passing the
- * CarDrawerAdapter for the next level to
- * {@link CarDrawerController#switchToAdapter(CarDrawerAdapter)}.
- *
- * <p>Any Activity's based on this class need to set their theme to CarDrawerActivityTheme or a
- * derivative.
- */
-public abstract class CarDrawerActivity extends AppCompatActivity {
- private CarDrawerController mDrawerController;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.car_drawer_activity);
-
- DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
- ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(
- this /* activity */,
- drawerLayout, /* DrawerLayout object */
- R.string.car_drawer_open,
- R.string.car_drawer_close);
-
- Toolbar toolbar = findViewById(R.id.car_toolbar);
- setSupportActionBar(toolbar);
-
- mDrawerController = new CarDrawerController(toolbar, drawerLayout, drawerToggle);
- mDrawerController.setRootAdapter(getRootAdapter());
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
- }
-
- /**
- * Returns the {@link CarDrawerController} that is responsible for handling events relating
- * to the drawer in this Activity.
- *
- * @return The {@link CarDrawerController} linked to this Activity. This value will be
- * {@code null} if this method is called before {@code onCreate()} has been called.
- */
- @Nullable
- protected CarDrawerController getDrawerController() {
- return mDrawerController;
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- mDrawerController.syncState();
- }
-
- /**
- * @return Adapter for root content of the Drawer.
- */
- protected abstract CarDrawerAdapter getRootAdapter();
-
- /**
- * Set main content to display in this Activity. It will be added to R.id.content_frame in
- * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(View)}.
- *
- * @param view View to display as main content.
- */
- public void setMainContent(View view) {
- ViewGroup parent = findViewById(getContentContainerId());
- parent.addView(view);
- }
-
- /**
- * Set main content to display in this Activity. It will be added to R.id.content_frame in
- * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(int)}.
- *
- * @param resourceId Layout to display as main content.
- */
- public void setMainContent(@LayoutRes int resourceId) {
- ViewGroup parent = findViewById(getContentContainerId());
- LayoutInflater inflater = getLayoutInflater();
- inflater.inflate(resourceId, parent, true);
- }
-
- /**
- * Get the id of the main content Container which is a FrameLayout. Subclasses can add their own
- * content/fragments inside here.
- *
- * @return Id of FrameLayout where main content of the subclass Activity can be added.
- */
- protected int getContentContainerId() {
- return R.id.content_frame;
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mDrawerController.closeDrawer();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mDrawerController.onConfigurationChanged(newConfig);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return mDrawerController.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
- }
-}
diff --git a/android/support/car/drawer/CarDrawerAdapter.java b/android/support/car/drawer/CarDrawerAdapter.java
deleted file mode 100644
index b0fd965d..00000000
--- a/android/support/car/drawer/CarDrawerAdapter.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.drawer;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.car.widget.PagedListView;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Base adapter for displaying items in the car navigation drawer, which uses a
- * {@link PagedListView}.
- *
- * <p>Subclasses must set the title that will be displayed when displaying the contents of the
- * drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later on. The
- * title of the root adapter will also be the main title showed in the toolbar when the drawer is
- * closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more information.
- *
- * <p>This class also takes care of implementing the PageListView.ItemCamp contract and subclasses
- * should implement {@link #getActualItemCount()}.
- */
-public abstract class CarDrawerAdapter extends RecyclerView.Adapter<DrawerItemViewHolder>
- implements PagedListView.ItemCap, DrawerItemClickListener {
- private final boolean mShowDisabledListOnEmpty;
- private final Drawable mEmptyListDrawable;
- private int mMaxItems = PagedListView.ItemCap.UNLIMITED;
- private CharSequence mTitle;
- private TitleChangeListener mTitleChangeListener;
-
- /**
- * Interface for a class that will be notified a new title has been set on this adapter.
- */
- interface TitleChangeListener {
- /**
- * Called when {@link #setTitle(CharSequence)} has been called and the title has been
- * changed.
- */
- void onTitleChanged(CharSequence newTitle);
- }
-
- protected CarDrawerAdapter(Context context, boolean showDisabledListOnEmpty) {
- mShowDisabledListOnEmpty = showDisabledListOnEmpty;
-
- mEmptyListDrawable = context.getDrawable(R.drawable.ic_list_view_disable);
- mEmptyListDrawable.setColorFilter(context.getColor(R.color.car_tint),
- PorterDuff.Mode.SRC_IN);
- }
-
- /** Returns the title set via {@link #setTitle(CharSequence)}. */
- CharSequence getTitle() {
- return mTitle;
- }
-
- /** Updates the title to display in the toolbar for this Adapter. */
- public final void setTitle(@NonNull CharSequence title) {
- if (title == null) {
- throw new IllegalArgumentException("setTitle() cannot be passed a null title!");
- }
-
- mTitle = title;
-
- if (mTitleChangeListener != null) {
- mTitleChangeListener.onTitleChanged(mTitle);
- }
- }
-
- /** Sets a listener to be notified whenever the title of this adapter has been changed. */
- void setTitleChangeListener(@Nullable TitleChangeListener listener) {
- mTitleChangeListener = listener;
- }
-
- @Override
- public final void setMaxItems(int maxItems) {
- mMaxItems = maxItems;
- }
-
- @Override
- public final int getItemCount() {
- if (shouldShowDisabledListItem()) {
- return 1;
- }
- return mMaxItems >= 0 ? Math.min(mMaxItems, getActualItemCount()) : getActualItemCount();
- }
-
- /**
- * Returns the absolute number of items that can be displayed in the list.
- *
- * <p>A class should implement this method to supply the number of items to be displayed.
- * Returning 0 from this method will cause an empty list icon to be displayed in the drawer.
- *
- * <p>A class should override this method rather than {@link #getItemCount()} because that
- * method is handling the logic of when to display the empty list icon. It will return 1 when
- * {@link #getActualItemCount()} returns 0.
- *
- * @return The number of items to be displayed in the list.
- */
- protected abstract int getActualItemCount();
-
- @Override
- public final int getItemViewType(int position) {
- if (shouldShowDisabledListItem()) {
- return R.layout.car_drawer_list_item_empty;
- }
-
- return usesSmallLayout(position)
- ? R.layout.car_drawer_list_item_small
- : R.layout.car_drawer_list_item_normal;
- }
-
- /**
- * Used to indicate the layout used for the Drawer item at given position. Subclasses can
- * override this to use normal layout which includes text element below title.
- *
- * <p>A small layout is presented by the layout {@code R.layout.car_drawer_list_item_small}.
- * Otherwise, the layout {@code R.layout.car_drawer_list_item_normal} will be used.
- *
- * @param position Adapter position of item.
- * @return Whether the item at this position will use a small layout (default) or normal layout.
- */
- protected boolean usesSmallLayout(int position) {
- return true;
- }
-
- @Override
- public final DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- return new DrawerItemViewHolder(view);
- }
-
- @Override
- public final void onBindViewHolder(DrawerItemViewHolder holder, int position) {
- if (shouldShowDisabledListItem()) {
- holder.getTitle().setText(null);
- holder.getIcon().setImageDrawable(mEmptyListDrawable);
- holder.setItemClickListener(null);
- } else {
- holder.setItemClickListener(this);
- populateViewHolder(holder, position);
- }
- }
-
- /**
- * Whether or not this adapter should be displaying an empty list icon. The icon is shown if it
- * has been configured to show and there are no items to be displayed.
- */
- private boolean shouldShowDisabledListItem() {
- return mShowDisabledListOnEmpty && getActualItemCount() == 0;
- }
-
- /**
- * Subclasses should set all elements in {@code holder} to populate the drawer-item. If some
- * element is not used, it should be nulled out since these ViewHolder/View's are recycled.
- */
- protected abstract void populateViewHolder(DrawerItemViewHolder holder, int position);
-
- /**
- * Called when this adapter has been popped off the stack and is no longer needed. Subclasses
- * can override to do any necessary cleanup.
- */
- public void cleanup() {}
-}
diff --git a/android/support/car/drawer/CarDrawerController.java b/android/support/car/drawer/CarDrawerController.java
deleted file mode 100644
index 4d9f4e99..00000000
--- a/android/support/car/drawer/CarDrawerController.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.drawer;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.car.widget.PagedListView;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.widget.Toolbar;
-import android.view.Gravity;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ProgressBar;
-
-import java.util.Stack;
-
-/**
- * A controller that will handle the set up of the navigation drawer. It will hook up the
- * necessary buttons for up navigation, as well as expose methods to allow for a drill down
- * navigation.
- */
-public class CarDrawerController {
- /** The amount that the drawer has been opened before its color should be switched. */
- private static final float COLOR_SWITCH_SLIDE_OFFSET = 0.25f;
-
- /**
- * A representation of the hierarchy of navigation being displayed in the list. The ordering of
- * this stack is the order that the user has visited each level. When the user navigates up,
- * the adapters are poopped from this list.
- */
- private final Stack<CarDrawerAdapter> mAdapterStack = new Stack<>();
-
- private final Context mContext;
-
- private final Toolbar mToolbar;
- private final DrawerLayout mDrawerLayout;
- private final ActionBarDrawerToggle mDrawerToggle;
-
- private final PagedListView mDrawerList;
- private final ProgressBar mProgressBar;
- private final View mDrawerContent;
-
- /**
- * Creates a {@link CarDrawerController} that will control the navigation of the drawer given by
- * {@code drawerLayout}.
- *
- * <p>The given {@code drawerLayout} should either have a child View that is inflated from
- * {@code R.layout.car_drawer} or ensure that it three children that have the IDs found in that
- * layout.
- *
- * @param toolbar The {@link Toolbar} that will serve as the action bar for an Activity.
- * @param drawerLayout The top-level container for the window content that shows the
- * interactive drawer.
- * @param drawerToggle The {@link ActionBarDrawerToggle} that bridges the given {@code toolbar}
- * and {@code drawerLayout}.
- */
- public CarDrawerController(Toolbar toolbar,
- DrawerLayout drawerLayout,
- ActionBarDrawerToggle drawerToggle) {
- mToolbar = toolbar;
- mContext = drawerLayout.getContext();
-
- mDrawerLayout = drawerLayout;
-
- mDrawerContent = drawerLayout.findViewById(R.id.drawer_content);
- mDrawerList = drawerLayout.findViewById(R.id.drawer_list);
- mDrawerList.setMaxPages(PagedListView.ItemCap.UNLIMITED);
-
- mProgressBar = drawerLayout.findViewById(R.id.drawer_progress);
-
- mDrawerToggle = drawerToggle;
- setupDrawerToggling();
- }
-
- /**
- * Sets the {@link CarDrawerAdapter} that will function as the root adapter. The contents of
- * this root adapter are shown when the drawer is first opened. It is also the top-most level of
- * navigation in the drawer.
- *
- * @param rootAdapter The adapter that will act as the root. If this value is {@code null}, then
- * this method will do nothing.
- */
- public void setRootAdapter(@Nullable CarDrawerAdapter rootAdapter) {
- if (rootAdapter == null) {
- return;
- }
-
- mAdapterStack.push(rootAdapter);
- setToolbarTitleFrom(rootAdapter);
- mDrawerList.setAdapter(rootAdapter);
- }
-
- /**
- * Switches to use the given {@link CarDrawerAdapter} as the one to supply the list to display
- * in the navigation drawer. The title will also be updated from the adapter.
- *
- * <p>This switch is treated as a navigation to the next level in the drawer. Navigation away
- * from this level will pop the given adapter off and surface contents of the previous adapter
- * that was set via this method. If no such adapter exists, then the root adapter set by
- * {@link #setRootAdapter(CarDrawerAdapter)} will be used instead.
- *
- * @param adapter Adapter for next level of content in the drawer.
- */
- public final void switchToAdapter(CarDrawerAdapter adapter) {
- mAdapterStack.peek().setTitleChangeListener(null);
- mAdapterStack.push(adapter);
- switchToAdapterInternal(adapter);
- }
-
- /** Close the drawer. */
- public void closeDrawer() {
- if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
- mDrawerLayout.closeDrawer(Gravity.LEFT);
- }
- }
-
- /** Opens the drawer. */
- public void openDrawer() {
- if (!mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
- mDrawerLayout.openDrawer(Gravity.LEFT);
- }
- }
-
- /** Sets a listener to be notified of Drawer events. */
- public void addDrawerListener(@NonNull DrawerLayout.DrawerListener listener) {
- mDrawerLayout.addDrawerListener(listener);
- }
-
- /** Removes a listener to be notified of Drawer events. */
- public void removeDrawerListener(@NonNull DrawerLayout.DrawerListener listener) {
- mDrawerLayout.removeDrawerListener(listener);
- }
-
- /**
- * Sets whether the loading progress bar is displayed in the navigation drawer. If {@code true},
- * the progress bar is displayed and the navigation list is hidden and vice versa.
- */
- public void showLoadingProgressBar(boolean show) {
- mDrawerList.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
- mProgressBar.setVisibility(show ? View.VISIBLE : View.GONE);
- }
-
- /** Scroll to given position in the list. */
- public void scrollToPosition(int position) {
- mDrawerList.getRecyclerView().smoothScrollToPosition(position);
- }
-
- /**
- * Retrieves the title from the given {@link CarDrawerAdapter} and set its as the title of this
- * controller's internal Toolbar.
- */
- private void setToolbarTitleFrom(CarDrawerAdapter adapter) {
- if (adapter.getTitle() == null) {
- throw new RuntimeException("CarDrawerAdapter must supply a title via setTitle()");
- }
-
- mToolbar.setTitle(adapter.getTitle());
- adapter.setTitleChangeListener(mToolbar::setTitle);
- }
-
- /**
- * Sets up the necessary listeners for {@link DrawerLayout} so that the navigation drawer
- * hierarchy is properly displayed.
- */
- private void setupDrawerToggling() {
- mDrawerLayout.addDrawerListener(mDrawerToggle);
- mDrawerLayout.addDrawerListener(
- new DrawerLayout.DrawerListener() {
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- // Correctly set the title and arrow colors as they are different between
- // the open and close states.
- updateTitleAndArrowColor(slideOffset >= COLOR_SWITCH_SLIDE_OFFSET);
- }
-
- @Override
- public void onDrawerClosed(View drawerView) {
- // If drawer is closed, revert stack/drawer to initial root state.
- cleanupStackAndShowRoot();
- scrollToPosition(0);
- }
-
- @Override
- public void onDrawerOpened(View drawerView) {}
-
- @Override
- public void onDrawerStateChanged(int newState) {}
- });
- }
-
- /** Sets the title and arrow color of the drawer depending on if it is open or not. */
- private void updateTitleAndArrowColor(boolean drawerOpen) {
- // When the drawer is open, use car_title, which resolves to appropriate color depending on
- // day-night mode. When drawer is closed, we always use light color.
- int titleColorResId = drawerOpen ? R.color.car_title : R.color.car_title_light;
- int titleColor = mContext.getColor(titleColorResId);
- mToolbar.setTitleTextColor(titleColor);
- mDrawerToggle.getDrawerArrowDrawable().setColor(titleColor);
- }
-
- /**
- * Synchronizes the display of the drawer with its linked {@link DrawerLayout}.
- *
- * <p>This should be called from the associated Activity's
- * {@link android.support.v7.app.AppCompatActivity#onPostCreate(Bundle)} method to synchronize
- * after teh DRawerLayout's instance state has been restored, and any other time when the
- * state may have diverged in such a way that this controller's associated
- * {@link ActionBarDrawerToggle} had not been notified.
- */
- public void syncState() {
- mDrawerToggle.syncState();
-
- // In case we're restarting after a config change (e.g. day, night switch), set colors
- // again. Doing it here so that Drawer state is fully synced and we know if its open or not.
- // NOTE: isDrawerOpen must be passed the second child of the DrawerLayout.
- updateTitleAndArrowColor(mDrawerLayout.isDrawerOpen(mDrawerContent));
- }
-
- /**
- * Notify this controller that device configurations may have changed.
- *
- * <p>This method should be called from the associated Activity's
- * {@code onConfigurationChanged()} method.
- */
- public void onConfigurationChanged(Configuration newConfig) {
- // Pass any configuration change to the drawer toggle.
- mDrawerToggle.onConfigurationChanged(newConfig);
- }
-
- /**
- * An analog to an Activity's {@code onOptionsItemSelected()}. This method should be called
- * when the Activity's method is called and will return {@code true} if the selection has
- * been handled.
- *
- * @return {@code true} if the item processing was handled by this class.
- */
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle home-click and see if we can navigate up in the drawer.
- if (item != null && item.getItemId() == android.R.id.home && maybeHandleUpClick()) {
- return true;
- }
-
- // DrawerToggle gets next chance to handle up-clicks (and any other clicks).
- return mDrawerToggle.onOptionsItemSelected(item);
- }
-
- /**
- * Sets the navigation drawer's title to be the one supplied by the given adapter and updates
- * the navigation drawer list with the adapter's contents.
- */
- private void switchToAdapterInternal(CarDrawerAdapter adapter) {
- setToolbarTitleFrom(adapter);
- // NOTE: We don't use swapAdapter() since different levels in the Drawer may switch between
- // car_drawer_list_item_normal, car_drawer_list_item_small and car_list_empty layouts.
- mDrawerList.getRecyclerView().setAdapter(adapter);
- scrollToPosition(0);
- }
-
- /**
- * Switches to the previous level in the drawer hierarchy if the current list being displayed
- * is not the root adapter. This is analogous to a navigate up.
- *
- * @return {@code true} if a navigate up was possible and executed. {@code false} otherwise.
- */
- private boolean maybeHandleUpClick() {
- // Check if already at the root level.
- if (mAdapterStack.size() <= 1) {
- return false;
- }
-
- CarDrawerAdapter adapter = mAdapterStack.pop();
- adapter.setTitleChangeListener(null);
- adapter.cleanup();
- switchToAdapterInternal(mAdapterStack.peek());
- return true;
- }
-
- /** Clears stack down to root adapter and switches to root adapter. */
- private void cleanupStackAndShowRoot() {
- while (mAdapterStack.size() > 1) {
- CarDrawerAdapter adapter = mAdapterStack.pop();
- adapter.setTitleChangeListener(null);
- adapter.cleanup();
- }
- switchToAdapterInternal(mAdapterStack.peek());
- }
-}
diff --git a/android/support/car/drawer/DrawerItemViewHolder.java b/android/support/car/drawer/DrawerItemViewHolder.java
deleted file mode 100644
index d016b2de..00000000
--- a/android/support/car/drawer/DrawerItemViewHolder.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.car.drawer;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.car.R;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * Re-usable {@link RecyclerView.ViewHolder} for displaying items in the
- * {@link android.support.car.drawer.CarDrawerAdapter}.
- */
-public class DrawerItemViewHolder extends RecyclerView.ViewHolder {
- private final ImageView mIcon;
- private final TextView mTitle;
- private final TextView mText;
- private final ImageView mEndIcon;
-
- DrawerItemViewHolder(View view) {
- super(view);
- mIcon = view.findViewById(R.id.icon);
- if (mIcon == null) {
- throw new IllegalArgumentException("Icon view cannot be null!");
- }
-
- mTitle = view.findViewById(R.id.title);
- if (mTitle == null) {
- throw new IllegalArgumentException("Title view cannot be null!");
- }
-
- // Next two are optional and may be null.
- mText = view.findViewById(R.id.text);
- mEndIcon = view.findViewById(R.id.end_icon);
- }
-
- /** Returns the view that should be used to display the main icon. */
- @NonNull
- public ImageView getIcon() {
- return mIcon;
- }
-
- /** Returns the view that will display the main title. */
- @NonNull
- public TextView getTitle() {
- return mTitle;
- }
-
- /** Returns the view that is used for text that is smaller than the title text. */
- @Nullable
- public TextView getText() {
- return mText;
- }
-
- /** Returns the icon that is displayed at the end of the view. */
- @Nullable
- public ImageView getEndIcon() {
- return mEndIcon;
- }
-
- /**
- * Sets the listener that will be notified when the view held by this ViewHolder has been
- * clicked. Passing {@code null} will clear any previously set listeners.
- */
- void setItemClickListener(@Nullable DrawerItemClickListener listener) {
- itemView.setOnClickListener(listener != null
- ? v -> listener.onItemClick(getAdapterPosition())
- : null);
- }
-}
diff --git a/android/support/car/widget/PagedListView.java b/android/support/car/widget/PagedListView.java
index 46527001..8527c659 100644
--- a/android/support/car/widget/PagedListView.java
+++ b/android/support/car/widget/PagedListView.java
@@ -27,6 +27,7 @@ import android.os.Handler;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.Px;
import android.support.annotation.RestrictTo;
import android.support.annotation.UiThread;
import android.support.car.R;
@@ -60,6 +61,7 @@ public class PagedListView extends FrameLayout {
protected final CarLayoutManager mLayoutManager;
protected final Handler mHandler = new Handler();
private final boolean mScrollBarEnabled;
+ private final boolean mRightGutterEnabled;
private final PagedScrollBarView mScrollBarView;
private int mRowsPerPage = -1;
@@ -96,11 +98,6 @@ public class PagedListView extends FrameLayout {
*/
public interface ItemCap {
/**
- * A value to pass to {@link #setMaxItems(int)} that indicates there should be no limit.
- */
- int UNLIMITED = -1;
-
- /**
* Sets the maximum number of items available in the adapter. A value less than '0' means
* the list should not be capped.
*/
@@ -142,6 +139,7 @@ public class PagedListView extends FrameLayout {
}
LayoutInflater.from(context).inflate(layoutId, this /*root*/, true /*attachToRoot*/);
+ FrameLayout maxWidthLayout = (FrameLayout) findViewById(R.id.recycler_view_container);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.PagedListView, defStyleAttrs, defStyleRes);
mRecyclerView = (CarRecyclerView) findViewById(R.id.recycler_view);
@@ -158,16 +156,6 @@ public class PagedListView extends FrameLayout {
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
mRecyclerView.setItemAnimator(new CarItemAnimator(mLayoutManager));
- boolean offsetScrollBar = a.getBoolean(R.styleable.PagedListView_offsetScrollBar, false);
- if (offsetScrollBar) {
- MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
- params.setMarginStart(getResources().getDimensionPixelSize(
- R.dimen.car_screen_margin_size));
- params.setMarginEnd(
- a.getDimensionPixelSize(R.styleable.PagedListView_listEndMargin, 0));
- mRecyclerView.setLayoutParams(params);
- }
-
if (a.getBoolean(R.styleable.PagedListView_showPagedListViewDivider, true)) {
int dividerStartMargin = a.getDimensionPixelSize(
R.styleable.PagedListView_dividerStartMargin, 0);
@@ -211,20 +199,47 @@ public class PagedListView extends FrameLayout {
}
}
});
-
mScrollBarView.setVisibility(mScrollBarEnabled ? VISIBLE : GONE);
- // Modify the layout the Scroll Bar is not visible.
- if (!mScrollBarEnabled) {
- MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
- params.setMarginStart(0);
- mRecyclerView.setLayoutParams(params);
+ // Modify the layout if the Gutter or the Scroll Bar are not visible.
+ mRightGutterEnabled = a.getBoolean(R.styleable.PagedListView_rightGutterEnabled, false);
+ if (mRightGutterEnabled || !mScrollBarEnabled) {
+ FrameLayout.LayoutParams maxWidthLayoutLayoutParams =
+ (FrameLayout.LayoutParams) maxWidthLayout.getLayoutParams();
+ if (mRightGutterEnabled) {
+ maxWidthLayoutLayoutParams.rightMargin =
+ getResources().getDimensionPixelSize(R.dimen.car_card_margin);
+ }
+ if (!mScrollBarEnabled) {
+ maxWidthLayoutLayoutParams.setMarginStart(0);
+ }
+ maxWidthLayout.setLayoutParams(maxWidthLayoutLayoutParams);
}
setDayNightStyle(DayNightStyle.AUTO);
a.recycle();
}
+ /**
+ * Sets the starting and ending padding for each view in the list.
+ *
+ * @param start The start padding.
+ * @param end The end padding.
+ */
+ public void setListViewStartEndPadding(@Px int start, @Px int end) {
+ int carCardMargin = getResources().getDimensionPixelSize(R.dimen.car_card_margin);
+ int startGutter = mScrollBarEnabled ? carCardMargin : 0;
+ int startPadding = Math.max(start - startGutter, 0);
+ int endGutter = mRightGutterEnabled ? carCardMargin : 0;
+ int endPadding = Math.max(end - endGutter, 0);
+ mRecyclerView.setPaddingRelative(startPadding, mRecyclerView.getPaddingTop(),
+ endPadding, mRecyclerView.getPaddingBottom());
+
+ // Since we're setting padding we'll need to set the clip to padding to the same
+ // value as clip children to ensure that the cards fly off the screen.
+ mRecyclerView.setClipToPadding(mRecyclerView.getClipChildren());
+ }
+
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/android/support/media/tv/BasePreviewProgram.java b/android/support/media/tv/BasePreviewProgram.java
index 39c30140..1423d9d6 100644
--- a/android/support/media/tv/BasePreviewProgram.java
+++ b/android/support/media/tv/BasePreviewProgram.java
@@ -23,13 +23,14 @@ import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
-import android.support.annotation.IntDef;
import android.support.annotation.RestrictTo;
import android.support.media.tv.TvContractCompat.PreviewProgramColumns;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.AspectRatio;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.Availability;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.InteractionType;
+import android.support.media.tv.TvContractCompat.PreviewProgramColumns.Type;
import android.support.media.tv.TvContractCompat.PreviewPrograms;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -54,89 +55,6 @@ public abstract class BasePreviewProgram extends BaseProgram {
private static final int IS_LIVE = 1;
private static final int IS_BROWSABLE = 1;
- /** @hide */
- @IntDef({
- TYPE_UNKNOWN,
- PreviewProgramColumns.TYPE_MOVIE,
- PreviewProgramColumns.TYPE_TV_SERIES,
- PreviewProgramColumns.TYPE_TV_SEASON,
- PreviewProgramColumns.TYPE_TV_EPISODE,
- PreviewProgramColumns.TYPE_CLIP,
- PreviewProgramColumns.TYPE_EVENT,
- PreviewProgramColumns.TYPE_CHANNEL,
- PreviewProgramColumns.TYPE_TRACK,
- PreviewProgramColumns.TYPE_ALBUM,
- PreviewProgramColumns.TYPE_ARTIST,
- PreviewProgramColumns.TYPE_PLAYLIST,
- PreviewProgramColumns.TYPE_STATION,
- PreviewProgramColumns.TYPE_GAME
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- public @interface Type {}
-
- /**
- * The unknown program type.
- */
- private static final int TYPE_UNKNOWN = -1;
-
- /** @hide */
- @IntDef({
- ASPECT_RATIO_UNKNOWN,
- PreviewProgramColumns.ASPECT_RATIO_16_9,
- PreviewProgramColumns.ASPECT_RATIO_3_2,
- PreviewProgramColumns.ASPECT_RATIO_4_3,
- PreviewProgramColumns.ASPECT_RATIO_1_1,
- PreviewProgramColumns.ASPECT_RATIO_2_3,
- PreviewProgramColumns.ASPECT_RATIO_MOVIE_POSTER
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- public @interface AspectRatio {}
-
- /**
- * The aspect ratio for unknown aspect ratios.
- */
- private static final int ASPECT_RATIO_UNKNOWN = -1;
-
- /** @hide */
- @IntDef({
- AVAILABILITY_UNKNOWN,
- PreviewProgramColumns.AVAILABILITY_AVAILABLE,
- PreviewProgramColumns.AVAILABILITY_FREE_WITH_SUBSCRIPTION,
- PreviewProgramColumns.AVAILABILITY_PAID_CONTENT,
- PreviewProgramColumns.AVAILABILITY_PURCHASED,
- PreviewProgramColumns.AVAILABILITY_FREE
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- public @interface Availability {}
-
- /**
- * The unknown availability.
- */
- private static final int AVAILABILITY_UNKNOWN = -1;
-
- /** @hide */
- @IntDef({
- INTERACTION_TYPE_UNKNOWN,
- PreviewProgramColumns.INTERACTION_TYPE_VIEWS,
- PreviewProgramColumns.INTERACTION_TYPE_LISTENS,
- PreviewProgramColumns.INTERACTION_TYPE_FOLLOWERS,
- PreviewProgramColumns.INTERACTION_TYPE_FANS,
- PreviewProgramColumns.INTERACTION_TYPE_LIKES,
- PreviewProgramColumns.INTERACTION_TYPE_THUMBS,
- PreviewProgramColumns.INTERACTION_TYPE_VIEWERS,
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- public @interface InteractionType {}
-
- /**
- * The unknown interaction type.
- */
- private static final int INTERACTION_TYPE_UNKNOWN = -1;
-
BasePreviewProgram(Builder builder) {
super(builder);
}
@@ -209,7 +127,7 @@ public abstract class BasePreviewProgram extends BaseProgram {
*/
public @Type int getType() {
Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_TYPE);
- return i == null ? TYPE_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
@@ -219,7 +137,7 @@ public abstract class BasePreviewProgram extends BaseProgram {
*/
public @AspectRatio int getPosterArtAspectRatio() {
Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO);
- return i == null ? ASPECT_RATIO_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
@@ -229,7 +147,7 @@ public abstract class BasePreviewProgram extends BaseProgram {
*/
public @AspectRatio int getThumbnailAspectRatio() {
Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO);
- return i == null ? ASPECT_RATIO_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
@@ -247,7 +165,7 @@ public abstract class BasePreviewProgram extends BaseProgram {
*/
public @Availability int getAvailability() {
Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_AVAILABILITY);
- return i == null ? AVAILABILITY_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
@@ -298,7 +216,7 @@ public abstract class BasePreviewProgram extends BaseProgram {
*/
public @InteractionType int getInteractionType() {
Integer i = mValues.getAsInteger(PreviewPrograms.COLUMN_INTERACTION_TYPE);
- return i == null ? INTERACTION_TYPE_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
diff --git a/android/support/media/tv/BaseProgram.java b/android/support/media/tv/BaseProgram.java
index 23b5cf9c..e4ce9d1f 100644
--- a/android/support/media/tv/BaseProgram.java
+++ b/android/support/media/tv/BaseProgram.java
@@ -22,16 +22,13 @@ import android.database.Cursor;
import android.media.tv.TvContentRating;
import android.net.Uri;
import android.os.Build;
-import android.support.annotation.IntDef;
import android.support.annotation.RestrictTo;
import android.support.media.tv.TvContractCompat.BaseTvColumns;
import android.support.media.tv.TvContractCompat.ProgramColumns;
+import android.support.media.tv.TvContractCompat.ProgramColumns.ReviewRatingStyle;
import android.support.media.tv.TvContractCompat.Programs;
import android.support.media.tv.TvContractCompat.Programs.Genres.Genre;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Base class for derived classes that want to have common fields for programs defined in
* {@link TvContractCompat}.
@@ -49,22 +46,6 @@ public abstract class BaseProgram {
private static final int IS_SEARCHABLE = 1;
/** @hide */
- @IntDef({
- REVIEW_RATING_STYLE_UNKNOWN,
- ProgramColumns.REVIEW_RATING_STYLE_STARS,
- ProgramColumns.REVIEW_RATING_STYLE_THUMBS_UP_DOWN,
- ProgramColumns.REVIEW_RATING_STYLE_PERCENTAGE,
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- @interface ReviewRatingStyle {}
-
- /**
- * The unknown review rating style.
- */
- private static final int REVIEW_RATING_STYLE_UNKNOWN = -1;
-
- /** @hide */
@RestrictTo(LIBRARY_GROUP)
protected ContentValues mValues;
@@ -273,7 +254,7 @@ public abstract class BaseProgram {
*/
public @ReviewRatingStyle int getReviewRatingStyle() {
Integer i = mValues.getAsInteger(Programs.COLUMN_REVIEW_RATING_STYLE);
- return i == null ? REVIEW_RATING_STYLE_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
diff --git a/android/support/media/tv/Program.java b/android/support/media/tv/Program.java
index 233f1bab..4e3bd7ac 100644
--- a/android/support/media/tv/Program.java
+++ b/android/support/media/tv/Program.java
@@ -25,7 +25,6 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.support.media.tv.TvContractCompat.Programs;
-import android.support.media.tv.TvContractCompat.Programs.Genres.Genre;
/**
* A convenience class to access {@link TvContractCompat.Programs} entries in the system content
@@ -283,7 +282,7 @@ public final class Program extends BaseProgram implements Comparable<Program> {
* @return This Builder object to allow for chaining of calls to builder methods.
* @see Programs#COLUMN_BROADCAST_GENRE
*/
- public Builder setBroadcastGenres(@Genre String[] genres) {
+ public Builder setBroadcastGenres(String[] genres) {
mValues.put(Programs.COLUMN_BROADCAST_GENRE, Programs.Genres.encode(genres));
return this;
}
diff --git a/android/support/media/tv/TvContractCompat.java b/android/support/media/tv/TvContractCompat.java
index de4fd04f..5a46e791 100644
--- a/android/support/media/tv/TvContractCompat.java
+++ b/android/support/media/tv/TvContractCompat.java
@@ -30,6 +30,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.BaseColumns;
+import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
@@ -605,6 +606,16 @@ public final class TvContractCompat {
*/
@RestrictTo(LIBRARY_GROUP)
interface ProgramColumns {
+ /** @hide */
+ @IntDef({
+ REVIEW_RATING_STYLE_STARS,
+ REVIEW_RATING_STYLE_THUMBS_UP_DOWN,
+ REVIEW_RATING_STYLE_PERCENTAGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ @interface ReviewRatingStyle {}
+
/**
* The review rating style for five star rating.
*
@@ -923,6 +934,27 @@ public final class TvContractCompat {
*/
@RestrictTo(LIBRARY_GROUP)
public interface PreviewProgramColumns {
+
+ /** @hide */
+ @IntDef({
+ TYPE_MOVIE,
+ TYPE_TV_SERIES,
+ TYPE_TV_SEASON,
+ TYPE_TV_EPISODE,
+ TYPE_CLIP,
+ TYPE_EVENT,
+ TYPE_CHANNEL,
+ TYPE_TRACK,
+ TYPE_ALBUM,
+ TYPE_ARTIST,
+ TYPE_PLAYLIST,
+ TYPE_STATION,
+ TYPE_GAME
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ public @interface Type {}
+
/**
* The program type for movie.
*
@@ -1014,6 +1046,19 @@ public final class TvContractCompat {
*/
int TYPE_GAME = 12;
+ /** @hide */
+ @IntDef({
+ ASPECT_RATIO_16_9,
+ ASPECT_RATIO_3_2,
+ ASPECT_RATIO_4_3,
+ ASPECT_RATIO_1_1,
+ ASPECT_RATIO_2_3,
+ ASPECT_RATIO_MOVIE_POSTER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ public @interface AspectRatio {}
+
/**
* The aspect ratio for 16:9.
*
@@ -1062,6 +1107,18 @@ public final class TvContractCompat {
*/
int ASPECT_RATIO_MOVIE_POSTER = 5;
+ /** @hide */
+ @IntDef({
+ AVAILABILITY_AVAILABLE,
+ AVAILABILITY_FREE_WITH_SUBSCRIPTION,
+ AVAILABILITY_PAID_CONTENT,
+ AVAILABILITY_PURCHASED,
+ AVAILABILITY_FREE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ public @interface Availability {}
+
/**
* The availability for "available to this user".
*
@@ -1098,6 +1155,20 @@ public final class TvContractCompat {
*/
int AVAILABILITY_FREE = 4;
+ /** @hide */
+ @IntDef({
+ INTERACTION_TYPE_VIEWS,
+ INTERACTION_TYPE_LISTENS,
+ INTERACTION_TYPE_FOLLOWERS,
+ INTERACTION_TYPE_FANS,
+ INTERACTION_TYPE_LIKES,
+ INTERACTION_TYPE_THUMBS,
+ INTERACTION_TYPE_VIEWERS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ public @interface InteractionType {}
+
/**
* The interaction type for "views".
*
@@ -2824,6 +2895,17 @@ public final class TvContractCompat {
/** The MIME type of a single preview TV program. */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
+ /** @hide */
+ @IntDef({
+ WATCH_NEXT_TYPE_CONTINUE,
+ WATCH_NEXT_TYPE_NEXT,
+ WATCH_NEXT_TYPE_NEW,
+ WATCH_NEXT_TYPE_WATCHLIST,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY_GROUP)
+ public @interface WatchNextType {}
+
/**
* The watch next type for CONTINUE. Use this type when the user has already watched more
* than 1 minute of this content.
diff --git a/android/support/media/tv/WatchNextProgram.java b/android/support/media/tv/WatchNextProgram.java
index c192745c..f4665846 100644
--- a/android/support/media/tv/WatchNextProgram.java
+++ b/android/support/media/tv/WatchNextProgram.java
@@ -22,15 +22,12 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.media.tv.TvContentRating; // For javadoc gen of super class
import android.os.Build;
-import android.support.annotation.IntDef;
import android.support.annotation.RestrictTo;
import android.support.media.tv.TvContractCompat.PreviewPrograms; // For javadoc gen of super class
import android.support.media.tv.TvContractCompat.Programs; // For javadoc gen of super class
import android.support.media.tv.TvContractCompat.Programs.Genres; // For javadoc gen of super class
import android.support.media.tv.TvContractCompat.WatchNextPrograms;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import android.support.media.tv.TvContractCompat.WatchNextPrograms.WatchNextType;
/**
* A convenience class to access {@link WatchNextPrograms} entries in the system content
@@ -90,34 +87,16 @@ public final class WatchNextProgram extends BasePreviewProgram {
private static final long INVALID_LONG_VALUE = -1;
private static final int INVALID_INT_VALUE = -1;
- /** @hide */
- @IntDef({
- WATCH_NEXT_TYPE_UNKNOWN,
- WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE,
- WatchNextPrograms.WATCH_NEXT_TYPE_NEXT,
- WatchNextPrograms.WATCH_NEXT_TYPE_NEW,
- WatchNextPrograms.WATCH_NEXT_TYPE_WATCHLIST,
- })
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(LIBRARY_GROUP)
- public @interface WatchNextType {}
-
- /**
- * The unknown watch next type. Use this type when the actual type is not known.
- */
- public static final int WATCH_NEXT_TYPE_UNKNOWN = -1;
-
private WatchNextProgram(Builder builder) {
super(builder);
}
/**
- * @return The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for the program,
- * or {@link #WATCH_NEXT_TYPE_UNKNOWN} if it's unknown.
+ * @return The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for the program.
*/
public @WatchNextType int getWatchNextType() {
Integer i = mValues.getAsInteger(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
- return i == null ? WATCH_NEXT_TYPE_UNKNOWN : i;
+ return i == null ? INVALID_INT_VALUE : i;
}
/**
diff --git a/android/support/mediacompat/testlib/IntentConstants.java b/android/support/mediacompat/testlib/IntentConstants.java
index 57db43e7..bc35935e 100644
--- a/android/support/mediacompat/testlib/IntentConstants.java
+++ b/android/support/mediacompat/testlib/IntentConstants.java
@@ -22,8 +22,6 @@ package android.support.mediacompat.testlib;
public class IntentConstants {
public static final String ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD =
"android.support.mediacompat.service.action.CALL_MEDIA_BROWSER_SERVICE_METHOD";
- public static final String ACTION_CALL_MEDIA_SESSION_METHOD =
- "android.support.mediacompat.service.action.CALL_MEDIA_SESSION_METHOD";
public static final String KEY_METHOD_ID = "method_id";
public static final String KEY_ARGUMENT = "argument";
}
diff --git a/android/support/mediacompat/testlib/MediaSessionConstants.java b/android/support/mediacompat/testlib/MediaSessionConstants.java
deleted file mode 100644
index 82b5c59a..00000000
--- a/android/support/mediacompat/testlib/MediaSessionConstants.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.mediacompat.testlib;
-
-/**
- * Constants for testing the media session and controller.
- */
-public class MediaSessionConstants {
-
- // MediaSessionCompat methods.
- public static final int SET_EXTRAS = 101;
- public static final int SET_FLAGS = 102;
- public static final int SET_METADATA = 103;
- public static final int SET_PLAYBACK_STATE = 104;
- public static final int SET_QUEUE = 105;
- public static final int SET_QUEUE_TITLE = 106;
- public static final int SET_SESSION_ACTIVITY = 107;
- public static final int SET_CAPTIONING_ENABLED = 108;
- public static final int SET_REPEAT_MODE = 109;
- public static final int SET_SHUFFLE_MODE = 110;
- public static final int SEND_SESSION_EVENT = 112;
- public static final int SET_ACTIVE = 113;
- public static final int RELEASE = 114;
- public static final int SET_PLAYBACK_TO_LOCAL = 115;
- public static final int SET_PLAYBACK_TO_REMOTE = 116;
- public static final int SET_RATING_TYPE = 117;
-
- public static final String SERVICE_PACKAGE_NAME = "android.support.mediacompat.service.test";
- public static final String TEST_KEY = "test-key";
- public static final String TEST_VALUE = "test-val";
- public static final String TEST_SESSION_EVENT = "test-session-event";
- public static final int TEST_FLAGS = 5;
- public static final int TEST_CURRENT_VOLUME = 10;
- public static final int TEST_MAX_VOLUME = 11;
- public static final long TEST_QUEUE_ID_1 = 10L;
- public static final long TEST_QUEUE_ID_2 = 20L;
- public static final String TEST_MEDIA_ID_1 = "media_id_1";
- public static final String TEST_MEDIA_ID_2 = "media_id_2";
- public static final long TEST_ACTION = 55L;
-
- public static final int TEST_ERROR_CODE = 0x3;
- public static final String TEST_ERROR_MSG = "test-error-msg";
-}
diff --git a/android/support/transition/AutoTransition.java b/android/support/transition/AutoTransition.java
index bf39c3c3..02b49e26 100644
--- a/android/support/transition/AutoTransition.java
+++ b/android/support/transition/AutoTransition.java
@@ -45,9 +45,9 @@ public class AutoTransition extends TransitionSet {
private void init() {
setOrdering(ORDERING_SEQUENTIAL);
- addTransition(new Fade(Fade.OUT))
- .addTransition(new ChangeBounds())
- .addTransition(new Fade(Fade.IN));
+ addTransition(new Fade(Fade.OUT)).
+ addTransition(new ChangeBounds()).
+ addTransition(new Fade(Fade.IN));
}
}
diff --git a/android/support/v17/leanback/widget/GridLayoutManager.java b/android/support/v17/leanback/widget/GridLayoutManager.java
index af37f77a..81431972 100644
--- a/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -2240,24 +2240,10 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
focusToViewInLayout(hadFocus, scrollToFocus, -deltaPrimary, -deltaSecondary);
appendVisibleItems();
prependVisibleItems();
- // b/67370222: do not removeInvisibleViewsAtFront/End() in the loop, otherwise
- // loop may bounce between scroll forward and scroll backward forever. Example:
- // Assuming there are 19 items, child#18 and child#19 are both in RV, we are
- // trying to focus to child#18 and there are 200px remaining scroll distance.
- // 1 focusToViewInLayout() tries scroll forward 50 px to align focused child#18 on
- // right edge, but there to compensate remaining scroll 200px, also scroll
- // backward 200px, 150px pushes last child#19 out side of right edge.
- // 2 removeInvisibleViewsAtEnd() remove last child#19, updateScrollLimits()
- // invalidates scroll max
- // 3 In next iteration, when scroll max/min is unknown, focusToViewInLayout() will
- // align focused child#18 at center of screen.
- // 4 Because #18 is aligned at center, appendVisibleItems() will fill child#19 to
- // the right.
- // 5 (back to 1 and loop forever)
+ removeInvisibleViewsAtFront();
+ removeInvisibleViewsAtEnd();
} while (mGrid.getFirstVisibleIndex() != oldFirstVisible
|| mGrid.getLastVisibleIndex() != oldLastVisible);
- removeInvisibleViewsAtFront();
- removeInvisibleViewsAtEnd();
if (state.willRunPredictiveAnimations()) {
fillScrapViewsInPostLayout();
diff --git a/android/support/v4/content/res/ResourcesCompat.java b/android/support/v4/content/res/ResourcesCompat.java
index 15b8ce9a..4c70ce93 100644
--- a/android/support/v4/content/res/ResourcesCompat.java
+++ b/android/support/v4/content/res/ResourcesCompat.java
@@ -307,11 +307,11 @@ public final class ResourcesCompat {
*/
@RestrictTo(LIBRARY_GROUP)
public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,
- int style, @Nullable FontCallback fontCallback) throws NotFoundException {
+ int style) throws NotFoundException {
if (context.isRestricted()) {
return null;
}
- return loadFont(context, id, value, style, fontCallback, null /* handler */,
+ return loadFont(context, id, value, style, null /* callback */, null /* handler */,
true /* isXmlRequest */);
}
diff --git a/android/support/v4/media/RatingCompat.java b/android/support/v4/media/RatingCompat.java
index e70243f8..b538cac4 100644
--- a/android/support/v4/media/RatingCompat.java
+++ b/android/support/v4/media/RatingCompat.java
@@ -18,7 +18,6 @@ package android.support.v4.media;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import android.media.Rating;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -327,25 +326,25 @@ public final class RatingCompat implements Parcelable {
*/
public static RatingCompat fromRating(Object ratingObj) {
if (ratingObj != null && Build.VERSION.SDK_INT >= 19) {
- final int ratingStyle = ((Rating) ratingObj).getRatingStyle();
+ final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj);
final RatingCompat rating;
- if (((Rating) ratingObj).isRated()) {
+ if (RatingCompatKitkat.isRated(ratingObj)) {
switch (ratingStyle) {
case RATING_HEART:
- rating = newHeartRating(((Rating) ratingObj).hasHeart());
+ rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj));
break;
case RATING_THUMB_UP_DOWN:
- rating = newThumbRating(((Rating) ratingObj).isThumbUp());
+ rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj));
break;
case RATING_3_STARS:
case RATING_4_STARS:
case RATING_5_STARS:
rating = newStarRating(ratingStyle,
- ((Rating) ratingObj).getStarRating());
+ RatingCompatKitkat.getStarRating(ratingObj));
break;
case RATING_PERCENTAGE:
rating = newPercentageRating(
- ((Rating) ratingObj).getPercentRating());
+ RatingCompatKitkat.getPercentRating(ratingObj));
break;
default:
return null;
@@ -373,25 +372,25 @@ public final class RatingCompat implements Parcelable {
if (isRated()) {
switch (mRatingStyle) {
case RATING_HEART:
- mRatingObj = Rating.newHeartRating(hasHeart());
+ mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart());
break;
case RATING_THUMB_UP_DOWN:
- mRatingObj = Rating.newThumbRating(isThumbUp());
+ mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp());
break;
case RATING_3_STARS:
case RATING_4_STARS:
case RATING_5_STARS:
- mRatingObj = Rating.newStarRating(mRatingStyle,
+ mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle,
getStarRating());
break;
case RATING_PERCENTAGE:
- mRatingObj = Rating.newPercentageRating(getPercentRating());
+ mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating());
break;
default:
return null;
}
} else {
- mRatingObj = Rating.newUnratedRating(mRatingStyle);
+ mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle);
}
}
return mRatingObj;
diff --git a/android/support/v4/media/RatingCompatKitkat.java b/android/support/v4/media/RatingCompatKitkat.java
new file mode 100644
index 00000000..1d3fa505
--- /dev/null
+++ b/android/support/v4/media/RatingCompatKitkat.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media;
+
+import android.media.Rating;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+class RatingCompatKitkat {
+ public static Object newUnratedRating(int ratingStyle) {
+ return Rating.newUnratedRating(ratingStyle);
+ }
+
+ public static Object newHeartRating(boolean hasHeart) {
+ return Rating.newHeartRating(hasHeart);
+ }
+
+ public static Object newThumbRating(boolean thumbIsUp) {
+ return Rating.newThumbRating(thumbIsUp);
+ }
+
+ public static Object newStarRating(int starRatingStyle, float starRating) {
+ return Rating.newStarRating(starRatingStyle, starRating);
+ }
+
+ public static Object newPercentageRating(float percent) {
+ return Rating.newPercentageRating(percent);
+ }
+
+ public static boolean isRated(Object ratingObj) {
+ return ((Rating)ratingObj).isRated();
+ }
+
+ public static int getRatingStyle(Object ratingObj) {
+ return ((Rating)ratingObj).getRatingStyle();
+ }
+
+ public static boolean hasHeart(Object ratingObj) {
+ return ((Rating)ratingObj).hasHeart();
+ }
+
+ public static boolean isThumbUp(Object ratingObj) {
+ return ((Rating)ratingObj).isThumbUp();
+ }
+
+ public static float getStarRating(Object ratingObj) {
+ return ((Rating)ratingObj).getStarRating();
+ }
+
+ public static float getPercentRating(Object ratingObj) {
+ return ((Rating)ratingObj).getPercentRating();
+ }
+}
diff --git a/android/support/v4/provider/FontsContractCompat.java b/android/support/v4/provider/FontsContractCompat.java
index 09261869..9ef1b0b0 100644
--- a/android/support/v4/provider/FontsContractCompat.java
+++ b/android/support/v4/provider/FontsContractCompat.java
@@ -303,9 +303,6 @@ public class FontsContractCompat {
final ArrayList<ReplyCallback<TypefaceResult>> replies;
synchronized (sLock) {
replies = sPendingReplies.get(id);
- if (replies == null) {
- return; // Nobody requested replies. Do nothing.
- }
sPendingReplies.remove(id);
}
for (int i = 0; i < replies.size(); ++i) {
diff --git a/android/support/v7/app/MediaRouteButton.java b/android/support/v7/app/MediaRouteButton.java
index fdbcf9ad..d3f7020b 100644
--- a/android/support/v7/app/MediaRouteButton.java
+++ b/android/support/v7/app/MediaRouteButton.java
@@ -121,7 +121,8 @@ public class MediaRouteButton extends View {
}
public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
- super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr);
+ super(MediaRouterThemeHelper.createThemedContext(context, defStyleAttr), attrs,
+ defStyleAttr);
context = getContext();
mRouter = MediaRouter.getInstance(context);
diff --git a/android/support/v7/app/MediaRouteChooserDialog.java b/android/support/v7/app/MediaRouteChooserDialog.java
index 17364efb..0ab2eb11 100644
--- a/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/android/support/v7/app/MediaRouteChooserDialog.java
@@ -92,8 +92,10 @@ public class MediaRouteChooserDialog extends AppCompatDialog {
}
public MediaRouteChooserDialog(Context context, int theme) {
- super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, false),
- MediaRouterThemeHelper.createThemedDialogStyle(context));
+ // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+ // which may override our style settings. Passes our uppermost theme ID to prevent this.
+ super(MediaRouterThemeHelper.createThemedContext(context, theme),
+ theme == 0 ? MediaRouterThemeHelper.createThemeForDialog(context, theme) : theme);
context = getContext();
mRouter = MediaRouter.getInstance(context);
diff --git a/android/support/v7/app/MediaRouteControllerDialog.java b/android/support/v7/app/MediaRouteControllerDialog.java
index d89bf21e..4b9a17a3 100644
--- a/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/android/support/v7/app/MediaRouteControllerDialog.java
@@ -201,8 +201,12 @@ public class MediaRouteControllerDialog extends AlertDialog {
}
public MediaRouteControllerDialog(Context context, int theme) {
- super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, true),
- MediaRouterThemeHelper.createThemedDialogStyle(context));
+ // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+ // which may override our style settings. Passes our uppermost theme ID to prevent this.
+ super(MediaRouterThemeHelper.createThemedContext(context,
+ MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme == 0
+ ? MediaRouterThemeHelper.createThemeForDialog(context, MediaRouterThemeHelper
+ .getAlertDialogResolvedTheme(context, theme)) : theme);
mContext = getContext();
mControllerCallback = new MediaControllerCallback();
diff --git a/android/support/v7/app/MediaRouterThemeHelper.java b/android/support/v7/app/MediaRouterThemeHelper.java
index 69e40ac7..9ef218e0 100644
--- a/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/android/support/v7/app/MediaRouterThemeHelper.java
@@ -42,76 +42,47 @@ final class MediaRouterThemeHelper {
private MediaRouterThemeHelper() {
}
- static Context createThemedButtonContext(Context context) {
- // Apply base Media Router theme.
- context = new ContextThemeWrapper(context, getRouterThemeId(context));
-
- // Apply custom Media Router theme.
- int style = getThemeResource(context, R.attr.mediaRouteTheme);
- if (style != 0) {
- context = new ContextThemeWrapper(context, style);
- }
-
- return context;
- }
-
- /*
- * The following two methods are to be used in conjunction. They should be used to prepare
- * the context and theme for a super class constructor (the latter method relies on the
- * former method to properly prepare the context):
- * super(context = createThemedDialogContext(context, theme),
- * createThemedDialogStyle(context));
+ /**
+ * Creates a themed context based on the explicit style resource or the parent context's default
+ * theme.
+ * <p>
+ * The theme which will be applied on top of the parent {@code context}'s theme is determined
+ * by the primary color defined in the given {@code style}, or in the parent {@code context}.
*
- * It will apply theme in the following order (style lookups will be done in reverse):
- * 1) Current theme
- * 2) Supplied theme
- * 3) Base Media Router theme
- * 4) Custom Media Router theme, if provided
+ * @param context the parent context
+ * @param style the resource ID of the style against which to inflate this context, or
+ * {@code 0} to use the parent {@code context}'s default theme.
+ * @return The themed context.
*/
- static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) {
- // 1) Current theme is already applied to the context
-
- // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme)
- if (theme == 0) {
- theme = getThemeResource(context, !alertDialog
- ? android.support.v7.appcompat.R.attr.dialogTheme
- : android.support.v7.appcompat.R.attr.alertDialogTheme);
- }
- // Apply it
- context = new ContextThemeWrapper(context, theme);
-
- // 3) If a custom Media Router theme is provided then apply the base theme
- if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) {
- context = new ContextThemeWrapper(context, getRouterThemeId(context));
- }
-
- return context;
+ static Context createThemedContext(Context context, int style) {
+ // First, apply dialog property overlay.
+ Context themedContext =
+ new ContextThemeWrapper(context, getStyledRouterThemeId(context, style));
+ int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+ return customizedThemeId == 0 ? themedContext
+ : new ContextThemeWrapper(themedContext, customizedThemeId);
}
- // This method should be used in conjunction with the previous method.
- static int createThemedDialogStyle(Context context) {
- // 4) Apply the custom Media Router theme
- int theme = getThemeResource(context, R.attr.mediaRouteTheme);
- if (theme == 0) {
- // 3) No custom MediaRouther theme was provided so apply the base theme instead
- theme = getRouterThemeId(context);
- }
- return theme;
+ /**
+ * Creates the theme resource ID intended to be used by dialogs.
+ */
+ static int createThemeForDialog(Context context, int style) {
+ int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+ return customizedThemeId != 0 ? customizedThemeId : getStyledRouterThemeId(context, style);
}
- // END. Previous two methods should be used in conjunction.
- static int getThemeResource(Context context, int attr) {
+ public static int getThemeResource(Context context, int attr) {
TypedValue value = new TypedValue();
return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
}
- static float getDisabledAlpha(Context context) {
+ public static float getDisabledAlpha(Context context) {
TypedValue value = new TypedValue();
return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true)
? value.getFloat() : 0.5f;
}
- static @ControllerColorType int getControllerColor(Context context, int style) {
+ public static @ControllerColorType int getControllerColor(Context context, int style) {
int primaryColor = getThemeColor(context, style,
android.support.v7.appcompat.R.attr.colorPrimary);
if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
@@ -121,7 +92,7 @@ final class MediaRouterThemeHelper {
return COLOR_DARK_ON_LIGHT_BACKGROUND;
}
- static int getButtonTextColor(Context context) {
+ public static int getButtonTextColor(Context context) {
int primaryColor = getThemeColor(context, 0,
android.support.v7.appcompat.R.attr.colorPrimary);
int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground);
@@ -133,7 +104,7 @@ final class MediaRouterThemeHelper {
return primaryColor;
}
- static void setMediaControlsBackgroundColor(
+ public static void setMediaControlsBackgroundColor(
Context context, View mainControls, View groupControls, boolean hasGroup) {
int primaryColor = getThemeColor(context, 0,
android.support.v7.appcompat.R.attr.colorPrimary);
@@ -153,7 +124,7 @@ final class MediaRouterThemeHelper {
groupControls.setTag(primaryDarkColor);
}
- static void setVolumeSliderColor(
+ public static void setVolumeSliderColor(
Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) {
int controllerColor = getControllerColor(context, 0);
if (Color.alpha(controllerColor) != 0xFF) {
@@ -165,10 +136,23 @@ final class MediaRouterThemeHelper {
volumeSlider.setColor(controllerColor);
}
+ // This is copied from {@link AlertDialog#resolveDialogTheme} to pre-evaluate theme in advance.
+ public static int getAlertDialogResolvedTheme(Context context, int themeResId) {
+ if (themeResId >= 0x01000000) { // start of real resource IDs.
+ return themeResId;
+ } else {
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(
+ android.support.v7.appcompat.R.attr.alertDialogTheme, outValue, true);
+ return outValue.resourceId;
+ }
+ }
+
private static boolean isLightTheme(Context context) {
TypedValue value = new TypedValue();
- return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme,
- value, true) && value.data != 0;
+ return context.getTheme().resolveAttribute(
+ android.support.v7.appcompat.R.attr.isLightTheme, value, true)
+ && value.data != 0;
}
private static int getThemeColor(Context context, int style, int attr) {
@@ -189,16 +173,16 @@ final class MediaRouterThemeHelper {
return value.data;
}
- private static int getRouterThemeId(Context context) {
+ private static int getStyledRouterThemeId(Context context, int style) {
int themeId;
if (isLightTheme(context)) {
- if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+ if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
themeId = R.style.Theme_MediaRouter_Light;
} else {
themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
}
} else {
- if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+ if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
themeId = R.style.Theme_MediaRouter_LightControlPanel;
} else {
themeId = R.style.Theme_MediaRouter;
diff --git a/android/support/v7/util/DiffUtil.java b/android/support/v7/util/DiffUtil.java
index ebc33f31..6302666f 100644
--- a/android/support/v7/util/DiffUtil.java
+++ b/android/support/v7/util/DiffUtil.java
@@ -16,7 +16,6 @@
package android.support.v7.util;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
@@ -349,72 +348,6 @@ public class DiffUtil {
}
/**
- * Callback for calculating the diff between two non-null items in a list.
- * <p>
- * {@link Callback} serves two roles - list indexing, and item diffing. ItemCallback handles
- * just the second of these, which allows separation of code that indexes into an array or List
- * from the presentation-layer and content specific diffing code.
- *
- * @param <T> Type of items to compare.
- */
- public abstract static class ItemCallback<T> {
- /**
- * Called to check whether two objects represent the same item.
- * <p>
- * For example, if your items have unique ids, this method should check their id equality.
- *
- * @param oldItem The item in the old list.
- * @param newItem The item in the new list.
- * @return True if the two items represent the same object or false if they are different.
- *
- * @see Callback#areItemsTheSame(int, int)
- */
- public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);
-
- /**
- * Called to check whether two items have the same data.
- * <p>
- * This information is used to detect if the contents of an item have changed.
- * <p>
- * This method to check equality instead of {@link Object#equals(Object)} so that you can
- * change its behavior depending on your UI.
- * <p>
- * For example, if you are using DiffUtil with a
- * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
- * return whether the items' visual representations are the same.
- * <p>
- * This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for
- * these items.
- *
- * @param oldItem The item in the old list.
- * @param newItem The item in the new list.
- * @return True if the contents of the items are the same or false if they are different.
- *
- * @see Callback#areContentsTheSame(int, int)
- */
- public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);
-
- /**
- * When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and
- * {@link #areContentsTheSame(T, T)} returns false for them, this method is called to
- * get a payload about the change.
- * <p>
- * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
- * particular field that changed in the item and your
- * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
- * information to run the correct animation.
- * <p>
- * Default implementation returns {@code null}.
- *
- * @see Callback#getChangePayload(int, int)
- */
- @SuppressWarnings({"WeakerAccess", "unused"})
- public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
- return null;
- }
- }
-
- /**
* Snakes represent a match between two lists. It is optionally prefixed or postfixed with an
* add or remove operation. See the Myers' paper for details.
*/
diff --git a/android/support/v7/widget/AppCompatTextHelper.java b/android/support/v7/widget/AppCompatTextHelper.java
index fa6196f5..51510aa2 100644
--- a/android/support/v7/widget/AppCompatTextHelper.java
+++ b/android/support/v7/widget/AppCompatTextHelper.java
@@ -29,7 +29,6 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.appcompat.R;
import android.text.method.PasswordTransformationMethod;
@@ -37,8 +36,6 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
-import java.lang.ref.WeakReference;
-
@RequiresApi(9)
class AppCompatTextHelper {
@@ -66,7 +63,6 @@ class AppCompatTextHelper {
private int mStyle = Typeface.NORMAL;
private Typeface mFontTypeface;
- private boolean mAsyncFontPending;
AppCompatTextHelper(TextView view) {
mView = view;
@@ -217,23 +213,8 @@ class AppCompatTextHelper {
? R.styleable.TextAppearance_android_fontFamily
: R.styleable.TextAppearance_fontFamily;
if (!context.isRestricted()) {
- final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
- ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() {
- @Override
- public void onFontRetrieved(@NonNull Typeface typeface) {
- onAsyncTypefaceReceived(textViewWeak, typeface);
- }
-
- @Override
- public void onFontRetrievalFailed(int reason) {
- // Do nothing.
- }
- };
try {
- // Note the callback will be triggered on the UI thread.
- mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback);
- // If this call gave us an immediate result, ignore any pending callbacks.
- mAsyncFontPending = mFontTypeface == null;
+ mFontTypeface = a.getFont(fontFamilyId, mStyle);
} catch (UnsupportedOperationException | Resources.NotFoundException e) {
// Expected if it is not a font resource.
}
@@ -241,16 +222,12 @@ class AppCompatTextHelper {
if (mFontTypeface == null) {
// Try with String. This is done by TextView JB+, but fails in ICS
String fontFamilyName = a.getString(fontFamilyId);
- if (fontFamilyName != null) {
- mFontTypeface = Typeface.create(fontFamilyName, mStyle);
- }
+ mFontTypeface = Typeface.create(fontFamilyName, mStyle);
}
return;
}
if (a.hasValue(R.styleable.TextAppearance_android_typeface)) {
- // Ignore previous pending fonts
- mAsyncFontPending = false;
int typefaceIndex = a.getInt(R.styleable.TextAppearance_android_typeface, SANS);
switch (typefaceIndex) {
case SANS:
@@ -268,16 +245,6 @@ class AppCompatTextHelper {
}
}
- private void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, Typeface typeface) {
- if (mAsyncFontPending) {
- mFontTypeface = typeface;
- final TextView textView = textViewWeak.get();
- if (textView != null) {
- textView.setTypeface(typeface, mStyle);
- }
- }
- }
-
void onSetTextAppearance(Context context, int resId) {
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
resId, R.styleable.TextAppearance);
diff --git a/android/support/v7/widget/TintTypedArray.java b/android/support/v7/widget/TintTypedArray.java
index 384c4615..22709551 100644
--- a/android/support/v7/widget/TintTypedArray.java
+++ b/android/support/v7/widget/TintTypedArray.java
@@ -106,8 +106,7 @@ public class TintTypedArray {
* not a font resource.
*/
@Nullable
- public Typeface getFont(@StyleableRes int index, int style,
- @Nullable ResourcesCompat.FontCallback fontCallback) {
+ public Typeface getFont(@StyleableRes int index, int style) {
final int resourceId = mWrapped.getResourceId(index, 0);
if (resourceId == 0) {
return null;
@@ -115,7 +114,7 @@ public class TintTypedArray {
if (mTypedValue == null) {
mTypedValue = new TypedValue();
}
- return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style, fontCallback);
+ return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style);
}
public int length() {
diff --git a/android/telephony/CarrierConfigManager.java b/android/telephony/CarrierConfigManager.java
index de980b2f..689ce954 100644
--- a/android/telephony/CarrierConfigManager.java
+++ b/android/telephony/CarrierConfigManager.java
@@ -763,18 +763,6 @@ public class CarrierConfigManager {
public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
/**
- * Some carriers will send call forwarding responses for voicemail in a format that is not 3gpp
- * compliant, which causes issues during parsing. This causes the
- * {@link com.android.internal.telephony.CallForwardInfo#number} to contain non-numerical
- * characters instead of a number.
- *
- * If true, we will detect the non-numerical characters and replace them with "Voicemail".
- * @hide
- */
- public static final String KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL =
- "call_forwarding_map_non_number_to_voicemail_bool";
-
- /**
* Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
*/
@@ -1585,25 +1573,6 @@ public class CarrierConfigManager {
public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
"show_ims_registration_status_bool";
- /**
- * The flag to disable the popup dialog which warns the user of data charges.
- * @hide
- */
- public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL =
- "disable_charge_indication_bool";
-
- /**
- * Boolean indicating whether to skip the call forwarding (CF) fail-to-disable dialog.
- * The logic used to determine whether we succeeded in disabling is carrier specific,
- * so the dialog may not always be accurate.
- * {@code false} - show CF fail-to-disable dialog.
- * {@code true} - skip showing CF fail-to-disable dialog.
- *
- * @hide
- */
- public static final String KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL =
- "skip_cf_fail_to_disable_dialog_bool";
-
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1734,7 +1703,6 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
- sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
@@ -1758,7 +1726,6 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
- sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -1873,7 +1840,6 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
- sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
}
/**
diff --git a/android/telephony/MbmsDownloadSession.java b/android/telephony/MbmsDownloadSession.java
index 9a9877a8..764b7b22 100644
--- a/android/telephony/MbmsDownloadSession.java
+++ b/android/telephony/MbmsDownloadSession.java
@@ -77,9 +77,8 @@ public class MbmsDownloadSession implements AutoCloseable {
* Integer extra that Android will attach to the intent supplied via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
* Indicates the result code of the download. One of
- * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED},
- * {@link #RESULT_IO_ERROR}, {@link #RESULT_DOWNLOAD_FAILURE}, {@link #RESULT_OUT_OF_STORAGE},
- * {@link #RESULT_SERVICE_ID_NOT_DEFINED}, or {@link #RESULT_FILE_ROOT_UNREACHABLE}.
+ * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED}, or
+ * {@link #RESULT_IO_ERROR}.
*
* This extra may also be used by the middleware when it is sending intents to the app.
*/
@@ -143,41 +142,11 @@ public class MbmsDownloadSession implements AutoCloseable {
/**
* Indicates that the download will not be completed due to an I/O error incurred while
- * writing to temp files.
- *
- * This is likely a transient error and another {@link DownloadRequest} should be sent to try
- * the download again.
+ * writing to temp files. This commonly indicates that the device is out of storage space,
+ * but may indicate other conditions as well (such as an SD card being removed).
*/
public static final int RESULT_IO_ERROR = 4;
-
- /**
- * Indicates that the Service ID specified in the {@link DownloadRequest} is incorrect due to
- * the Id being incorrect, stale, expired, or similar.
- */
- public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5;
-
- /**
- * Indicates that there was an error while processing downloaded files, such as a file repair or
- * file decoding error and is not due to a file I/O error.
- *
- * This is likely a transient error and another {@link DownloadRequest} should be sent to try
- * the download again.
- */
- public static final int RESULT_DOWNLOAD_FAILURE = 6;
-
- /**
- * Indicates that the file system is full and the {@link DownloadRequest} can not complete.
- * Either space must be made on the current file system or the temp file root location must be
- * changed to a location that is not full to download the temp files.
- */
- public static final int RESULT_OUT_OF_STORAGE = 7;
-
- /**
- * Indicates that the file root that was set is currently unreachable. This can happen if the
- * temp files are set to be stored on external storage and the SD card was removed, for example.
- * The temp file root should be changed before sending another DownloadRequest.
- */
- public static final int RESULT_FILE_ROOT_UNREACHABLE = 8;
+ // TODO - more results!
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/android/telephony/NetworkScanRequest.java b/android/telephony/NetworkScanRequest.java
index 9674c930..d2aef200 100644
--- a/android/telephony/NetworkScanRequest.java
+++ b/android/telephony/NetworkScanRequest.java
@@ -19,7 +19,6 @@ package android.telephony;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -39,20 +38,6 @@ public final class NetworkScanRequest implements Parcelable {
public static final int MAX_BANDS = 8;
/** @hide */
public static final int MAX_CHANNELS = 32;
- /** @hide */
- public static final int MAX_MCC_MNC_LIST_SIZE = 20;
- /** @hide */
- public static final int MIN_SEARCH_PERIODICITY_SEC = 5;
- /** @hide */
- public static final int MAX_SEARCH_PERIODICITY_SEC = 300;
- /** @hide */
- public static final int MIN_SEARCH_MAX_SEC = 60;
- /** @hide */
- public static final int MAX_SEARCH_MAX_SEC = 3600;
- /** @hide */
- public static final int MIN_INCREMENTAL_PERIODICITY_SEC = 1;
- /** @hide */
- public static final int MAX_INCREMENTAL_PERIODICITY_SEC = 10;
/** Performs the scan only once */
public static final int SCAN_TYPE_ONE_SHOT = 0;
@@ -61,84 +46,24 @@ public final class NetworkScanRequest implements Parcelable {
*
* The modem will start new scans periodically, and the interval between two scans is usually
* multiple minutes.
- */
+ * */
public static final int SCAN_TYPE_PERIODIC = 1;
/** Defines the type of the scan. */
public int scanType;
- /**
- * Search periodicity (in seconds).
- * Expected range for the input is [5s - 300s]
- * This value must be less than or equal to maxSearchTime
- */
- public int searchPeriodicity;
-
- /**
- * Maximum duration of the periodic search (in seconds).
- * Expected range for the input is [60s - 3600s]
- * If the search lasts this long, it will be terminated.
- */
- public int maxSearchTime;
-
- /**
- * Indicates whether the modem should report incremental
- * results of the network scan to the client.
- * FALSE – Incremental results are not reported.
- * TRUE (default) – Incremental results are reported
- */
- public boolean incrementalResults;
-
- /**
- * Indicates the periodicity with which the modem should
- * report incremental results to the client (in seconds).
- * Expected range for the input is [1s - 10s]
- * This value must be less than or equal to maxSearchTime
- */
- public int incrementalResultsPeriodicity;
-
/** Describes the radio access technologies with bands or channels that need to be scanned. */
public RadioAccessSpecifier[] specifiers;
/**
- * Describes the List of PLMN ids (MCC-MNC)
- * If any PLMN of this list is found, search should end at that point and
- * results with all PLMN found till that point should be sent as response.
- * If list not sent, search to be completed till end and all PLMNs found to be reported.
- * Max size of array is MAX_MCC_MNC_LIST_SIZE
- */
- public ArrayList<String> mccMncs;
-
- /**
* Creates a new NetworkScanRequest with scanType and network specifiers
*
* @param scanType The type of the scan
* @param specifiers the radio network with bands / channels to be scanned
- * @param searchPeriodicity Search periodicity (in seconds)
- * @param maxSearchTime Maximum duration of the periodic search (in seconds)
- * @param incrementalResults Indicates whether the modem should report incremental
- * results of the network scan to the client
- * @param incrementalResultsPeriodicity Indicates the periodicity with which the modem should
- * report incremental results to the client (in seconds)
- * @param mccMncs Describes the List of PLMN ids (MCC-MNC)
*/
- public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers,
- int searchPeriodicity,
- int maxSearchTime,
- boolean incrementalResults,
- int incrementalResultsPeriodicity,
- ArrayList<String> mccMncs) {
+ public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers) {
this.scanType = scanType;
this.specifiers = specifiers;
- this.searchPeriodicity = searchPeriodicity;
- this.maxSearchTime = maxSearchTime;
- this.incrementalResults = incrementalResults;
- this.incrementalResultsPeriodicity = incrementalResultsPeriodicity;
- if (mccMncs != null) {
- this.mccMncs = mccMncs;
- } else {
- this.mccMncs = new ArrayList<>();
- }
}
@Override
@@ -150,11 +75,6 @@ public final class NetworkScanRequest implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(scanType);
dest.writeParcelableArray(specifiers, flags);
- dest.writeInt(searchPeriodicity);
- dest.writeInt(maxSearchTime);
- dest.writeBoolean(incrementalResults);
- dest.writeInt(incrementalResultsPeriodicity);
- dest.writeStringList(mccMncs);
}
private NetworkScanRequest(Parcel in) {
@@ -162,12 +82,6 @@ public final class NetworkScanRequest implements Parcelable {
specifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
Object.class.getClassLoader(),
RadioAccessSpecifier.class);
- searchPeriodicity = in.readInt();
- maxSearchTime = in.readInt();
- incrementalResults = in.readBoolean();
- incrementalResultsPeriodicity = in.readInt();
- mccMncs = new ArrayList<>();
- in.readStringList(mccMncs);
}
@Override
@@ -185,24 +99,13 @@ public final class NetworkScanRequest implements Parcelable {
}
return (scanType == nsr.scanType
- && Arrays.equals(specifiers, nsr.specifiers)
- && searchPeriodicity == nsr.searchPeriodicity
- && maxSearchTime == nsr.maxSearchTime
- && incrementalResults == nsr.incrementalResults
- && incrementalResultsPeriodicity == nsr.incrementalResultsPeriodicity
- && (((mccMncs != null)
- && mccMncs.equals(nsr.mccMncs))));
+ && Arrays.equals(specifiers, nsr.specifiers));
}
@Override
public int hashCode () {
return ((scanType * 31)
- + (Arrays.hashCode(specifiers)) * 37
- + (searchPeriodicity * 41)
- + (maxSearchTime * 43)
- + ((incrementalResults == true? 1 : 0) * 47)
- + (incrementalResultsPeriodicity * 53)
- + (mccMncs.hashCode() * 59));
+ + (Arrays.hashCode(specifiers)) * 37);
}
public static final Creator<NetworkScanRequest> CREATOR =
diff --git a/android/telephony/ServiceState.java b/android/telephony/ServiceState.java
index 116e711e..e448fb2a 100644
--- a/android/telephony/ServiceState.java
+++ b/android/telephony/ServiceState.java
@@ -1197,6 +1197,15 @@ public class ServiceState implements Parcelable {
}
}
+ /**
+ * @Deprecated to be removed Q3 2013 use {@link #getVoiceNetworkType}
+ * @hide
+ */
+ public int getNetworkType() {
+ Rlog.e(LOG_TAG, "ServiceState.getNetworkType() DEPRECATED will be removed *******");
+ return rilRadioTechnologyToNetworkType(mRilVoiceRadioTechnology);
+ }
+
/** @hide */
public int getDataNetworkType() {
return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
diff --git a/android/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
index f0d60b68..5a57f322 100644
--- a/android/telephony/mbms/DownloadRequest.java
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -16,7 +16,6 @@
package android.telephony.mbms;
-import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Intent;
import android.net.Uri;
@@ -27,6 +26,7 @@ import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -71,19 +71,6 @@ public final class DownloadRequest implements Parcelable {
private String appIntent;
private int version = CURRENT_VERSION;
-
- /**
- * Builds a new DownloadRequest.
- * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
- * never be null.
- */
- public Builder(@NonNull Uri sourceUri) {
- if (sourceUri == null) {
- throw new IllegalArgumentException("Source URI must be non-null.");
- }
- source = sourceUri;
- }
-
/**
* Sets the service from which the download request to be built will download from.
* @param serviceInfo
@@ -105,6 +92,15 @@ public final class DownloadRequest implements Parcelable {
}
/**
+ * Sets the source URI for the download request to be built.
+ * @param source
+ */
+ public Builder setSource(Uri source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
* Set the subscription ID on which the file(s) should be downloaded.
* @param subscriptionId
*/
@@ -320,11 +316,9 @@ public final class DownloadRequest implements Parcelable {
throw new RuntimeException("Could not get sha256 hash object");
}
if (version >= 1) {
- // Hash the source URI and the app intent
+ // Hash the source URI, destination URI, and the app intent
digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
- if (serializedResultIntentForApp != null) {
- digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
- }
+ digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
}
// Add updates for future versions here
return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
diff --git a/android/telephony/mbms/MbmsDownloadReceiver.java b/android/telephony/mbms/MbmsDownloadReceiver.java
index 9af1eb9e..fe275372 100644
--- a/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -287,7 +287,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
return;
}
- List<Uri> tempFiles = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_LIST);
+ List<Uri> tempFiles = intent.getParcelableExtra(VendorUtils.EXTRA_TEMP_LIST);
if (tempFiles == null) {
return;
}
@@ -309,7 +309,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
return;
}
int fdCount = intent.getIntExtra(VendorUtils.EXTRA_FD_COUNT, 0);
- List<Uri> pausedList = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_PAUSED_LIST);
+ List<Uri> pausedList = intent.getParcelableExtra(VendorUtils.EXTRA_PAUSED_LIST);
if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -492,14 +492,9 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Package manager couldn't find " + context.getPackageName());
}
- if (appInfo.metaData == null) {
- throw new RuntimeException("App must declare the file provider authority as metadata " +
- "in the manifest.");
- }
String authority = appInfo.metaData.getString(MBMS_FILE_PROVIDER_META_DATA_KEY);
if (authority == null) {
- throw new RuntimeException("App must declare the file provider authority as metadata " +
- "in the manifest.");
+ throw new RuntimeException("Must declare the file provider authority as meta data");
}
return authority;
}
diff --git a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index c3b2c482..2f85a1df 100644
--- a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -113,10 +113,6 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override
public final int initialize(final int subscriptionId,
final IMbmsDownloadSessionCallback callback) throws RemoteException {
- if (callback == null) {
- throw new NullPointerException("Callback must not be null");
- }
-
final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() {
@Override
@@ -244,13 +240,6 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
public final int registerStateCallback(final DownloadRequest downloadRequest,
final IDownloadStateCallback callback, int flags) throws RemoteException {
final int uid = Binder.getCallingUid();
- if (downloadRequest == null) {
- throw new NullPointerException("Download request must not be null");
- }
- if (callback == null) {
- throw new NullPointerException("Callback must not be null");
- }
-
DeathRecipient deathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
@@ -303,13 +292,6 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
public final int unregisterStateCallback(
final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
throws RemoteException {
- if (downloadRequest == null) {
- throw new NullPointerException("Download request must not be null");
- }
- if (callback == null) {
- throw new NullPointerException("Callback must not be null");
- }
-
DeathRecipient deathRecipient =
mDownloadCallbackDeathRecipients.remove(callback.asBinder());
if (deathRecipient == null) {
diff --git a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 65b726df..f8f370a5 100644
--- a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -65,10 +65,6 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override
public final int initialize(final IMbmsStreamingSessionCallback callback,
final int subscriptionId) throws RemoteException {
- if (callback == null) {
- throw new NullPointerException("Callback must not be null");
- }
-
final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() {
@Override
@@ -156,10 +152,6 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override
public int startStreaming(final int subscriptionId, String serviceId,
final IStreamingServiceCallback callback) throws RemoteException {
- if (callback == null) {
- throw new NullPointerException("Callback must not be null");
- }
-
final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() {
@Override
diff --git a/android/text/BoringLayoutCreateDrawPerfTest.java b/android/text/BoringLayoutCreateDrawPerfTest.java
index 586c3852..47dd257b 100644
--- a/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -46,7 +46,7 @@ public class BoringLayoutCreateDrawPerfTest {
private static final float SPACING_ADD = 10f;
private static final float SPACING_MULT = 1.5f;
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/android/text/BoringLayoutIsBoringPerfTest.java b/android/text/BoringLayoutIsBoringPerfTest.java
index 9d11f295..34de65de 100644
--- a/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/android/text/BoringLayoutIsBoringPerfTest.java
@@ -40,7 +40,7 @@ public class BoringLayoutIsBoringPerfTest {
private static final boolean[] BOOLEANS = new boolean[]{false, true};
- @Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached={4},{1} chars,{0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/android/text/DynamicLayout.java b/android/text/DynamicLayout.java
index fba358cf..24260c4f 100644
--- a/android/text/DynamicLayout.java
+++ b/android/text/DynamicLayout.java
@@ -299,7 +299,7 @@ public class DynamicLayout extends Layout
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
- private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
+ private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
}
/**
@@ -440,7 +440,7 @@ public class DynamicLayout extends Layout
mEllipsizeAt = null;
}
- mObjects = new PackedObjectVector<>(1);
+ mObjects = new PackedObjectVector<Directions>(1);
// Initial state is a single line with 0 characters (0 to 0), with top at 0 and bottom at
// whatever is natural, and undefined ellipsis.
@@ -1050,7 +1050,7 @@ public class DynamicLayout extends Layout
private static class ChangeWatcher implements TextWatcher, SpanWatcher {
public ChangeWatcher(DynamicLayout layout) {
- mLayout = new WeakReference<>(layout);
+ mLayout = new WeakReference<DynamicLayout>(layout);
}
private void reflow(CharSequence s, int where, int before, int after) {
diff --git a/android/text/Hyphenator.java b/android/text/Hyphenator.java
index 4f1488e1..ad26f23a 100644
--- a/android/text/Hyphenator.java
+++ b/android/text/Hyphenator.java
@@ -16,15 +16,258 @@
package android.text;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Locale;
+
/**
- * Hyphenator just initializes the native implementation of automatic hyphenation,
+ * Hyphenator is a wrapper class for a native implementation of automatic hyphenation,
* in essence finding valid hyphenation opportunities in a word.
*
* @hide
*/
public class Hyphenator {
- public static void init() {
- nInit();
+ private static String TAG = "Hyphenator";
+
+ private final static Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+
+ private final long mNativePtr;
+ private final HyphenationData mData;
+
+ private Hyphenator(long nativePtr, HyphenationData data) {
+ mNativePtr = nativePtr;
+ mData = data;
+ }
+
+ public long getNativePtr() {
+ return mNativePtr;
}
- private static native void nInit();
+
+ public static Hyphenator get(@Nullable Locale locale) {
+ synchronized (sLock) {
+ Hyphenator result = sMap.get(locale);
+ if (result != null) {
+ return result;
+ }
+
+ // If there's a variant, fall back to language+variant only, if available
+ final String variant = locale.getVariant();
+ if (!variant.isEmpty()) {
+ final Locale languageAndVariantOnlyLocale =
+ new Locale(locale.getLanguage(), "", variant);
+ result = sMap.get(languageAndVariantOnlyLocale);
+ if (result != null) {
+ return putAlias(locale, result);
+ }
+ }
+
+ // Fall back to language-only, if available
+ final Locale languageOnlyLocale = new Locale(locale.getLanguage());
+ result = sMap.get(languageOnlyLocale);
+ if (result != null) {
+ return putAlias(locale, result);
+ }
+
+ // Fall back to script-only, if available
+ final String script = locale.getScript();
+ if (!script.equals("")) {
+ final Locale scriptOnlyLocale = new Locale.Builder()
+ .setLanguage("und")
+ .setScript(script)
+ .build();
+ result = sMap.get(scriptOnlyLocale);
+ if (result != null) {
+ return putAlias(locale, result);
+ }
+ }
+
+ return putEmptyAlias(locale);
+ }
+ }
+
+ private static class HyphenationData {
+ private static final String SYSTEM_HYPHENATOR_LOCATION = "/system/usr/hyphen-data";
+
+ public final int mMinPrefix, mMinSuffix;
+ public final long mDataAddress;
+
+ // Reasonable enough values for cases where we have no hyphenation patterns but may be able
+ // to do some automatic hyphenation based on characters. These values would be used very
+ // rarely.
+ private static final int DEFAULT_MIN_PREFIX = 2;
+ private static final int DEFAULT_MIN_SUFFIX = 2;
+
+ public static final HyphenationData sEmptyData =
+ new HyphenationData(DEFAULT_MIN_PREFIX, DEFAULT_MIN_SUFFIX);
+
+ // Create empty HyphenationData.
+ private HyphenationData(int minPrefix, int minSuffix) {
+ mMinPrefix = minPrefix;
+ mMinSuffix = minSuffix;
+ mDataAddress = 0;
+ }
+
+ HyphenationData(String languageTag, int minPrefix, int minSuffix) {
+ mMinPrefix = minPrefix;
+ mMinSuffix = minSuffix;
+
+ final String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
+ final File patternFile = new File(SYSTEM_HYPHENATOR_LOCATION, patternFilename);
+ if (!patternFile.canRead()) {
+ Log.e(TAG, "hyphenation patterns for " + patternFile + " not found or unreadable");
+ mDataAddress = 0;
+ } else {
+ long address;
+ try (RandomAccessFile f = new RandomAccessFile(patternFile, "r")) {
+ address = Os.mmap(0, f.length(), OsConstants.PROT_READ,
+ OsConstants.MAP_SHARED, f.getFD(), 0 /* offset */);
+ } catch (IOException | ErrnoException e) {
+ Log.e(TAG, "error loading hyphenation " + patternFile, e);
+ address = 0;
+ }
+ mDataAddress = address;
+ }
+ }
+ }
+
+ // Do not call this method outside of init method.
+ private static Hyphenator putNewHyphenator(Locale loc, HyphenationData data) {
+ final Hyphenator hyphenator = new Hyphenator(nBuildHyphenator(
+ data.mDataAddress, loc.getLanguage(), data.mMinPrefix, data.mMinSuffix), data);
+ sMap.put(loc, hyphenator);
+ return hyphenator;
+ }
+
+ // Do not call this method outside of init method.
+ private static void loadData(String langTag, int minPrefix, int maxPrefix) {
+ final HyphenationData data = new HyphenationData(langTag, minPrefix, maxPrefix);
+ putNewHyphenator(Locale.forLanguageTag(langTag), data);
+ }
+
+ // Caller must acquire sLock before calling this method.
+ // The Hyphenator for the baseLangTag must exists.
+ private static Hyphenator addAliasByTag(String langTag, String baseLangTag) {
+ return putAlias(Locale.forLanguageTag(langTag),
+ sMap.get(Locale.forLanguageTag(baseLangTag)));
+ }
+
+ // Caller must acquire sLock before calling this method.
+ private static Hyphenator putAlias(Locale locale, Hyphenator base) {
+ return putNewHyphenator(locale, base.mData);
+ }
+
+ // Caller must acquire sLock before calling this method.
+ private static Hyphenator putEmptyAlias(Locale locale) {
+ return putNewHyphenator(locale, HyphenationData.sEmptyData);
+ }
+
+ // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but
+ // that appears too small.
+ private static final int INDIC_MIN_PREFIX = 2;
+ private static final int INDIC_MIN_SUFFIX = 2;
+
+ /**
+ * Load hyphenation patterns at initialization time. We want to have patterns
+ * for all locales loaded and ready to use so we don't have to do any file IO
+ * on the UI thread when drawing text in different locales.
+ *
+ * @hide
+ */
+ public static void init() {
+ synchronized (sLock) {
+ sMap.put(null, null);
+
+ loadData("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese
+ loadData("bg", 2, 2); // Bulgarian
+ loadData("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali
+ loadData("cu", 1, 2); // Church Slavonic
+ loadData("cy", 2, 3); // Welsh
+ loadData("da", 2, 2); // Danish
+ loadData("de-1901", 2, 2); // German 1901 orthography
+ loadData("de-1996", 2, 2); // German 1996 orthography
+ loadData("de-CH-1901", 2, 2); // Swiss High German 1901 orthography
+ loadData("en-GB", 2, 3); // British English
+ loadData("en-US", 2, 3); // American English
+ loadData("es", 2, 2); // Spanish
+ loadData("et", 2, 3); // Estonian
+ loadData("eu", 2, 2); // Basque
+ loadData("fr", 2, 3); // French
+ loadData("ga", 2, 3); // Irish
+ loadData("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Gujarati
+ loadData("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Hindi
+ loadData("hr", 2, 2); // Croatian
+ loadData("hu", 2, 2); // Hungarian
+ // texhyphen sources say Armenian may be (1, 2); but that it needs confirmation.
+ // Going with a more conservative value of (2, 2) for now.
+ loadData("hy", 2, 2); // Armenian
+ loadData("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada
+ loadData("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam
+ loadData("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script
+ loadData("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi
+ loadData("nb", 2, 2); // Norwegian Bokmål
+ loadData("nn", 2, 2); // Norwegian Nynorsk
+ loadData("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
+ loadData("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
+ loadData("pt", 2, 3); // Portuguese
+ loadData("sl", 2, 2); // Slovenian
+ loadData("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil
+ loadData("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu
+ loadData("tk", 2, 2); // Turkmen
+ loadData("und-Ethi", 1, 1); // Any language in Ethiopic script
+
+ // English locales that fall back to en-US. The data is
+ // from CLDR. It's all English locales, minus the locales whose
+ // parent is en-001 (from supplementalData.xml, under <parentLocales>).
+ // TODO: Figure out how to get this from ICU.
+ addAliasByTag("en-AS", "en-US"); // English (American Samoa)
+ addAliasByTag("en-GU", "en-US"); // English (Guam)
+ addAliasByTag("en-MH", "en-US"); // English (Marshall Islands)
+ addAliasByTag("en-MP", "en-US"); // English (Northern Mariana Islands)
+ addAliasByTag("en-PR", "en-US"); // English (Puerto Rico)
+ addAliasByTag("en-UM", "en-US"); // English (United States Minor Outlying Islands)
+ addAliasByTag("en-VI", "en-US"); // English (Virgin Islands)
+
+ // All English locales other than those falling back to en-US are mapped to en-GB.
+ addAliasByTag("en", "en-GB");
+
+ // For German, we're assuming the 1996 (and later) orthography by default.
+ addAliasByTag("de", "de-1996");
+ // Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
+ addAliasByTag("de-LI-1901", "de-CH-1901");
+
+ // Norwegian is very probably Norwegian Bokmål.
+ addAliasByTag("no", "nb");
+
+ // Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
+ addAliasByTag("mn", "mn-Cyrl"); // Mongolian
+
+ // Fall back to Ethiopic script for languages likely to be written in Ethiopic.
+ // Data is from CLDR's likelySubtags.xml.
+ // TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
+ addAliasByTag("am", "und-Ethi"); // Amharic
+ addAliasByTag("byn", "und-Ethi"); // Blin
+ addAliasByTag("gez", "und-Ethi"); // Geʻez
+ addAliasByTag("ti", "und-Ethi"); // Tigrinya
+ addAliasByTag("wal", "und-Ethi"); // Wolaytta
+ }
+ };
+
+ private static native long nBuildHyphenator(/* non-zero */ long dataAddress,
+ @NonNull String langTag, @IntRange(from = 1) int minPrefix,
+ @IntRange(from = 1) int minSuffix);
}
diff --git a/android/text/Layout.java b/android/text/Layout.java
index ac5c2e92..60fff738 100644
--- a/android/text/Layout.java
+++ b/android/text/Layout.java
@@ -319,6 +319,8 @@ public abstract class Layout {
private float getJustifyWidth(int lineNum) {
Alignment paraAlign = mAlignment;
+ TabStops tabStops = null;
+ boolean tabStopsIsInitialized = false;
int left = 0;
int right = mWidth;
@@ -369,6 +371,10 @@ public abstract class Layout {
}
}
+ if (getLineContainsTab(lineNum)) {
+ tabStops = new TabStops(TAB_INCREMENT, spans);
+ }
+
final Alignment align;
if (paraAlign == Alignment.ALIGN_LEFT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
@@ -1417,6 +1423,7 @@ public abstract class Layout {
float dist = Math.abs(getHorizontal(max, primary) - horiz);
if (dist <= bestdist) {
+ bestdist = dist;
best = max;
}
@@ -1563,7 +1570,7 @@ public abstract class Layout {
// XXX: we don't care about tabs
tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
- TextLine.recycle(tl);
+ tl = TextLine.recycle(tl);
return caret;
}
@@ -1887,7 +1894,10 @@ public abstract class Layout {
int margin = 0;
- boolean useFirstLineMargin = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
+ boolean isFirstParaLine = lineStart == 0 ||
+ spanned.charAt(lineStart - 1) == '\n';
+
+ boolean useFirstLineMargin = isFirstParaLine;
for (int i = 0; i < spans.length; i++) {
if (spans[i] instanceof LeadingMarginSpan2) {
int spStart = spanned.getSpanStart(spans[i]);
diff --git a/android/text/PaintMeasureDrawPerfTest.java b/android/text/PaintMeasureDrawPerfTest.java
index 67687985..00b60add 100644
--- a/android/text/PaintMeasureDrawPerfTest.java
+++ b/android/text/PaintMeasureDrawPerfTest.java
@@ -42,7 +42,7 @@ public class PaintMeasureDrawPerfTest {
private static final boolean[] BOOLEANS = new boolean[]{false, true};
- @Parameterized.Parameters(name = "cached={1},{0}chars")
+ @Parameterized.Parameters(name = "cached={1},{0} chars")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/android/text/StaticLayout.java b/android/text/StaticLayout.java
index 5c60188d..961cd8ee 100644
--- a/android/text/StaticLayout.java
+++ b/android/text/StaticLayout.java
@@ -21,18 +21,21 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
+import android.os.LocaleList;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
import android.util.Log;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import java.util.Arrays;
+import java.util.Locale;
/**
* StaticLayout is a Layout for text that will not be edited after it
@@ -98,6 +101,7 @@ public class StaticLayout extends Layout {
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
+ b.mLocales = null;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -114,6 +118,7 @@ public class StaticLayout extends Layout {
b.mMeasuredText = null;
b.mLeftIndents = null;
b.mRightIndents = null;
+ b.mLocales = null;
b.mLeftPaddings = null;
b.mRightPaddings = null;
nFinishBuilder(b.mNativePtr);
@@ -404,6 +409,17 @@ public class StaticLayout extends Layout {
return this;
}
+ @NonNull
+ private long[] getHyphenators(@NonNull LocaleList locales) {
+ final int length = locales.size();
+ final long[] result = new long[length];
+ for (int i = 0; i < length; i++) {
+ final Locale locale = locales.get(i);
+ result[i] = Hyphenator.get(locale).getNativePtr();
+ }
+ return result;
+ }
+
/**
* Measurement and break iteration is done in native code. The protocol for using
* the native code is as follows.
@@ -417,17 +433,35 @@ public class StaticLayout extends Layout {
* + addStyleRun (a text run, to be measured in native code)
* + addReplacementRun (a replacement run, width is given)
*
+ * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
* Run nComputeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
*/
+ private Pair<String, long[]> getLocaleAndHyphenatorIfChanged(TextPaint paint) {
+ final LocaleList locales = paint.getTextLocales();
+ final String languageTags;
+ long[] hyphenators;
+ if (!locales.equals(mLocales)) {
+ mLocales = locales;
+ return new Pair(locales.toLanguageTags(), getHyphenators(locales));
+ } else {
+ // passing null means keep current locale.
+ // TODO: move locale change detection to native.
+ return new Pair(null, null);
+ }
+ }
+
/* package */ void addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
- nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+ Pair<String, long[]> locHyph = getLocaleAndHyphenatorIfChanged(paint);
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl, locHyph.first,
+ locHyph.second);
}
/* package */ void addReplacementRun(TextPaint paint, int start, int end, float width) {
- nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
+ Pair<String, long[]> locHyph = getLocaleAndHyphenatorIfChanged(paint);
+ nAddReplacementRun(mNativePtr, start, end, width, locHyph.first, locHyph.second);
}
/**
@@ -485,7 +519,9 @@ public class StaticLayout extends Layout {
// This will go away and be subsumed by native builder code
private MeasuredText mMeasuredText;
- private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
+ private LocaleList mLocales;
+
+ private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
}
public StaticLayout(CharSequence source, TextPaint paint,
@@ -774,6 +810,9 @@ public class StaticLayout extends Layout {
}
}
+ // TODO: Move locale tracking code to native.
+ b.mLocales = null; // Reset the locale tracking.
+
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
firstWidth, firstWidthLineCount, restWidth,
variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
@@ -827,9 +866,10 @@ public class StaticLayout extends Layout {
spanEndCacheCount++;
}
+ nGetWidths(b.mNativePtr, widths);
int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags,
- lineBreaks.breaks.length, widths);
+ lineBreaks.breaks.length);
final int[] breaks = lineBreaks.breaks;
final float[] lineWidths = lineBreaks.widths;
@@ -907,10 +947,10 @@ public class StaticLayout extends Layout {
boolean moreChars = (endPos < bufEnd);
final int ascent = fallbackLineSpacing
- ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
+ ? Math.min(fmAscent, (int) Math.round(ascents[breakIndex]))
: fmAscent;
final int descent = fallbackLineSpacing
- ? Math.max(fmDescent, Math.round(descents[breakIndex]))
+ ? Math.max(fmDescent, (int) Math.round(descents[breakIndex]))
: fmDescent;
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
@@ -1137,7 +1177,7 @@ public class StaticLayout extends Layout {
mWorkPaint.set(paint);
do {
final float ellipsizedWidth = guessEllipsis(text, lineStart, lineEnd, widths,
- widthStart, tempAvail, where, line, mWorkPaint, forceEllipsis, dir);
+ widthStart, tempAvail, where, line, textWidth, mWorkPaint, forceEllipsis, dir);
if (ellipsizedWidth <= avail) {
lineFits = true;
} else {
@@ -1167,7 +1207,7 @@ public class StaticLayout extends Layout {
// This method temporarily modifies the TextPaint passed to it, so the TextPaint passed to it
// should not be accessed while the method is running.
private float guessEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths,
- int widthStart, float avail, TextUtils.TruncateAt where, int line,
+ int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth,
TextPaint paint, boolean forceEllipsis, int dir) {
final int savedHyphenEdit = paint.getHyphenEdit();
paint.setHyphenEdit(0);
@@ -1501,28 +1541,26 @@ public class StaticLayout extends Layout {
@Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings,
@IntRange(from = 0) int indentsOffset);
- // TODO: Make this method CriticalNative once native code defers doing layouts.
private static native void nAddStyleRun(
/* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
- @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl);
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl,
+ @Nullable String languageTags, @Nullable long[] hyphenators);
- // TODO: Make this method CriticalNative once native code defers doing layouts.
- private static native void nAddReplacementRun(
- /* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
+ private static native void nAddReplacementRun(/* non-zero */ long nativePtr,
@IntRange(from = 0) int start, @IntRange(from = 0) int end,
- @FloatRange(from = 0.0f) float width);
+ @FloatRange(from = 0.0f) float width, @Nullable String languageTags,
+ @Nullable long[] hyphenators);
+
+ private static native void nGetWidths(long nativePtr, float[] widths);
// populates LineBreaks and returns the number of breaks found
//
// the arrays inside the LineBreaks objects are passed in as well
// to reduce the number of JNI calls in the common case where the
// arrays do not have to be resized
- // The individual character widths will be returned in charWidths. The length of charWidths must
- // be at least the length of the text.
private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents,
- float[] recycleDescents, int[] recycleFlags, int recycleLength,
- float[] charWidths);
+ float[] recycleDescents, int[] recycleFlags, int recycleLength);
private int mLineCount;
private int mTopPadding, mBottomPadding;
diff --git a/android/text/StaticLayoutCreateDrawPerfTest.java b/android/text/StaticLayoutCreateDrawPerfTest.java
index bfdb7589..356e2e0d 100644
--- a/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -50,7 +50,7 @@ public class StaticLayoutCreateDrawPerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/android/text/StaticLayout_Delegate.java b/android/text/StaticLayout_Delegate.java
index def3c91c..63337f08 100644
--- a/android/text/StaticLayout_Delegate.java
+++ b/android/text/StaticLayout_Delegate.java
@@ -13,6 +13,7 @@ import android.icu.util.ULocale;
import android.text.Primitive.PrimitiveType;
import android.text.StaticLayout.LineBreaks;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -71,11 +72,13 @@ public class StaticLayout_Delegate {
@LayoutlibDelegate
/*package*/ static void nAddStyleRun(long nativeBuilder, long nativePaint, int start,
- int end, boolean isRtl) {
+ int end, boolean isRtl, String languageTags, long[] hyphenators) {
Builder builder = sBuilderManager.getDelegate(nativeBuilder);
if (builder == null) {
return;
}
+ builder.mLocales = languageTags;
+ builder.mNativeHyphenators = hyphenators;
int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
measureText(nativePaint, builder.mText, start, end - start, builder.mWidths,
@@ -83,20 +86,30 @@ public class StaticLayout_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void nAddReplacementRun(long nativeBuilder, long nativePaint, int start,
- int end, float width) {
+ /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width,
+ String languageTags, long[] hyphenators) {
Builder builder = sBuilderManager.getDelegate(nativeBuilder);
if (builder == null) {
return;
}
+ builder.mLocales = languageTags;
+ builder.mNativeHyphenators = hyphenators;
builder.mWidths[start] = width;
Arrays.fill(builder.mWidths, start + 1, end, 0.0f);
}
@LayoutlibDelegate
+ /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) {
+ Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+ if (builder != null) {
+ System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length);
+ }
+ }
+
+ @LayoutlibDelegate
/*package*/ static int nComputeLineBreaks(long nativeBuilder, LineBreaks recycle,
int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents,
- float[] recycleDescents, int[] recycleFlags, int recycleLength, float[] charWidths) {
+ float[] recycleDescents, int[] recycleFlags, int recycleLength) {
Builder builder = sBuilderManager.getDelegate(nativeBuilder);
if (builder == null) {
@@ -105,7 +118,7 @@ public class StaticLayout_Delegate {
// compute all possible breakpoints.
int length = builder.mWidths.length;
- BreakIterator it = BreakIterator.getLineInstance();
+ BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocales));
it.setText(new Segment(builder.mText, 0, length));
// average word length in english is 5. So, initialize the possible breaks with a guess.
@@ -136,7 +149,6 @@ public class StaticLayout_Delegate {
builder.mTabStopCalculator);
}
builder.mLineBreaker.computeBreaks(recycle);
- System.arraycopy(builder.mWidths, 0, charWidths, 0, builder.mWidths.length);
return recycle.breaks.length;
}
@@ -194,9 +206,11 @@ public class StaticLayout_Delegate {
* Java representation of the native Builder class.
*/
private static class Builder {
+ String mLocales;
char[] mText;
float[] mWidths;
LineBreaker mLineBreaker;
+ long[] mNativeHyphenators;
int mBreakStrategy;
LineWidth mLineWidth;
TabStops mTabStopCalculator;
diff --git a/android/text/TextLine.java b/android/text/TextLine.java
index 20c0ed87..2dbff100 100644
--- a/android/text/TextLine.java
+++ b/android/text/TextLine.java
@@ -73,7 +73,7 @@ class TextLine {
new SpanSet<ReplacementSpan>(ReplacementSpan.class);
private final DecorationInfo mDecorationInfo = new DecorationInfo();
- private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>();
+ private final ArrayList<DecorationInfo> mDecorations = new ArrayList();
private static final TextLine[] sCached = new TextLine[3];
@@ -340,14 +340,14 @@ class TextLine {
boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
if (inSegment && advance) {
- return h + measureRun(segstart, offset, j, runIsRtl, fmi);
+ return h += measureRun(segstart, offset, j, runIsRtl, fmi);
}
float w = measureRun(segstart, j, j, runIsRtl, fmi);
h += advance ? w : -w;
if (inSegment) {
- return h + measureRun(segstart, offset, j, runIsRtl, null);
+ return h += measureRun(segstart, offset, j, runIsRtl, null);
}
if (codept == '\t') {
@@ -828,14 +828,14 @@ class TextLine {
}
if (info.isUnderlineText) {
final float thickness =
- Math.max(wp.getUnderlineThickness(), 1.0f);
+ Math.max(((Paint) wp).getUnderlineThickness(), 1.0f);
drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness,
decorationXLeft, decorationXRight, y);
}
if (info.isStrikeThruText) {
final float thickness =
- Math.max(wp.getStrikeThruThickness(), 1.0f);
+ Math.max(((Paint) wp).getStrikeThruThickness(), 1.0f);
drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness,
decorationXLeft, decorationXRight, y);
}
diff --git a/android/text/TextViewSetTextMeasurePerfTest.java b/android/text/TextViewSetTextMeasurePerfTest.java
index ff2d57ed..a2bf33e1 100644
--- a/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/android/text/TextViewSetTextMeasurePerfTest.java
@@ -40,7 +40,7 @@ import java.util.Locale;
import java.util.Random;
/**
- * Performance test for {@link TextView} measure/draw.
+ * Performance test for multi line, single style {@link StaticLayout} creation/draw.
*/
@LargeTest
@RunWith(Parameterized.class)
@@ -51,7 +51,7 @@ public class TextViewSetTextMeasurePerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/android/util/Log.java b/android/util/Log.java
index b94e48b3..02998653 100644
--- a/android/util/Log.java
+++ b/android/util/Log.java
@@ -16,12 +16,45 @@
package android.util;
+import android.os.DeadSystemException;
+
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.LineBreakBufferedWriter;
+
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.io.Writer;
import java.net.UnknownHostException;
/**
- * Mock Log implementation for testing on non android host.
+ * API for sending log output.
+ *
+ * <p>Generally, you should use the {@link #v Log.v()}, {@link #d Log.d()},
+ * {@link #i Log.i()}, {@link #w Log.w()}, and {@link #e Log.e()} methods to write logs.
+ * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
+ *
+ * <p>The order in terms of verbosity, from least to most is
+ * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
+ * into an application except during development. Debug logs are compiled
+ * in but stripped at runtime. Error, warning and info logs are always kept.
+ *
+ * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
+ * in your class:
+ *
+ * <pre>private static final String TAG = "MyActivity";</pre>
+ *
+ * and use that in subsequent calls to the log methods.
+ * </p>
+ *
+ * <p><b>Tip:</b> Don't forget that when you make a call like
+ * <pre>Log.v(TAG, "index=" + i);</pre>
+ * that when you're building the string to pass into Log.d, the compiler uses a
+ * StringBuilder and at least three allocations occur: the StringBuilder
+ * itself, the buffer, and the String object. Realistically, there is also
+ * another buffer allocation and copy, and even more pressure on the gc.
+ * That means that if your log message is filtered out, you might be doing
+ * significant work and incurring significant overhead.
*/
public final class Log {
@@ -55,6 +88,29 @@ public final class Log {
*/
public static final int ASSERT = 7;
+ /**
+ * Exception class used to capture a stack trace in {@link #wtf}.
+ * @hide
+ */
+ public static class TerribleFailure extends Exception {
+ TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
+ }
+
+ /**
+ * Interface to handle terrible failures from {@link #wtf}.
+ *
+ * @hide
+ */
+ public interface TerribleFailureHandler {
+ void onTerribleFailure(String tag, TerribleFailure what, boolean system);
+ }
+
+ private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
+ public void onTerribleFailure(String tag, TerribleFailure what, boolean system) {
+ RuntimeInit.wtf(tag, what, system);
+ }
+ };
+
private Log() {
}
@@ -65,7 +121,7 @@ public final class Log {
* @param msg The message you would like logged.
*/
public static int v(String tag, String msg) {
- return println(LOG_ID_MAIN, VERBOSE, tag, msg);
+ return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
/**
@@ -76,7 +132,7 @@ public final class Log {
* @param tr An exception to log
*/
public static int v(String tag, String msg, Throwable tr) {
- return println(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);
}
/**
@@ -86,7 +142,7 @@ public final class Log {
* @param msg The message you would like logged.
*/
public static int d(String tag, String msg) {
- return println(LOG_ID_MAIN, DEBUG, tag, msg);
+ return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
}
/**
@@ -97,7 +153,7 @@ public final class Log {
* @param tr An exception to log
*/
public static int d(String tag, String msg, Throwable tr) {
- return println(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, DEBUG, tag, msg, tr);
}
/**
@@ -107,7 +163,7 @@ public final class Log {
* @param msg The message you would like logged.
*/
public static int i(String tag, String msg) {
- return println(LOG_ID_MAIN, INFO, tag, msg);
+ return println_native(LOG_ID_MAIN, INFO, tag, msg);
}
/**
@@ -118,7 +174,7 @@ public final class Log {
* @param tr An exception to log
*/
public static int i(String tag, String msg, Throwable tr) {
- return println(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, INFO, tag, msg, tr);
}
/**
@@ -128,7 +184,7 @@ public final class Log {
* @param msg The message you would like logged.
*/
public static int w(String tag, String msg) {
- return println(LOG_ID_MAIN, WARN, tag, msg);
+ return println_native(LOG_ID_MAIN, WARN, tag, msg);
}
/**
@@ -139,9 +195,31 @@ public final class Log {
* @param tr An exception to log
*/
public static int w(String tag, String msg, Throwable tr) {
- return println(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, WARN, tag, msg, tr);
}
+ /**
+ * Checks to see whether or not a log for the specified tag is loggable at the specified level.
+ *
+ * The default level of any tag is set to INFO. This means that any level above and including
+ * INFO will be logged. Before you make any calls to a logging method you should check to see
+ * if your tag should be logged. You can change the default level by setting a system property:
+ * 'setprop log.tag.&lt;YOUR_LOG_TAG> &lt;LEVEL>'
+ * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
+ * turn off all logging for your tag. You can also create a local.prop file that with the
+ * following in it:
+ * 'log.tag.&lt;YOUR_LOG_TAG>=&lt;LEVEL>'
+ * and place that in /data/local.prop.
+ *
+ * @param tag The tag to check.
+ * @param level The level to check.
+ * @return Whether or not that this is allowed to be logged.
+ * @throws IllegalArgumentException is thrown if the tag.length() > 23
+ * for Nougat (7.0) releases (API <= 23) and prior, there is no
+ * tag limit of concern after this API level.
+ */
+ public static native boolean isLoggable(String tag, int level);
+
/*
* Send a {@link #WARN} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
@@ -149,7 +227,7 @@ public final class Log {
* @param tr An exception to log
*/
public static int w(String tag, Throwable tr) {
- return println(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, WARN, tag, "", tr);
}
/**
@@ -159,7 +237,7 @@ public final class Log {
* @param msg The message you would like logged.
*/
public static int e(String tag, String msg) {
- return println(LOG_ID_MAIN, ERROR, tag, msg);
+ return println_native(LOG_ID_MAIN, ERROR, tag, msg);
}
/**
@@ -170,7 +248,82 @@ public final class Log {
* @param tr An exception to log
*/
public static int e(String tag, String msg, Throwable tr) {
- return println(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
+ return printlns(LOG_ID_MAIN, ERROR, tag, msg, tr);
+ }
+
+ /**
+ * What a Terrible Failure: Report a condition that should never happen.
+ * The error will always be logged at level ASSERT with the call stack.
+ * Depending on system configuration, a report may be added to the
+ * {@link android.os.DropBoxManager} and/or the process may be terminated
+ * immediately with an error dialog.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public static int wtf(String tag, String msg) {
+ return wtf(LOG_ID_MAIN, tag, msg, null, false, false);
+ }
+
+ /**
+ * Like {@link #wtf(String, String)}, but also writes to the log the full
+ * call stack.
+ * @hide
+ */
+ public static int wtfStack(String tag, String msg) {
+ return wtf(LOG_ID_MAIN, tag, msg, null, true, false);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, String)}, with an exception to log.
+ * @param tag Used to identify the source of a log message.
+ * @param tr An exception to log.
+ */
+ public static int wtf(String tag, Throwable tr) {
+ return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false, false);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, Throwable)}, with a message as well.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log. May be null.
+ */
+ public static int wtf(String tag, String msg, Throwable tr) {
+ return wtf(LOG_ID_MAIN, tag, msg, tr, false, false);
+ }
+
+ static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
+ boolean system) {
+ TerribleFailure what = new TerribleFailure(msg, tr);
+ // Only mark this as ERROR, do not use ASSERT since that should be
+ // reserved for cases where the system is guaranteed to abort.
+ // The onTerribleFailure call does not always cause a crash.
+ int bytes = printlns(logId, ERROR, tag, msg, localStack ? what : tr);
+ sWtfHandler.onTerribleFailure(tag, what, system);
+ return bytes;
+ }
+
+ static void wtfQuiet(int logId, String tag, String msg, boolean system) {
+ TerribleFailure what = new TerribleFailure(msg, null);
+ sWtfHandler.onTerribleFailure(tag, what, system);
+ }
+
+ /**
+ * Sets the terrible failure handler, for testing.
+ *
+ * @return the old handler
+ *
+ * @hide
+ */
+ public static TerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException("handler == null");
+ }
+ TerribleFailureHandler oldHandler = sWtfHandler;
+ sWtfHandler = handler;
+ return oldHandler;
}
/**
@@ -193,7 +346,7 @@ public final class Log {
}
StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
tr.printStackTrace(pw);
pw.flush();
return sw.toString();
@@ -208,7 +361,7 @@ public final class Log {
* @return The number of bytes written.
*/
public static int println(int priority, String tag, String msg) {
- return println(LOG_ID_MAIN, priority, tag, msg);
+ return println_native(LOG_ID_MAIN, priority, tag, msg);
}
/** @hide */ public static final int LOG_ID_MAIN = 0;
@@ -217,9 +370,115 @@ public final class Log {
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
/** @hide */ public static final int LOG_ID_CRASH = 4;
- /** @hide */ @SuppressWarnings("unused")
- public static int println(int bufID,
- int priority, String tag, String msg) {
- return 0;
+ /** @hide */ public static native int println_native(int bufID,
+ int priority, String tag, String msg);
+
+ /**
+ * Return the maximum payload the log daemon accepts without truncation.
+ * @return LOGGER_ENTRY_MAX_PAYLOAD.
+ */
+ private static native int logger_entry_max_payload_native();
+
+ /**
+ * Helper function for long messages. Uses the LineBreakBufferedWriter to break
+ * up long messages and stacktraces along newlines, but tries to write in large
+ * chunks. This is to avoid truncation.
+ * @hide
+ */
+ public static int printlns(int bufID, int priority, String tag, String msg,
+ Throwable tr) {
+ ImmediateLogWriter logWriter = new ImmediateLogWriter(bufID, priority, tag);
+ // Acceptable buffer size. Get the native buffer size, subtract two zero terminators,
+ // and the length of the tag.
+ // Note: we implicitly accept possible truncation for Modified-UTF8 differences. It
+ // is too expensive to compute that ahead of time.
+ int bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base.
+ - 2 // Two terminators.
+ - (tag != null ? tag.length() : 0) // Tag length.
+ - 32; // Some slack.
+ // At least assume you can print *some* characters (tag is not too large).
+ bufferSize = Math.max(bufferSize, 100);
+
+ LineBreakBufferedWriter lbbw = new LineBreakBufferedWriter(logWriter, bufferSize);
+
+ lbbw.println(msg);
+
+ if (tr != null) {
+ // This is to reduce the amount of log spew that apps do in the non-error
+ // condition of the network being unavailable.
+ Throwable t = tr;
+ while (t != null) {
+ if (t instanceof UnknownHostException) {
+ break;
+ }
+ if (t instanceof DeadSystemException) {
+ lbbw.println("DeadSystemException: The system died; "
+ + "earlier logs will point to the root cause");
+ break;
+ }
+ t = t.getCause();
+ }
+ if (t == null) {
+ tr.printStackTrace(lbbw);
+ }
+ }
+
+ lbbw.flush();
+
+ return logWriter.getWritten();
+ }
+
+ /**
+ * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
+ * a JNI call during logging.
+ */
+ static class PreloadHolder {
+ public final static int LOGGER_ENTRY_MAX_PAYLOAD =
+ logger_entry_max_payload_native();
+ }
+
+ /**
+ * Helper class to write to the logcat. Different from LogWriter, this writes
+ * the whole given buffer and does not break along newlines.
+ */
+ private static class ImmediateLogWriter extends Writer {
+
+ private int bufID;
+ private int priority;
+ private String tag;
+
+ private int written = 0;
+
+ /**
+ * Create a writer that immediately writes to the log, using the given
+ * parameters.
+ */
+ public ImmediateLogWriter(int bufID, int priority, String tag) {
+ this.bufID = bufID;
+ this.priority = priority;
+ this.tag = tag;
+ }
+
+ public int getWritten() {
+ return written;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) {
+ // Note: using String here has a bit of overhead as a Java object is created,
+ // but using the char[] directly is not easier, as it needs to be translated
+ // to a C char[] for logging.
+ written += println_native(bufID, priority, tag, new String(cbuf, off, len));
+ }
+
+ @Override
+ public void flush() {
+ // Ignored.
+ }
+
+ @Override
+ public void close() {
+ // Ignored.
+ }
}
}
diff --git a/android/util/LruCache.java b/android/util/LruCache.java
index 52086065..40154880 100644
--- a/android/util/LruCache.java
+++ b/android/util/LruCache.java
@@ -20,10 +20,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
- * BEGIN LAYOUTLIB CHANGE
- * This is a custom version that doesn't use the non standard LinkedHashMap#eldest.
- * END LAYOUTLIB CHANGE
- *
* A cache that holds strong references to a limited number of values. Each time
* a value is accessed, it is moved to the head of a queue. When a value is
* added to a full cache, the value at the end of that queue is evicted and may
@@ -91,9 +87,8 @@ public class LruCache<K, V> {
/**
* Sets the size of the cache.
- * @param maxSize The new maximum size.
*
- * @hide
+ * @param maxSize The new maximum size.
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
@@ -190,10 +185,13 @@ public class LruCache<K, V> {
}
/**
+ * Remove the eldest entries until the total of remaining entries is at or
+ * below the requested size.
+ *
* @param maxSize the maximum size of the cache before returning. May be -1
- * to evict even 0-sized elements.
+ * to evict even 0-sized elements.
*/
- private void trimToSize(int maxSize) {
+ public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
@@ -207,16 +205,7 @@ public class LruCache<K, V> {
break;
}
- // BEGIN LAYOUTLIB CHANGE
- // get the last item in the linked list.
- // This is not efficient, the goal here is to minimize the changes
- // compared to the platform version.
- Map.Entry<K, V> toEvict = null;
- for (Map.Entry<K, V> entry : map.entrySet()) {
- toEvict = entry;
- }
- // END LAYOUTLIB CHANGE
-
+ Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
diff --git a/android/util/StatsLog.java b/android/util/StatsLog.java
new file mode 100644
index 00000000..0be1a8cf
--- /dev/null
+++ b/android/util/StatsLog.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.util;
+
+/**
+ * Logging access for platform metrics.
+ *
+ * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
+ * These diagnostic stats are for system integrators, not application authors.
+ *
+ * <p>Stats use integer tag codes.
+ * They carry a payload of one or more int, long, or String values.
+ * @hide
+ */
+public class StatsLog {
+ /** @hide */ public StatsLog() {}
+
+ private static final String TAG = "StatsLog";
+
+ // We assume that the native methods deal with any concurrency issues.
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeInt(int tag, int value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeLong(int tag, long value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeFloat(int tag, float value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param str A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeString(int tag, String str);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param list A list of values to log. All values should
+ * be of type int, long, float or String.
+ * @return The number of bytes written
+ */
+ public static native int writeArray(int tag, Object... list);
+}
diff --git a/android/util/StatsLogKey.java b/android/util/StatsLogKey.java
new file mode 100644
index 00000000..9ad0a23d
--- /dev/null
+++ b/android/util/StatsLogKey.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogKey {
+ private StatsLogKey() {}
+
+ /** Constants for android.os.statsd.ScreenStateChange. */
+
+ /** display_state */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE = 1;
+
+ /** Constants for android.os.statsd.ProcessStateChange. */
+
+ /** state */
+ public static final int PROCESS_STATE_CHANGE__STATE = 1;
+
+ /** uid */
+ public static final int PROCESS_STATE_CHANGE__UID = 2;
+
+ /** package_name */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_NAME = 1002;
+
+ /** package_version */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION = 3;
+
+ /** package_version_string */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION_STRING = 4;
+
+}
diff --git a/android/support/car/drawer/DrawerItemClickListener.java b/android/util/StatsLogTag.java
index d707dbd0..5e5a8287 100644
--- a/android/support/car/drawer/DrawerItemClickListener.java
+++ b/android/util/StatsLogTag.java
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-package android.support.car.drawer;
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogTag {
+ private StatsLogTag() {}
+
+ /** android.os.statsd.ScreenStateChange. */
+ public static final int SCREEN_STATE_CHANGE = 2;
+
+ /** android.os.statsd.ProcessStateChange. */
+ public static final int PROCESS_STATE_CHANGE = 1112;
-/**
- * Listener for handling clicks on items/views managed by {@link DrawerItemViewHolder}.
- */
-public interface DrawerItemClickListener {
- /**
- * Callback when item is clicked.
- *
- * @param position Adapter position of the clicked item.
- */
- void onItemClick(int position);
}
diff --git a/android/util/StatsLogValue.java b/android/util/StatsLogValue.java
new file mode 100644
index 00000000..05b9d933
--- /dev/null
+++ b/android/util/StatsLogValue.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogValue {
+ private StatsLogValue() {}
+
+ /** Constants for android.os.statsd.ScreenStateChange. */
+
+ /** display_state: STATE_UNKNOWN */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_UNKNOWN = 0;
+
+ /** display_state: STATE_OFF */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF = 1;
+
+ /** display_state: STATE_ON */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON = 2;
+
+ /** display_state: STATE_DOZE */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE = 3;
+
+ /** display_state: STATE_DOZE_SUSPEND */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE_SUSPEND = 4;
+
+ /** display_state: STATE_VR */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_VR = 5;
+
+ /** Constants for android.os.statsd.ProcessStateChange. */
+
+ /** state: START */
+ public static final int PROCESS_STATE_CHANGE__STATE__START = 1;
+
+ /** state: CRASH */
+ public static final int PROCESS_STATE_CHANGE__STATE__CRASH = 2;
+
+}
diff --git a/android/view/SurfaceControl.java b/android/view/SurfaceControl.java
index 54825895..31daefff 100644
--- a/android/view/SurfaceControl.java
+++ b/android/view/SurfaceControl.java
@@ -21,22 +21,15 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
-import android.os.Debug;
import android.os.IBinder;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
import dalvik.system.CloseGuard;
-import java.io.Closeable;
-
-import libcore.util.NativeAllocationRegistry;
-
/**
* SurfaceControl
* @hide
@@ -61,34 +54,25 @@ public class SurfaceControl {
Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean allLayers, boolean useIdentityTransform);
- private static native long nativeCreateTransaction();
- private static native long nativeGetNativeTransactionFinalizer();
- private static native void nativeApplyTransaction(long transactionObj, boolean sync);
- private static native void nativeSetAnimationTransaction(long transactionObj);
-
- private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
- private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
- IBinder relativeTo, int zorder);
- private static native void nativeSetPosition(long transactionObj, long nativeObject,
- float x, float y);
- private static native void nativeSetGeometryAppliesWithResize(long transactionObj,
- long nativeObject);
- private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
- private static native void nativeSetTransparentRegionHint(long transactionObj,
- long nativeObject, Region region);
- private static native void nativeSetAlpha(long transactionObj, long nativeObject, float alpha);
- private static native void nativeSetMatrix(long transactionObj, long nativeObject,
- float dsdx, float dtdx,
+ private static native void nativeOpenTransaction();
+ private static native void nativeCloseTransaction(boolean sync);
+ private static native void nativeSetAnimationTransaction();
+
+ private static native void nativeSetLayer(long nativeObject, int zorder);
+ private static native void nativeSetRelativeLayer(long nativeObject, IBinder relativeTo,
+ int zorder);
+ private static native void nativeSetPosition(long nativeObject, float x, float y);
+ private static native void nativeSetGeometryAppliesWithResize(long nativeObject);
+ private static native void nativeSetSize(long nativeObject, int w, int h);
+ private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
+ private static native void nativeSetAlpha(long nativeObject, float alpha);
+ private static native void nativeSetColor(long nativeObject, float[] color);
+ private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
float dtdy, float dsdy);
- private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
- private static native void nativeSetFlags(long transactionObj, long nativeObject,
- int flags, int mask);
- private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
- int l, int t, int r, int b);
- private static native void nativeSetFinalCrop(long transactionObj, long nativeObject,
- int l, int t, int r, int b);
- private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
- int layerStack);
+ private static native void nativeSetFlags(long nativeObject, int flags, int mask);
+ private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
+ private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b);
+ private static native void nativeSetLayerStack(long nativeObject, int layerStack);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -98,16 +82,15 @@ public class SurfaceControl {
private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
- private static native void nativeSetDisplaySurface(long transactionObj,
+ private static native void nativeSetDisplaySurface(
IBinder displayToken, long nativeSurfaceObject);
- private static native void nativeSetDisplayLayerStack(long transactionObj,
+ private static native void nativeSetDisplayLayerStack(
IBinder displayToken, int layerStack);
- private static native void nativeSetDisplayProjection(long transactionObj,
+ private static native void nativeSetDisplayProjection(
IBinder displayToken, int orientation,
int l, int t, int r, int b,
int L, int T, int R, int B);
- private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
- int width, int height);
+ private static native void nativeSetDisplaySize(IBinder displayToken, int width, int height);
private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
IBinder displayToken);
private static native int nativeGetActiveConfig(IBinder displayToken);
@@ -118,17 +101,16 @@ public class SurfaceControl {
int colorMode);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
- private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
+ private static native void nativeDeferTransactionUntil(long nativeObject,
IBinder handle, long frame);
- private static native void nativeDeferTransactionUntilSurface(long transactionObj,
- long nativeObject,
+ private static native void nativeDeferTransactionUntilSurface(long nativeObject,
long surfaceObject, long frame);
- private static native void nativeReparentChildren(long transactionObj, long nativeObject,
+ private static native void nativeReparentChildren(long nativeObject,
IBinder handle);
- private static native void nativeReparent(long transactionObj, long nativeObject,
+ private static native void nativeReparent(long nativeObject,
IBinder parentHandle);
- private static native void nativeSeverChildren(long transactionObj, long nativeObject);
- private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
+ private static native void nativeSeverChildren(long nativeObject);
+ private static native void nativeSetOverrideScalingMode(long nativeObject,
int scalingMode);
private static native IBinder nativeGetHandle(long nativeObject);
private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -140,9 +122,6 @@ public class SurfaceControl {
private final String mName;
long mNativeObject; // package visibility only for Surface.java access
- static Transaction sGlobalTransaction;
- static long sTransactionNestCount = 0;
-
/* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
/**
@@ -398,6 +377,11 @@ public class SurfaceControl {
}
}
+ @Override
+ public String toString() {
+ return "Surface(name=" + mName + ")";
+ }
+
/**
* Release the local reference to the server-side surface.
* Always call release() when you're done with a Surface.
@@ -445,141 +429,102 @@ public class SurfaceControl {
/** start a transaction */
public static void openTransaction() {
- synchronized (SurfaceControl.class) {
- if (sGlobalTransaction == null) {
- sGlobalTransaction = new Transaction();
- }
- synchronized(SurfaceControl.class) {
- sTransactionNestCount++;
- }
- }
- }
-
- private static void closeTransaction(boolean sync) {
- synchronized(SurfaceControl.class) {
- if (sTransactionNestCount == 0) {
- Log.e(TAG, "Call to SurfaceControl.closeTransaction without matching openTransaction");
- } else if (--sTransactionNestCount > 0) {
- return;
- }
- sGlobalTransaction.apply(sync);
- }
+ nativeOpenTransaction();
}
/** end a transaction */
public static void closeTransaction() {
- closeTransaction(false);
+ nativeCloseTransaction(false);
}
public static void closeTransactionSync() {
- closeTransaction(true);
+ nativeCloseTransaction(true);
}
public void deferTransactionUntil(IBinder handle, long frame) {
if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntil(this, handle, frame);
- }
+ nativeDeferTransactionUntil(mNativeObject, handle, frame);
}
}
public void deferTransactionUntil(Surface barrier, long frame) {
if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
- }
+ nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
}
}
public void reparentChildren(IBinder newParentHandle) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparentChildren(this, newParentHandle);
- }
+ nativeReparentChildren(mNativeObject, newParentHandle);
}
+ /** Re-parents this layer to a new parent. */
public void reparent(IBinder newParentHandle) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparent(this, newParentHandle);
- }
+ nativeReparent(mNativeObject, newParentHandle);
}
public void detachChildren() {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.detachChildren(this);
- }
+ nativeSeverChildren(mNativeObject);
}
public void setOverrideScalingMode(int scalingMode) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setOverrideScalingMode(this, scalingMode);
- }
+ nativeSetOverrideScalingMode(mNativeObject, scalingMode);
}
public IBinder getHandle() {
return nativeGetHandle(mNativeObject);
}
+ /** flag the transaction as an animation */
public static void setAnimationTransaction() {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setAnimationTransaction();
- }
+ nativeSetAnimationTransaction();
}
public void setLayer(int zorder) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setLayer(this, zorder);
- }
+ nativeSetLayer(mNativeObject, zorder);
}
public void setRelativeLayer(IBinder relativeTo, int zorder) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder);
- }
+ nativeSetRelativeLayer(mNativeObject, relativeTo, zorder);
}
public void setPosition(float x, float y) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setPosition(this, x, y);
- }
+ nativeSetPosition(mNativeObject, x, y);
}
+ /**
+ * If the buffer size changes in this transaction, position and crop updates specified
+ * in this transaction will not complete until a buffer of the new size
+ * arrives. As transform matrix and size are already frozen in this fashion,
+ * this enables totally freezing the surface until the resize has completed
+ * (at which point the geometry influencing aspects of this transaction will then occur)
+ */
public void setGeometryAppliesWithResize() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setGeometryAppliesWithResize(this);
- }
+ nativeSetGeometryAppliesWithResize(mNativeObject);
}
public void setSize(int w, int h) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setSize(this, w, h);
- }
+ nativeSetSize(mNativeObject, w, h);
}
public void hide() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.hide(this);
- }
+ nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
}
public void show() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.show(this);
- }
+ nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN);
}
public void setTransparentRegionHint(Region region) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setTransparentRegionHint(this, region);
- }
+ nativeSetTransparentRegionHint(mNativeObject, region);
}
public boolean clearContentFrameStats() {
@@ -600,70 +545,80 @@ public class SurfaceControl {
return nativeGetAnimationFrameStats(outStats);
}
+ /**
+ * Sets an alpha value for the entire Surface. This value is combined with the
+ * per-pixel alpha. It may be used with opaque Surfaces.
+ */
public void setAlpha(float alpha) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setAlpha(this, alpha);
- }
+ nativeSetAlpha(mNativeObject, alpha);
}
+ /**
+ * Sets a color for the Surface.
+ * @param color A float array with three values to represent r, g, b in range [0..1]
+ */
public void setColor(@Size(3) float[] color) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setColor(this, color);
- }
+ nativeSetColor(mNativeObject, color);
}
public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setMatrix(this, dsdx, dtdx, dtdy, dsdy);
- }
+ nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy);
}
public void setWindowCrop(Rect crop) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setWindowCrop(this, crop);
+ if (crop != null) {
+ nativeSetWindowCrop(mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0);
}
}
public void setFinalCrop(Rect crop) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setFinalCrop(this, crop);
+ if (crop != null) {
+ nativeSetFinalCrop(mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetFinalCrop(mNativeObject, 0, 0, 0, 0);
}
}
public void setLayerStack(int layerStack) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setLayerStack(this, layerStack);
- }
+ nativeSetLayerStack(mNativeObject, layerStack);
}
+ /**
+ * Sets the opacity of the surface. Setting the flag is equivalent to creating the
+ * Surface with the {@link #OPAQUE} flag.
+ */
public void setOpaque(boolean isOpaque) {
checkNotReleased();
-
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setOpaque(this, isOpaque);
+ if (isOpaque) {
+ nativeSetFlags(mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
+ } else {
+ nativeSetFlags(mNativeObject, 0, SURFACE_OPAQUE);
}
}
+ /**
+ * Sets the security of the surface. Setting the flag is equivalent to creating the
+ * Surface with the {@link #SECURE} flag.
+ */
public void setSecure(boolean isSecure) {
checkNotReleased();
-
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setSecure(this, isSecure);
+ if (isSecure) {
+ nativeSetFlags(mNativeObject, SECURE, SECURE);
+ } else {
+ nativeSetFlags(mNativeObject, 0, SECURE);
}
}
- @Override
- public String toString() {
- return "Surface(name=" + mName + ")/@0x" +
- Integer.toHexString(System.identityHashCode(this));
- }
-
/*
* set display parameters.
* needs to be inside open/closeTransaction block
@@ -786,28 +741,50 @@ public class SurfaceControl {
public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplayProjection(displayToken, orientation,
- layerStackRect, displayRect);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (layerStackRect == null) {
+ throw new IllegalArgumentException("layerStackRect must not be null");
}
+ if (displayRect == null) {
+ throw new IllegalArgumentException("displayRect must not be null");
+ }
+ nativeSetDisplayProjection(displayToken, orientation,
+ layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
+ displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
}
public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplayLayerStack(displayToken, layerStack);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
}
+ nativeSetDisplayLayerStack(displayToken, layerStack);
}
public static void setDisplaySurface(IBinder displayToken, Surface surface) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplaySurface(displayToken, surface);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ if (surface != null) {
+ synchronized (surface.mLock) {
+ nativeSetDisplaySurface(displayToken, surface.mNativeObject);
+ }
+ } else {
+ nativeSetDisplaySurface(displayToken, 0);
}
}
public static void setDisplaySize(IBinder displayToken, int width, int height) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplaySize(displayToken, width, height);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be positive");
}
+
+ nativeSetDisplaySize(displayToken, width, height);
}
public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) {
@@ -969,261 +946,4 @@ public class SurfaceControl {
nativeScreenshot(display, consumer, sourceCrop, width, height,
minLayer, maxLayer, allLayers, useIdentityTransform);
}
-
- public static class Transaction implements Closeable {
- public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- Transaction.class.getClassLoader(),
- nativeGetNativeTransactionFinalizer(), 512);
- private long mNativeObject;
-
- Runnable mFreeNativeResources;
-
- public Transaction() {
- mNativeObject = nativeCreateTransaction();
- mFreeNativeResources
- = sRegistry.registerNativeAllocation(this, mNativeObject);
- }
-
- /**
- * Apply the transaction, clearing it's state, and making it usable
- * as a new transaction.
- */
- public void apply() {
- apply(false);
- }
-
- /**
- * Close the transaction, if the transaction was not already applied this will cancel the
- * transaction.
- */
- @Override
- public void close() {
- mFreeNativeResources.run();
- mNativeObject = 0;
- }
-
- /**
- * Jankier version of apply. Avoid use (b/28068298).
- */
- public void apply(boolean sync) {
- nativeApplyTransaction(mNativeObject, sync);
- }
-
- public Transaction show(SurfaceControl sc) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
- return this;
- }
-
- public Transaction hide(SurfaceControl sc) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
- return this;
- }
-
- public Transaction setPosition(SurfaceControl sc, float x, float y) {
- nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
- return this;
- }
-
- public Transaction setSize(SurfaceControl sc, int w, int h) {
- nativeSetSize(mNativeObject, sc.mNativeObject,
- w, h);
- return this;
- }
-
- public Transaction setLayer(SurfaceControl sc, int z) {
- nativeSetLayer(mNativeObject, sc.mNativeObject, z);
- return this;
- }
-
- public Transaction setRelativeLayer(SurfaceControl sc, IBinder relativeTo, int z) {
- nativeSetRelativeLayer(mNativeObject, sc.mNativeObject,
- relativeTo, z);
- return this;
- }
-
- public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
- nativeSetTransparentRegionHint(mNativeObject,
- sc.mNativeObject, transparentRegion);
- return this;
- }
-
- public Transaction setAlpha(SurfaceControl sc, float alpha) {
- nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
- return this;
- }
-
- public Transaction setMatrix(SurfaceControl sc,
- float dsdx, float dtdx, float dtdy, float dsdy) {
- nativeSetMatrix(mNativeObject, sc.mNativeObject,
- dsdx, dtdx, dtdy, dsdy);
- return this;
- }
-
- public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
- if (crop != null) {
- nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
- crop.left, crop.top, crop.right, crop.bottom);
- } else {
- nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
- }
-
- return this;
- }
-
- public Transaction setFinalCrop(SurfaceControl sc, Rect crop) {
- if (crop != null) {
- nativeSetFinalCrop(mNativeObject, sc.mNativeObject,
- crop.left, crop.top, crop.right, crop.bottom);
- } else {
- nativeSetFinalCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
- }
-
- return this;
- }
-
- public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
- nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
- return this;
- }
-
- public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle, long frameNumber) {
- nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
- return this;
- }
-
- public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
- long frameNumber) {
- nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
- barrierSurface.mNativeObject, frameNumber);
- return this;
- }
-
- public Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) {
- nativeReparentChildren(mNativeObject, sc.mNativeObject, newParentHandle);
- return this;
- }
-
- /** Re-parents a specific child layer to a new parent */
- public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
- nativeReparent(mNativeObject, sc.mNativeObject,
- newParentHandle);
- return this;
- }
-
- public Transaction detachChildren(SurfaceControl sc) {
- nativeSeverChildren(mNativeObject, sc.mNativeObject);
- return this;
- }
-
- public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
- nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
- overrideScalingMode);
- return this;
- }
-
- /**
- * Sets a color for the Surface.
- * @param color A float array with three values to represent r, g, b in range [0..1]
- */
- public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) {
- nativeSetColor(mNativeObject, sc.mNativeObject, color);
- return this;
- }
-
- /**
- * If the buffer size changes in this transaction, position and crop updates specified
- * in this transaction will not complete until a buffer of the new size
- * arrives. As transform matrix and size are already frozen in this fashion,
- * this enables totally freezing the surface until the resize has completed
- * (at which point the geometry influencing aspects of this transaction will then occur)
- */
- public Transaction setGeometryAppliesWithResize(SurfaceControl sc) {
- nativeSetGeometryAppliesWithResize(mNativeObject, sc.mNativeObject);
- return this;
- }
-
- /**
- * Sets the security of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #SECURE} flag.
- */
- Transaction setSecure(SurfaceControl sc, boolean isSecure) {
- if (isSecure) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
- } else {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SECURE);
- }
- return this;
- }
-
- /**
- * Sets the opacity of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #OPAQUE} flag.
- */
- public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
- if (isOpaque) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, OPAQUE, OPAQUE);
- } else {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, OPAQUE);
- }
- return this;
- }
-
- public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- if (surface != null) {
- synchronized (surface.mLock) {
- nativeSetDisplaySurface(mNativeObject, displayToken, surface.mNativeObject);
- }
- } else {
- nativeSetDisplaySurface(mNativeObject, displayToken, 0);
- }
- return this;
- }
-
- public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- nativeSetDisplayLayerStack(mNativeObject, displayToken, layerStack);
- return this;
- }
-
- public Transaction setDisplayProjection(IBinder displayToken,
- int orientation, Rect layerStackRect, Rect displayRect) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (layerStackRect == null) {
- throw new IllegalArgumentException("layerStackRect must not be null");
- }
- if (displayRect == null) {
- throw new IllegalArgumentException("displayRect must not be null");
- }
- nativeSetDisplayProjection(mNativeObject, displayToken, orientation,
- layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
- displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
- return this;
- }
-
- public Transaction setDisplaySize(IBinder displayToken, int width, int height) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("width and height must be positive");
- }
-
- nativeSetDisplaySize(mNativeObject, displayToken, width, height);
- return this;
- }
-
- /** flag the transaction as an animation */
- public Transaction setAnimationTransaction() {
- nativeSetAnimationTransaction(mNativeObject);
- return this;
- }
- }
}
diff --git a/android/view/SurfaceView.java b/android/view/SurfaceView.java
index ebb2af45..462dad3f 100644
--- a/android/view/SurfaceView.java
+++ b/android/view/SurfaceView.java
@@ -16,115 +16,1208 @@
package android.view;
-import com.android.layoutlib.bridge.MockView;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
+import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
+import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
import android.content.Context;
+import android.content.res.CompatibilityInfo.Translator;
+import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.view.SurfaceCallbackHelper;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
/**
- * Mock version of the SurfaceView.
- * Only non override public methods from the real SurfaceView have been added in there.
- * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * Provides a dedicated drawing surface embedded inside of a view hierarchy.
+ * You can control the format of this surface and, if you like, its size; the
+ * SurfaceView takes care of placing the surface at the correct location on the
+ * screen
+ *
+ * <p>The surface is Z ordered so that it is behind the window holding its
+ * SurfaceView; the SurfaceView punches a hole in its window to allow its
+ * surface to be displayed. The view hierarchy will take care of correctly
+ * compositing with the Surface any siblings of the SurfaceView that would
+ * normally appear on top of it. This can be used to place overlays such as
+ * buttons on top of the Surface, though note however that it can have an
+ * impact on performance since a full alpha-blended composite will be performed
+ * each time the Surface changes.
+ *
+ * <p> The transparent region that makes the surface visible is based on the
+ * layout positions in the view hierarchy. If the post-layout transform
+ * properties are used to draw a sibling view on top of the SurfaceView, the
+ * view may not be properly composited with the surface.
*
- * TODO: generate automatically.
+ * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
+ * which can be retrieved by calling {@link #getHolder}.
*
+ * <p>The Surface will be created for you while the SurfaceView's window is
+ * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
+ * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
+ * Surface is created and destroyed as the window is shown and hidden.
+ *
+ * <p>One of the purposes of this class is to provide a surface in which a
+ * secondary thread can render into the screen. If you are going to use it
+ * this way, you need to be aware of some threading semantics:
+ *
+ * <ul>
+ * <li> All SurfaceView and
+ * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
+ * from the thread running the SurfaceView's window (typically the main thread
+ * of the application). They thus need to correctly synchronize with any
+ * state that is also touched by the drawing thread.
+ * <li> You must ensure that the drawing thread only touches the underlying
+ * Surface while it is valid -- between
+ * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
+ * and
+ * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
+ * </ul>
+ *
+ * <p class="note"><strong>Note:</strong> Starting in platform version
+ * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
+ * updated synchronously with other View rendering. This means that translating
+ * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
+ * artifacts may occur on previous versions of the platform when its window is
+ * positioned asynchronously.</p>
*/
-public class SurfaceView extends MockView {
+public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
+ private static final String TAG = "SurfaceView";
+ private static final boolean DEBUG = false;
+
+ final ArrayList<SurfaceHolder.Callback> mCallbacks
+ = new ArrayList<SurfaceHolder.Callback>();
+
+ final int[] mLocation = new int[2];
+
+ final ReentrantLock mSurfaceLock = new ReentrantLock();
+ final Surface mSurface = new Surface(); // Current surface in use
+ boolean mDrawingStopped = true;
+ // We use this to track if the application has produced a frame
+ // in to the Surface. Up until that point, we should be careful not to punch
+ // holes.
+ boolean mDrawFinished = false;
+
+ final Rect mScreenRect = new Rect();
+ SurfaceSession mSurfaceSession;
+
+ SurfaceControl mSurfaceControl;
+ // In the case of format changes we switch out the surface in-place
+ // we need to preserve the old one until the new one has drawn.
+ SurfaceControl mDeferredDestroySurfaceControl;
+ final Rect mTmpRect = new Rect();
+ final Configuration mConfiguration = new Configuration();
+
+ int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+
+ boolean mIsCreating = false;
+ private volatile boolean mRtHandlingPositionUpdates = false;
+
+ private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+ = new ViewTreeObserver.OnScrollChangedListener() {
+ @Override
+ public void onScrollChanged() {
+ updateSurface();
+ }
+ };
+
+ private final ViewTreeObserver.OnPreDrawListener mDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // reposition ourselves where the surface is
+ mHaveFrame = getWidth() > 0 && getHeight() > 0;
+ updateSurface();
+ return true;
+ }
+ };
+
+ boolean mRequestedVisible = false;
+ boolean mWindowVisibility = false;
+ boolean mLastWindowVisibility = false;
+ boolean mViewVisibility = false;
+ boolean mWindowStopped = false;
+
+ int mRequestedWidth = -1;
+ int mRequestedHeight = -1;
+ /* Set SurfaceView's format to 565 by default to maintain backward
+ * compatibility with applications assuming this format.
+ */
+ int mRequestedFormat = PixelFormat.RGB_565;
+
+ boolean mHaveFrame = false;
+ boolean mSurfaceCreated = false;
+ long mLastLockTime = 0;
+
+ boolean mVisible = false;
+ int mWindowSpaceLeft = -1;
+ int mWindowSpaceTop = -1;
+ int mSurfaceWidth = -1;
+ int mSurfaceHeight = -1;
+ int mFormat = -1;
+ final Rect mSurfaceFrame = new Rect();
+ int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+ private Translator mTranslator;
+
+ private boolean mGlobalListenersAdded;
+ private boolean mAttachedToWindow;
+
+ private int mSurfaceFlags = SurfaceControl.HIDDEN;
+
+ private int mPendingReportDraws;
public SurfaceView(Context context) {
this(context, null);
}
public SurfaceView(Context context, AttributeSet attrs) {
- this(context, attrs , 0);
+ this(context, attrs, 0);
}
- public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mRenderNode.requestPositionUpdates(this);
+
+ setWillNotDraw(true);
+ }
+
+ /**
+ * Return the SurfaceHolder providing access and control over this
+ * SurfaceView's underlying surface.
+ *
+ * @return SurfaceHolder The holder of the surface.
+ */
+ public SurfaceHolder getHolder() {
+ return mSurfaceHolder;
+ }
+
+ private void updateRequestedVisibility() {
+ mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
+ }
+
+ /** @hide */
+ @Override
+ public void windowStopped(boolean stopped) {
+ mWindowStopped = stopped;
+ updateRequestedVisibility();
+ updateSurface();
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ getViewRootImpl().addWindowStoppedCallback(this);
+ mWindowStopped = false;
+
+ mViewVisibility = getVisibility() == VISIBLE;
+ updateRequestedVisibility();
+
+ mAttachedToWindow = true;
+ mParent.requestTransparentRegion(SurfaceView.this);
+ if (!mGlobalListenersAdded) {
+ ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnScrollChangedListener(mScrollChangedListener);
+ observer.addOnPreDrawListener(mDrawListener);
+ mGlobalListenersAdded = true;
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mWindowVisibility = visibility == VISIBLE;
+ updateRequestedVisibility();
+ updateSurface();
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ mViewVisibility = visibility == VISIBLE;
+ boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped;
+ if (newRequestedVisible != mRequestedVisible) {
+ // our base class (View) invalidates the layout only when
+ // we go from/to the GONE state. However, SurfaceView needs
+ // to request a re-layout when the visibility changes at all.
+ // This is needed because the transparent region is computed
+ // as part of the layout phase, and it changes (obviously) when
+ // the visibility changes.
+ requestLayout();
+ }
+ mRequestedVisible = newRequestedVisible;
+ updateSurface();
+ }
+
+ private void performDrawFinished() {
+ if (mPendingReportDraws > 0) {
+ mDrawFinished = true;
+ if (mAttachedToWindow) {
+ notifyDrawFinished();
+ invalidate();
+ }
+ } else {
+ Log.e(TAG, System.identityHashCode(this) + "finished drawing"
+ + " but no pending report draw (extra call"
+ + " to draw completion runnable?)");
+ }
+ }
+
+ void notifyDrawFinished() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.pendingDrawFinished();
+ }
+ mPendingReportDraws--;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ // It's possible to create a SurfaceView using the default constructor and never
+ // attach it to a view hierarchy, this is a common use case when dealing with
+ // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
+ // the lifecycle. Instead of attaching it to a view, he/she can just pass
+ // the SurfaceHolder forward, most live wallpapers do it.
+ if (viewRoot != null) {
+ viewRoot.removeWindowStoppedCallback(this);
+ }
+
+ mAttachedToWindow = false;
+ if (mGlobalListenersAdded) {
+ ViewTreeObserver observer = getViewTreeObserver();
+ observer.removeOnScrollChangedListener(mScrollChangedListener);
+ observer.removeOnPreDrawListener(mDrawListener);
+ mGlobalListenersAdded = false;
+ }
+
+ while (mPendingReportDraws > 0) {
+ notifyDrawFinished();
+ }
+
+ mRequestedVisible = false;
+
+ updateSurface();
+ if (mSurfaceControl != null) {
+ mSurfaceControl.destroy();
+ }
+ mSurfaceControl = null;
+
+ mHaveFrame = false;
+
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = mRequestedWidth >= 0
+ ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
+ : getDefaultSize(0, widthMeasureSpec);
+ int height = mRequestedHeight >= 0
+ ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
+ : getDefaultSize(0, heightMeasureSpec);
+ setMeasuredDimension(width, height);
+ }
+
+ /** @hide */
+ @Override
+ protected boolean setFrame(int left, int top, int right, int bottom) {
+ boolean result = super.setFrame(left, top, right, bottom);
+ updateSurface();
+ return result;
+ }
+
+ @Override
public boolean gatherTransparentRegion(Region region) {
- return false;
+ if (isAboveParent() || !mDrawFinished) {
+ return super.gatherTransparentRegion(region);
+ }
+
+ boolean opaque = true;
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+ // this view draws, remove it from the transparent region
+ opaque = super.gatherTransparentRegion(region);
+ } else if (region != null) {
+ int w = getWidth();
+ int h = getHeight();
+ if (w>0 && h>0) {
+ getLocationInWindow(mLocation);
+ // otherwise, punch a hole in the whole hierarchy
+ int l = mLocation[0];
+ int t = mLocation[1];
+ region.op(l, t, l+w, t+h, Region.Op.UNION);
+ }
+ }
+ if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
+ opaque = false;
+ }
+ return opaque;
}
+ @Override
+ public void draw(Canvas canvas) {
+ if (mDrawFinished && !isAboveParent()) {
+ // draw() is not called when SKIP_DRAW is set
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ }
+ super.draw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mDrawFinished && !isAboveParent()) {
+ // draw() is not called when SKIP_DRAW is set
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Control whether the surface view's surface is placed on top of another
+ * regular surface view in the window (but still behind the window itself).
+ * This is typically used to place overlays on top of an underlying media
+ * surface view.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ */
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ mSubLayer = isMediaOverlay
+ ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
}
+ /**
+ * Control whether the surface view's surface is placed on top of its
+ * window. Normally it is placed behind the window, to allow it to
+ * (for the most part) appear to composite with the views in the
+ * hierarchy. By setting this, you cause it to be placed above the
+ * window. This means that none of the contents of the window this
+ * SurfaceView is in will be visible on top of its surface.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ */
public void setZOrderOnTop(boolean onTop) {
+ if (onTop) {
+ mSubLayer = APPLICATION_PANEL_SUBLAYER;
+ } else {
+ mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ }
}
+ /**
+ * Control whether the surface view's content should be treated as secure,
+ * preventing it from appearing in screenshots or from being viewed on
+ * non-secure displays.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>See {@link android.view.Display#FLAG_SECURE} for details.
+ *
+ * @param isSecure True if the surface view is secure.
+ */
public void setSecure(boolean isSecure) {
+ if (isSecure) {
+ mSurfaceFlags |= SurfaceControl.SECURE;
+ } else {
+ mSurfaceFlags &= ~SurfaceControl.SECURE;
+ }
}
- public SurfaceHolder getHolder() {
- return mSurfaceHolder;
+ private void updateOpaqueFlag() {
+ if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
+ mSurfaceFlags |= SurfaceControl.OPAQUE;
+ } else {
+ mSurfaceFlags &= ~SurfaceControl.OPAQUE;
+ }
}
- private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+ private Rect getParentSurfaceInsets() {
+ final ViewRootImpl root = getViewRootImpl();
+ if (root == null) {
+ return null;
+ } else {
+ return root.mWindowAttributes.surfaceInsets;
+ }
+ }
+
+ /** @hide */
+ protected void updateSurface() {
+ if (!mHaveFrame) {
+ return;
+ }
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+ return;
+ }
+
+ mTranslator = viewRoot.mTranslator;
+ if (mTranslator != null) {
+ mSurface.setCompatibilityTranslator(mTranslator);
+ }
+
+ int myWidth = mRequestedWidth;
+ if (myWidth <= 0) myWidth = getWidth();
+ int myHeight = mRequestedHeight;
+ if (myHeight <= 0) myHeight = getHeight();
+
+ final boolean formatChanged = mFormat != mRequestedFormat;
+ final boolean visibleChanged = mVisible != mRequestedVisible;
+ final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
+ && mRequestedVisible;
+ final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+ final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+ boolean redrawNeeded = false;
+
+ if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+ getLocationInWindow(mLocation);
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Changes: creating=" + creating
+ + " format=" + formatChanged + " size=" + sizeChanged
+ + " visible=" + visibleChanged
+ + " left=" + (mWindowSpaceLeft != mLocation[0])
+ + " top=" + (mWindowSpaceTop != mLocation[1]));
+
+ try {
+ final boolean visible = mVisible = mRequestedVisible;
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ mSurfaceWidth = myWidth;
+ mSurfaceHeight = myHeight;
+ mFormat = mRequestedFormat;
+ mLastWindowVisibility = mWindowVisibility;
+
+ mScreenRect.left = mWindowSpaceLeft;
+ mScreenRect.top = mWindowSpaceTop;
+ mScreenRect.right = mWindowSpaceLeft + getWidth();
+ mScreenRect.bottom = mWindowSpaceTop + getHeight();
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ }
+
+ final Rect surfaceInsets = getParentSurfaceInsets();
+ mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
+
+ if (creating) {
+ mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
+ mDeferredDestroySurfaceControl = mSurfaceControl;
+
+ updateOpaqueFlag();
+ mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession,
+ "SurfaceView - " + viewRoot.getTitle().toString(),
+ mSurfaceWidth, mSurfaceHeight, mFormat,
+ mSurfaceFlags);
+ } else if (mSurfaceControl == null) {
+ return;
+ }
+
+ boolean realSizeChanged = false;
+
+ mSurfaceLock.lock();
+ try {
+ mDrawingStopped = !visible;
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Cur surface: " + mSurface);
+
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(mSubLayer);
+ if (mViewVisibility) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+
+ // While creating the surface, we will set it's initial
+ // geometry. Outside of that though, we should generally
+ // leave it to the RenderThread.
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+ mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
+ mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
+ 0.0f, 0.0f,
+ mScreenRect.height() / (float) mSurfaceHeight);
+ }
+ if (sizeChanged) {
+ mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
+ }
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+
+ if (sizeChanged || creating) {
+ redrawNeeded = true;
+ }
+
+ mSurfaceFrame.left = 0;
+ mSurfaceFrame.top = 0;
+ if (mTranslator == null) {
+ mSurfaceFrame.right = mSurfaceWidth;
+ mSurfaceFrame.bottom = mSurfaceHeight;
+ } else {
+ float appInvertedScale = mTranslator.applicationInvertedScale;
+ mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ }
+
+ final int surfaceWidth = mSurfaceFrame.right;
+ final int surfaceHeight = mSurfaceFrame.bottom;
+ realSizeChanged = mLastSurfaceWidth != surfaceWidth
+ || mLastSurfaceHeight != surfaceHeight;
+ mLastSurfaceWidth = surfaceWidth;
+ mLastSurfaceHeight = surfaceHeight;
+ } finally {
+ mSurfaceLock.unlock();
+ }
+
+ try {
+ redrawNeeded |= visible && !mDrawFinished;
+
+ SurfaceHolder.Callback callbacks[] = null;
+
+ final boolean surfaceChanged = creating;
+ if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+ mSurfaceCreated = false;
+ if (mSurface.isValid()) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "visibleChanged -- surfaceDestroyed");
+ callbacks = getSurfaceCallbacks();
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ // Since Android N the same surface may be reused and given to us
+ // again by the system server at a later point. However
+ // as we didn't do this in previous releases, clients weren't
+ // necessarily required to clean up properly in
+ // surfaceDestroyed. This leads to problems for example when
+ // clients don't destroy their EGL context, and try
+ // and create a new one on the same surface following reuse.
+ // Since there is no valid use of the surface in-between
+ // surfaceDestroyed and surfaceCreated, we force a disconnect,
+ // so the next connect will always work if we end up reusing
+ // the surface.
+ if (mSurface.isValid()) {
+ mSurface.forceScopedDisconnect();
+ }
+ }
+ }
+
+ if (creating) {
+ mSurface.copyFrom(mSurfaceControl);
+ }
+
+ if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ mSurface.createFrom(mSurfaceControl);
+ }
+
+ if (visible && mSurface.isValid()) {
+ if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
+ mSurfaceCreated = true;
+ mIsCreating = true;
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "visibleChanged -- surfaceCreated");
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceCreated(mSurfaceHolder);
+ }
+ }
+ if (creating || formatChanged || sizeChanged
+ || visibleChanged || realSizeChanged) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceChanged -- format=" + mFormat
+ + " w=" + myWidth + " h=" + myHeight);
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
+ }
+ }
+ if (redrawNeeded) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceRedrawNeeded");
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+
+ mPendingReportDraws++;
+ viewRoot.drawPending();
+ SurfaceCallbackHelper sch =
+ new SurfaceCallbackHelper(this::onDrawFinished);
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+ }
+ }
+ } finally {
+ mIsCreating = false;
+ if (mSurfaceControl != null && !mSurfaceCreated) {
+ mSurface.release();
+ // If we are not in the stopped state, then the destruction of the Surface
+ // represents a visual change we need to display, and we should go ahead
+ // and destroy the SurfaceControl. However if we are in the stopped state,
+ // we can just leave the Surface around so it can be a part of animations,
+ // and we let the life-time be tied to the parent surface.
+ if (!mWindowStopped) {
+ mSurfaceControl.destroy();
+ mSurfaceControl = null;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ if (DEBUG) Log.v(
+ TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
+ + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
+ + ", frame=" + mSurfaceFrame);
+ } else {
+ // Calculate the window position in case RT loses the window
+ // and we need to fallback to a UI-thread driven position update
+ getLocationInSurface(mLocation);
+ final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
+ || mWindowSpaceTop != mLocation[1];
+ final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
+ || getHeight() != mScreenRect.height();
+ if (positionChanged || layoutSizeChanged) { // Only the position has changed
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+ // in view local space.
+ mLocation[0] = getWidth();
+ mLocation[1] = getHeight();
+
+ mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+ mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ }
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
+ try {
+ if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ mScreenRect.left, mScreenRect.top,
+ mScreenRect.right, mScreenRect.bottom));
+ setParentSpaceRectangle(mScreenRect, -1);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ }
+ }
+ }
+ }
+
+ private void onDrawFinished() {
+ if (DEBUG) {
+ Log.i(TAG, System.identityHashCode(this) + " "
+ + "finishedDrawing");
+ }
+
+ if (mDeferredDestroySurfaceControl != null) {
+ mDeferredDestroySurfaceControl.destroy();
+ mDeferredDestroySurfaceControl = null;
+ }
+
+ runOnUiThread(() -> {
+ performDrawFinished();
+ });
+ }
+
+ private void setParentSpaceRectangle(Rect position, long frameNumber) {
+ ViewRootImpl viewRoot = getViewRootImpl();
+
+ SurfaceControl.openTransaction();
+ try {
+ if (frameNumber > 0) {
+ mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
+ }
+ mSurfaceControl.setPosition(position.left, position.top);
+ mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
+ 0.0f, 0.0f,
+ position.height() / (float) mSurfaceHeight);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+
+ private Rect mRTLastReportedPosition = new Rect();
+
+ /**
+ * Called by native by a Rendering Worker thread to update the window position
+ * @hide
+ */
+ public final void updateSurfacePosition_renderWorker(long frameNumber,
+ int left, int top, int right, int bottom) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+ // its 2nd frame if RenderThread is running slowly could potentially see
+ // this as false, enter the branch, get pre-empted, then this comes along
+ // and reports a new position, then the UI thread resumes and reports
+ // its position. This could therefore be de-sync'd in that interval, but
+ // the synchronization would violate the rule that RT must never block
+ // on the UI thread which would open up potential deadlocks. The risk of
+ // a single-frame desync is therefore preferable for now.
+ mRtHandlingPositionUpdates = true;
+ if (mRTLastReportedPosition.left == left
+ && mRTLastReportedPosition.top == top
+ && mRTLastReportedPosition.right == right
+ && mRTLastReportedPosition.bottom == bottom) {
+ return;
+ }
+ try {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ frameNumber, left, top, right, bottom));
+ }
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ // Now overwrite mRTLastReportedPosition with our values
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception from repositionChild", ex);
+ }
+ }
+
+ /**
+ * Called by native on RenderThread to notify that the view is no longer in the
+ * draw tree. UI thread is blocked at this point.
+ * @hide
+ */
+ public final void surfacePositionLost_uiRtSync(long frameNumber) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
+ System.identityHashCode(this), frameNumber));
+ }
+ mRTLastReportedPosition.setEmpty();
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (mRtHandlingPositionUpdates) {
+ mRtHandlingPositionUpdates = false;
+ // This callback will happen while the UI thread is blocked, so we can
+ // safely access other member variables at this time.
+ // So do what the UI thread would have done if RT wasn't handling position
+ // updates.
+ if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+ try {
+ if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ mScreenRect.left, mScreenRect.top,
+ mScreenRect.right, mScreenRect.bottom));
+ setParentSpaceRectangle(mScreenRect, frameNumber);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ }
+ }
+ }
+
+ private SurfaceHolder.Callback[] getSurfaceCallbacks() {
+ SurfaceHolder.Callback callbacks[];
+ synchronized (mCallbacks) {
+ callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+ mCallbacks.toArray(callbacks);
+ }
+ return callbacks;
+ }
+
+ /**
+ * This method still exists only for compatibility reasons because some applications have relied
+ * on this method via reflection. See Issue 36345857 for details.
+ *
+ * @deprecated No platform code is using this method anymore.
+ * @hide
+ */
+ @Deprecated
+ public void setWindowType(int type) {
+ if (getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+ throw new UnsupportedOperationException(
+ "SurfaceView#setWindowType() has never been a public API.");
+ }
+
+ if (type == TYPE_APPLICATION_PANEL) {
+ Log.e(TAG, "If you are calling SurfaceView#setWindowType(TYPE_APPLICATION_PANEL) "
+ + "just to make the SurfaceView to be placed on top of its window, you must "
+ + "call setZOrderOnTop(true) instead.", new Throwable());
+ setZOrderOnTop(true);
+ return;
+ }
+ Log.e(TAG, "SurfaceView#setWindowType(int) is deprecated and now does nothing. "
+ + "type=" + type, new Throwable());
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ Handler handler = getHandler();
+ if (handler != null && handler.getLooper() != Looper.myLooper()) {
+ handler.post(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ /**
+ * Check to see if the surface has fixed size dimensions or if the surface's
+ * dimensions are dimensions are dependent on its current layout.
+ *
+ * @return true if the surface has dimensions that are fixed in size
+ * @hide
+ */
+ public boolean isFixedSize() {
+ return (mRequestedWidth != -1 || mRequestedHeight != -1);
+ }
+
+ private boolean isAboveParent() {
+ return mSubLayer >= 0;
+ }
+
+ private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+ private static final String LOG_TAG = "SurfaceHolder";
@Override
public boolean isCreating() {
- return false;
+ return mIsCreating;
}
@Override
public void addCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ // This is a linear search, but in practice we'll
+ // have only a couple callbacks, so it doesn't matter.
+ if (mCallbacks.contains(callback) == false) {
+ mCallbacks.add(callback);
+ }
+ }
}
@Override
public void removeCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
}
@Override
public void setFixedSize(int width, int height) {
+ if (mRequestedWidth != width || mRequestedHeight != height) {
+ mRequestedWidth = width;
+ mRequestedHeight = height;
+ requestLayout();
+ }
}
@Override
public void setSizeFromLayout() {
+ if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ mRequestedWidth = mRequestedHeight = -1;
+ requestLayout();
+ }
}
@Override
public void setFormat(int format) {
+ // for backward compatibility reason, OPAQUE always
+ // means 565 for SurfaceView
+ if (format == PixelFormat.OPAQUE)
+ format = PixelFormat.RGB_565;
+
+ mRequestedFormat = format;
+ if (mSurfaceControl != null) {
+ updateSurface();
+ }
}
+ /**
+ * @deprecated setType is now ignored.
+ */
@Override
- public void setType(int type) {
- }
+ @Deprecated
+ public void setType(int type) { }
@Override
public void setKeepScreenOn(boolean screenOn) {
+ runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
}
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * The caller must redraw the entire surface.
+ * @return A canvas for drawing into the surface.
+ */
@Override
public Canvas lockCanvas() {
- return null;
+ return internalLockCanvas(null, false);
}
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * @param inOutDirty A rectangle that represents the dirty region that the caller wants
+ * to redraw. This function may choose to expand the dirty rectangle if for example
+ * the surface has been resized or if the previous contents of the surface were
+ * not available. The caller must redraw the entire dirty region as represented
+ * by the contents of the inOutDirty rectangle upon return from this function.
+ * The caller may also pass <code>null</code> instead, in the case where the
+ * entire surface should be redrawn.
+ * @return A canvas for drawing into the surface.
+ */
@Override
- public Canvas lockCanvas(Rect dirty) {
+ public Canvas lockCanvas(Rect inOutDirty) {
+ return internalLockCanvas(inOutDirty, false);
+ }
+
+ @Override
+ public Canvas lockHardwareCanvas() {
+ return internalLockCanvas(null, true);
+ }
+
+ private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
+ mSurfaceLock.lock();
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
+ + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+
+ Canvas c = null;
+ if (!mDrawingStopped && mSurfaceControl != null) {
+ try {
+ if (hardware) {
+ c = mSurface.lockHardwareCanvas();
+ } else {
+ c = mSurface.lockCanvas(dirty);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception locking surface", e);
+ }
+ }
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
+ if (c != null) {
+ mLastLockTime = SystemClock.uptimeMillis();
+ return c;
+ }
+
+ // If the Surface is not ready to be drawn, then return null,
+ // but throttle calls to this function so it isn't called more
+ // than every 100ms.
+ long now = SystemClock.uptimeMillis();
+ long nextTime = mLastLockTime + 100;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ now = SystemClock.uptimeMillis();
+ }
+ mLastLockTime = now;
+ mSurfaceLock.unlock();
+
return null;
}
+ /**
+ * Posts the new contents of the {@link Canvas} to the surface and
+ * releases the {@link Canvas}.
+ *
+ * @param canvas The canvas previously obtained from {@link #lockCanvas}.
+ */
@Override
public void unlockCanvasAndPost(Canvas canvas) {
+ mSurface.unlockCanvasAndPost(canvas);
+ mSurfaceLock.unlock();
}
@Override
public Surface getSurface() {
- return null;
+ return mSurface;
}
@Override
public Rect getSurfaceFrame() {
- return null;
+ return mSurfaceFrame;
}
};
-}
+ class SurfaceControlWithBackground extends SurfaceControl {
+ private SurfaceControl mBackgroundControl;
+ private boolean mOpaque = true;
+ public boolean mVisible = false;
+
+ public SurfaceControlWithBackground(SurfaceSession s,
+ String name, int w, int h, int format, int flags)
+ throws Exception {
+ super(s, name, w, h, format, flags);
+ mBackgroundControl = new SurfaceControl(s, "Background for - " + name, w, h,
+ PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
+ mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ mBackgroundControl.setAlpha(alpha);
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ super.setLayer(zorder);
+ // -3 is below all other child layers as SurfaceView never goes below -2
+ mBackgroundControl.setLayer(-3);
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ super.setPosition(x, y);
+ mBackgroundControl.setPosition(x, y);
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ super.setSize(w, h);
+ mBackgroundControl.setSize(w, h);
+ }
+
+ @Override
+ public void setWindowCrop(Rect crop) {
+ super.setWindowCrop(crop);
+ mBackgroundControl.setWindowCrop(crop);
+ }
+
+ @Override
+ public void setFinalCrop(Rect crop) {
+ super.setFinalCrop(crop);
+ mBackgroundControl.setFinalCrop(crop);
+ }
+
+ @Override
+ public void setLayerStack(int layerStack) {
+ super.setLayerStack(layerStack);
+ mBackgroundControl.setLayerStack(layerStack);
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ super.setOpaque(isOpaque);
+ mOpaque = isOpaque;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void setSecure(boolean isSecure) {
+ super.setSecure(isSecure);
+ }
+
+ @Override
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+ mVisible = false;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ mVisible = true;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mBackgroundControl.destroy();
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ mBackgroundControl.release();
+ }
+
+ @Override
+ public void setTransparentRegionHint(Region region) {
+ super.setTransparentRegionHint(region);
+ mBackgroundControl.setTransparentRegionHint(region);
+ }
+
+ @Override
+ public void deferTransactionUntil(IBinder handle, long frame) {
+ super.deferTransactionUntil(handle, frame);
+ mBackgroundControl.deferTransactionUntil(handle, frame);
+ }
+
+ @Override
+ public void deferTransactionUntil(Surface barrier, long frame) {
+ super.deferTransactionUntil(barrier, frame);
+ mBackgroundControl.deferTransactionUntil(barrier, frame);
+ }
+
+ void updateBackgroundVisibility() {
+ if (mOpaque && mVisible) {
+ mBackgroundControl.show();
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+ }
+}
diff --git a/android/view/View.java b/android/view/View.java
index c043dcac..b6be2961 100644
--- a/android/view/View.java
+++ b/android/view/View.java
@@ -1448,59 +1448,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* <p>Enables low quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000;
/**
* <p>Enables high quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000;
/**
* <p>Enables automatic quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000;
private static final int[] DRAWING_CACHE_QUALITY_FLAGS = {
@@ -2342,9 +2300,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static final int PFLAG_HOVERED = 0x10000000;
/**
- * Flag set by {@link AutofillManager} if it needs to be notified when this view is clicked.
+ * no longer needed, should be reused
*/
- private static final int PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK = 0x20000000;
+ private static final int PFLAG_DOES_NOTHING_REUSE_PLEASE = 0x20000000;
/** {@hide} */
static final int PFLAG_ACTIVATED = 0x40000000;
@@ -6397,42 +6355,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
- /** @hide */
- public void setNotifyAutofillManagerOnClick(boolean notify) {
- if (notify) {
- mPrivateFlags |= PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- } else {
- mPrivateFlags &= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- }
- }
-
- private void notifyAutofillManagerOnClick() {
- if ((mPrivateFlags & PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK) != 0) {
- try {
- getAutofillManager().notifyViewClicked(this);
- } finally {
- // Set it to already called so it's not called twice when called by
- // performClickInternal()
- mPrivateFlags |= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- }
- }
- }
-
- /**
- * Entry point for {@link #performClick()} - other methods on View should call it instead of
- * {@code performClick()} directly to make sure the autofill manager is notified when
- * necessary (as subclasses could extend {@code performClick()} without calling the parent's
- * method).
- */
- private boolean performClickInternal() {
- // Must notify autofill manager before performing the click actions to avoid scenarios where
- // the app has a click listener that changes the state of views the autofill service might
- // be interested on.
- notifyAutofillManagerOnClick();
-
- return performClick();
- }
-
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
@@ -6441,14 +6363,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
- // NOTE: other methods on View should not call this method directly, but performClickInternal()
- // instead, to guarantee that the autofill manager is notified when necessary (as subclasses
- // could extend this method without calling super.performClick()).
public boolean performClick() {
- // We still need to call this method to handle the cases where performClick() was called
- // externally, instead of through performClickInternal()
- notifyAutofillManagerOnClick();
-
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
@@ -8992,21 +8907,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@DrawingCacheQuality
public int getDrawingCacheQuality() {
return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
@@ -9024,21 +8925,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheQuality(@DrawingCacheQuality int quality) {
setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
}
@@ -11546,7 +11433,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
- performClickInternal();
+ performClick();
return true;
}
} break;
@@ -12658,7 +12545,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// This is a tap, so remove the longpress check
removeLongPressCallback();
if (!event.isCanceled()) {
- return performClickInternal();
+ return performClick();
}
}
}
@@ -13230,7 +13117,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
- performClickInternal();
+ performClick();
}
}
}
@@ -18216,21 +18103,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getDrawingCache()
* @see #buildDrawingCache()
* @see #setLayerType(int, android.graphics.Paint)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheEnabled(boolean enabled) {
mCachingFailed = false;
setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
@@ -18243,21 +18116,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
@@ -18271,11 +18130,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@SuppressWarnings({"UnusedDeclaration"})
public void outputDirtyFlags(String indent, boolean clear, int clearMask) {
- Log.d(VIEW_LOG_TAG, indent + this + " DIRTY("
- + (mPrivateFlags & View.PFLAG_DIRTY_MASK)
- + ") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID("
- + (mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID)
- + ") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
+ Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.PFLAG_DIRTY_MASK) +
+ ") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID(" +
+ (mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID) +
+ ") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
if (clear) {
mPrivateFlags &= clearMask;
}
@@ -18399,21 +18257,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return A non-scaled bitmap representing this view or null if cache is disabled.
*
* @see #getDrawingCache(boolean)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public Bitmap getDrawingCache() {
return getDrawingCache(false);
}
@@ -18444,21 +18288,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
* @see #buildDrawingCache(boolean)
* @see #destroyDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public Bitmap getDrawingCache(boolean autoScale) {
if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
return null;
@@ -18478,21 +18308,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
@@ -18514,21 +18330,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheBackgroundColor(@ColorInt int color) {
if (color != mDrawingCacheBackgroundColor) {
mDrawingCacheBackgroundColor = color;
@@ -18540,21 +18342,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheBackgroundColor(int)
*
* @return The background color to used for the drawing cache's bitmap
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ColorInt
public int getDrawingCacheBackgroundColor() {
return mDrawingCacheBackgroundColor;
@@ -18564,21 +18352,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p>
*
* @see #buildDrawingCache(boolean)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void buildDrawingCache() {
buildDrawingCache(false);
}
@@ -18605,21 +18379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #getDrawingCache()
* @see #destroyDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
@@ -20052,7 +19812,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
boolean changed = false;
if (DBG) {
- Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
+ Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
@@ -25098,7 +24858,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private final class PerformClick implements Runnable {
@Override
public void run() {
- performClickInternal();
+ performClick();
}
}
diff --git a/android/view/ViewDebug.java b/android/view/ViewDebug.java
index afa94131..3426485e 100644
--- a/android/view/ViewDebug.java
+++ b/android/view/ViewDebug.java
@@ -528,23 +528,84 @@ public class ViewDebug {
/** @hide */
public static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
- RenderNode node = RenderNode.create("ViewDebug", null);
- profileViewAndChildren(view, node, out, true);
- node.destroy();
+ profileViewAndChildren(view, out, true);
}
- private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out,
- boolean root) throws IOException {
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
+
long durationMeasure =
(root || (view.mPrivateFlags & View.PFLAG_MEASURED_DIMENSION_SET) != 0)
- ? profileViewMeasure(view) : 0;
+ ? profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
+
+ private void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
+
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
+
+ public void post(Void... data) {
+ }
+ })
+ : 0;
long durationLayout =
(root || (view.mPrivateFlags & View.PFLAG_LAYOUT_REQUIRED) != 0)
- ? profileViewLayout(view) : 0;
+ ? profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
+
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
+
+ public void post(Void... data) {
+ }
+ }) : 0;
long durationDraw =
(root || !view.willNotDraw() || (view.mPrivateFlags & View.PFLAG_DRAWN) != 0)
- ? profileViewDraw(view, node) : 0;
+ ? profileViewOperation(view, new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics =
+ (view != null && view.getResources() != null) ?
+ view.getResources().getDisplayMetrics() : null;
+ final Bitmap bitmap = metrics != null ?
+ Bitmap.createBitmap(metrics, metrics.widthPixels,
+ metrics.heightPixels, Bitmap.Config.RGB_565) : null;
+ final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null;
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
+ public void run(Object... data) {
+ if (data[1] != null) {
+ view.draw((Canvas) data[1]);
+ }
+ }
+
+ public void post(Object... data) {
+ if (data[1] != null) {
+ ((Canvas) data[1]).setBitmap(null);
+ }
+ if (data[0] != null) {
+ ((Bitmap) data[0]).recycle();
+ }
+ }
+ }) : 0;
out.write(String.valueOf(durationMeasure));
out.write(' ');
out.write(String.valueOf(durationLayout));
@@ -555,86 +616,34 @@ public class ViewDebug {
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- profileViewAndChildren(group.getChildAt(i), node, out, false);
- }
- }
- }
-
- private static long profileViewMeasure(final View view) {
- return profileViewOperation(view, new ViewOperation() {
- @Override
- public void pre() {
- forceLayout(view);
- }
-
- private void forceLayout(View view) {
- view.forceLayout();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- final int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- forceLayout(group.getChildAt(i));
- }
- }
- }
-
- @Override
- public void run() {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
- });
- }
-
- private static long profileViewLayout(View view) {
- return profileViewOperation(view,
- () -> view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom));
- }
-
- private static long profileViewDraw(View view, RenderNode node) {
- DisplayMetrics dm = view.getResources().getDisplayMetrics();
- if (dm == null) {
- return 0;
- }
-
- if (view.isHardwareAccelerated()) {
- DisplayListCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
- try {
- return profileViewOperation(view, () -> view.draw(canvas));
- } finally {
- node.end(canvas);
- }
- } else {
- Bitmap bitmap = Bitmap.createBitmap(
- dm, dm.widthPixels, dm.heightPixels, Bitmap.Config.RGB_565);
- Canvas canvas = new Canvas(bitmap);
- try {
- return profileViewOperation(view, () -> view.draw(canvas));
- } finally {
- canvas.setBitmap(null);
- bitmap.recycle();
+ profileViewAndChildren(group.getChildAt(i), out, false);
}
}
}
- interface ViewOperation {
- default void pre() {}
-
- void run();
+ interface ViewOperation<T> {
+ T[] pre();
+ void run(T... data);
+ void post(T... data);
}
- private static long profileViewOperation(View view, final ViewOperation operation) {
+ private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
final CountDownLatch latch = new CountDownLatch(1);
final long[] duration = new long[1];
- view.post(() -> {
- try {
- operation.pre();
- long start = Debug.threadCpuTimeNanos();
- //noinspection unchecked
- operation.run();
- duration[0] = Debug.threadCpuTimeNanos() - start;
- } finally {
- latch.countDown();
+ view.post(new Runnable() {
+ public void run() {
+ try {
+ T[] data = operation.pre();
+ long start = Debug.threadCpuTimeNanos();
+ //noinspection unchecked
+ operation.run(data);
+ duration[0] = Debug.threadCpuTimeNanos() - start;
+ //noinspection unchecked
+ operation.post(data);
+ } finally {
+ latch.countDown();
+ }
}
});
diff --git a/android/view/ViewGroup.java b/android/view/ViewGroup.java
index 929beaea..b2e5a163 100644
--- a/android/view/ViewGroup.java
+++ b/android/view/ViewGroup.java
@@ -421,78 +421,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Used to indicate that no drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_NO_CACHE = 0x0;
/**
* Used to indicate that the animation drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
/**
* Used to indicate that the scrolling drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
/**
* Used to indicate that all drawing caches should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_ALL_CACHES = 0x3;
// Layout Modes
@@ -3825,21 +3769,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* Enables or disables the drawing cache for each child of this view group.
*
* @param enabled true to enable the cache, false to dispose of it
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
protected void setChildrenDrawingCacheEnabled(boolean enabled) {
if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
final View[] children = mChildren;
@@ -6401,21 +6331,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @return one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@@ -6436,21 +6352,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setPersistentDrawingCache(int drawingCacheToKeep) {
mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
}
diff --git a/android/view/ViewRootImpl.java b/android/view/ViewRootImpl.java
index 99438d87..71106ada 100644
--- a/android/view/ViewRootImpl.java
+++ b/android/view/ViewRootImpl.java
@@ -72,7 +72,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MergedConfiguration;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
@@ -1669,6 +1668,8 @@ public final class ViewRootImpl implements ViewParent,
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
+ //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
@@ -2826,7 +2827,7 @@ public final class ViewRootImpl implements ViewParent,
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
- Log.e(mTag, "Window redraw count down interrupted!");
+ Log.e(mTag, "Window redraw count down interruped!");
}
mWindowDrawCountDown = null;
}
@@ -2896,6 +2897,8 @@ public final class ViewRootImpl implements ViewParent,
final float appScale = mAttachInfo.mApplicationScale;
final boolean scalingRequired = mAttachInfo.mScalingRequired;
+ int resizeAlpha = 0;
+
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
@@ -3466,7 +3469,6 @@ public final class ViewRootImpl implements ViewParent,
}
void dispatchDetachedFromWindow() {
- mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
@@ -3729,273 +3731,266 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_INVALIDATE:
- ((View) msg.obj).invalidate();
- break;
- case MSG_INVALIDATE_RECT:
- final View.AttachInfo.InvalidateInfo info =
- (View.AttachInfo.InvalidateInfo) msg.obj;
- info.target.invalidate(info.left, info.top, info.right, info.bottom);
- info.recycle();
- break;
- case MSG_PROCESS_INPUT_EVENTS:
- mProcessInputEventsScheduled = false;
- doProcessInputEvents();
- break;
- case MSG_DISPATCH_APP_VISIBILITY:
- handleAppVisibility(msg.arg1 != 0);
- break;
- case MSG_DISPATCH_GET_NEW_SURFACE:
- handleGetNewSurface();
+ case MSG_INVALIDATE:
+ ((View) msg.obj).invalidate();
+ break;
+ case MSG_INVALIDATE_RECT:
+ final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
+ info.target.invalidate(info.left, info.top, info.right, info.bottom);
+ info.recycle();
+ break;
+ case MSG_PROCESS_INPUT_EVENTS:
+ mProcessInputEventsScheduled = false;
+ doProcessInputEvents();
+ break;
+ case MSG_DISPATCH_APP_VISIBILITY:
+ handleAppVisibility(msg.arg1 != 0);
+ break;
+ case MSG_DISPATCH_GET_NEW_SURFACE:
+ handleGetNewSurface();
+ break;
+ case MSG_RESIZED: {
+ // Recycled in the fall through...
+ SomeArgs args = (SomeArgs) msg.obj;
+ if (mWinFrame.equals(args.arg1)
+ && mPendingOverscanInsets.equals(args.arg5)
+ && mPendingContentInsets.equals(args.arg2)
+ && mPendingStableInsets.equals(args.arg6)
+ && mPendingVisibleInsets.equals(args.arg3)
+ && mPendingOutsets.equals(args.arg7)
+ && mPendingBackDropFrame.equals(args.arg8)
+ && args.arg4 == null
+ && args.argi1 == 0
+ && mDisplay.getDisplayId() == args.argi3) {
break;
- case MSG_RESIZED: {
- // Recycled in the fall through...
+ }
+ } // fall through...
+ case MSG_RESIZED_REPORT:
+ if (mAdded) {
SomeArgs args = (SomeArgs) msg.obj;
- if (mWinFrame.equals(args.arg1)
- && mPendingOverscanInsets.equals(args.arg5)
- && mPendingContentInsets.equals(args.arg2)
- && mPendingStableInsets.equals(args.arg6)
- && mPendingVisibleInsets.equals(args.arg3)
- && mPendingOutsets.equals(args.arg7)
- && mPendingBackDropFrame.equals(args.arg8)
- && args.arg4 == null
- && args.argi1 == 0
- && mDisplay.getDisplayId() == args.argi3) {
- break;
+
+ final int displayId = args.argi3;
+ MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
+ final boolean displayChanged = mDisplay.getDisplayId() != displayId;
+
+ if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
+ // If configuration changed - notify about that and, maybe, about move to
+ // display.
+ performConfigurationChange(mergedConfiguration, false /* force */,
+ displayChanged ? displayId : INVALID_DISPLAY /* same display */);
+ } else if (displayChanged) {
+ // Moved to display without config change - report last applied one.
+ onMovedToDisplay(displayId, mLastConfigurationFromResources);
}
- } // fall through...
- case MSG_RESIZED_REPORT:
- if (mAdded) {
- SomeArgs args = (SomeArgs) msg.obj;
-
- final int displayId = args.argi3;
- MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
- final boolean displayChanged = mDisplay.getDisplayId() != displayId;
-
- if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
- // If configuration changed - notify about that and, maybe,
- // about move to display.
- performConfigurationChange(mergedConfiguration, false /* force */,
- displayChanged
- ? displayId : INVALID_DISPLAY /* same display */);
- } else if (displayChanged) {
- // Moved to display without config change - report last applied one.
- onMovedToDisplay(displayId, mLastConfigurationFromResources);
- }
- final boolean framesChanged = !mWinFrame.equals(args.arg1)
- || !mPendingOverscanInsets.equals(args.arg5)
- || !mPendingContentInsets.equals(args.arg2)
- || !mPendingStableInsets.equals(args.arg6)
- || !mPendingVisibleInsets.equals(args.arg3)
- || !mPendingOutsets.equals(args.arg7);
-
- mWinFrame.set((Rect) args.arg1);
- mPendingOverscanInsets.set((Rect) args.arg5);
- mPendingContentInsets.set((Rect) args.arg2);
- mPendingStableInsets.set((Rect) args.arg6);
- mPendingVisibleInsets.set((Rect) args.arg3);
- mPendingOutsets.set((Rect) args.arg7);
- mPendingBackDropFrame.set((Rect) args.arg8);
- mForceNextWindowRelayout = args.argi1 != 0;
- mPendingAlwaysConsumeNavBar = args.argi2 != 0;
-
- args.recycle();
-
- if (msg.what == MSG_RESIZED_REPORT) {
- reportNextDraw();
- }
+ final boolean framesChanged = !mWinFrame.equals(args.arg1)
+ || !mPendingOverscanInsets.equals(args.arg5)
+ || !mPendingContentInsets.equals(args.arg2)
+ || !mPendingStableInsets.equals(args.arg6)
+ || !mPendingVisibleInsets.equals(args.arg3)
+ || !mPendingOutsets.equals(args.arg7);
+
+ mWinFrame.set((Rect) args.arg1);
+ mPendingOverscanInsets.set((Rect) args.arg5);
+ mPendingContentInsets.set((Rect) args.arg2);
+ mPendingStableInsets.set((Rect) args.arg6);
+ mPendingVisibleInsets.set((Rect) args.arg3);
+ mPendingOutsets.set((Rect) args.arg7);
+ mPendingBackDropFrame.set((Rect) args.arg8);
+ mForceNextWindowRelayout = args.argi1 != 0;
+ mPendingAlwaysConsumeNavBar = args.argi2 != 0;
- if (mView != null && framesChanged) {
- forceLayout(mView);
- }
- requestLayout();
+ args.recycle();
+
+ if (msg.what == MSG_RESIZED_REPORT) {
+ reportNextDraw();
}
- break;
- case MSG_WINDOW_MOVED:
- if (mAdded) {
- final int w = mWinFrame.width();
- final int h = mWinFrame.height();
- final int l = msg.arg1;
- final int t = msg.arg2;
- mWinFrame.left = l;
- mWinFrame.right = l + w;
- mWinFrame.top = t;
- mWinFrame.bottom = t + h;
-
- mPendingBackDropFrame.set(mWinFrame);
- maybeHandleWindowMove(mWinFrame);
+
+ if (mView != null && framesChanged) {
+ forceLayout(mView);
}
- break;
- case MSG_WINDOW_FOCUS_CHANGED: {
- final boolean hasWindowFocus = msg.arg1 != 0;
- if (mAdded) {
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
-
- profileRendering(hasWindowFocus);
-
- if (hasWindowFocus) {
- boolean inTouchMode = msg.arg2 != 0;
- ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
- mFullRedrawNeeded = true;
+ requestLayout();
+ }
+ break;
+ case MSG_WINDOW_MOVED:
+ if (mAdded) {
+ final int w = mWinFrame.width();
+ final int h = mWinFrame.height();
+ final int l = msg.arg1;
+ final int t = msg.arg2;
+ mWinFrame.left = l;
+ mWinFrame.right = l + w;
+ mWinFrame.top = t;
+ mWinFrame.bottom = t + h;
+
+ mPendingBackDropFrame.set(mWinFrame);
+ maybeHandleWindowMove(mWinFrame);
+ }
+ break;
+ case MSG_WINDOW_FOCUS_CHANGED: {
+ if (mAdded) {
+ boolean hasWindowFocus = msg.arg1 != 0;
+ mAttachInfo.mHasWindowFocus = hasWindowFocus;
+
+ profileRendering(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ boolean inTouchMode = msg.arg2 != 0;
+ ensureTouchModeLocally(inTouchMode);
+
+ if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()){
+ mFullRedrawNeeded = true;
+ try {
+ final WindowManager.LayoutParams lp = mWindowAttributes;
+ final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+ mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+ mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+ } catch (OutOfResourcesException e) {
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
try {
- final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
- mAttachInfo.mThreadedRenderer.initializeIfNeeded(
- mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
- } catch (OutOfResourcesException e) {
- Log.e(mTag, "OutOfResourcesException locking surface", e);
- try {
- if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(mTag, "No processes killed for memory;"
- + " killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
+ if (!mWindowSession.outOfMemory(mWindow)) {
+ Slog.w(mTag, "No processes killed for memory; killing self");
+ Process.killProcess(Process.myPid());
}
- // Retry in a bit.
- sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2),
- 500);
- return;
+ } catch (RemoteException ex) {
}
+ // Retry in a bit.
+ sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
+ return;
}
}
+ }
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
+ mLastWasImTarget = WindowManager.LayoutParams
+ .mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- }
- if (mView != null) {
- mAttachInfo.mKeyDispatchState.reset();
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
+ if (mView != null) {
+ mAttachInfo.mKeyDispatchState.reset();
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
- if (mAttachInfo.mTooltipHost != null) {
- mAttachInfo.mTooltipHost.hideTooltip();
- }
+ if (mAttachInfo.mTooltipHost != null) {
+ mAttachInfo.mTooltipHost.hideTooltip();
}
+ }
- // Note: must be done after the focus change callbacks,
- // so all of the view state is set up correctly.
- if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
- }
- // Clear the forward bit. We can just do this directly, since
- // the window manager doesn't care about it.
- mWindowAttributes.softInputMode &=
+ // Note: must be done after the focus change callbacks,
+ // so all of the view state is set up correctly.
+ if (hasWindowFocus) {
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPostWindowFocus(mView, mView.findFocus(),
+ mWindowAttributes.softInputMode,
+ !mHasHadWindowFocus, mWindowAttributes.flags);
+ }
+ // Clear the forward bit. We can just do this directly, since
+ // the window manager doesn't care about it.
+ mWindowAttributes.softInputMode &=
+ ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ ((WindowManager.LayoutParams)mView.getLayoutParams())
+ .softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- ((WindowManager.LayoutParams) mView.getLayoutParams())
- .softInputMode &=
- ~WindowManager.LayoutParams
- .SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
- } else {
- if (mPointerCapture) {
- handlePointerCaptureChanged(false);
- }
+ mHasHadWindowFocus = true;
+ } else {
+ if (mPointerCapture) {
+ handlePointerCaptureChanged(false);
}
}
- mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
- } break;
- case MSG_DIE:
- doDie();
- break;
- case MSG_DISPATCH_INPUT_EVENT: {
- SomeArgs args = (SomeArgs) msg.obj;
- InputEvent event = (InputEvent) args.arg1;
- InputEventReceiver receiver = (InputEventReceiver) args.arg2;
- enqueueInputEvent(event, receiver, 0, true);
- args.recycle();
- } break;
- case MSG_SYNTHESIZE_INPUT_EVENT: {
- InputEvent event = (InputEvent) msg.obj;
- enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
- } break;
- case MSG_DISPATCH_KEY_FROM_IME: {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Dispatching key " + msg.obj + " from IME to " + mView);
- }
- KeyEvent event = (KeyEvent) msg.obj;
- if ((event.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) != 0) {
- // The IME is trying to say this event is from the
- // system! Bad bad bad!
- //noinspection UnusedAssignment
- event = KeyEvent.changeFlags(event,
- event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
- }
- enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
- } break;
- case MSG_CHECK_FOCUS: {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- imm.checkFocus();
- }
- } break;
- case MSG_CLOSE_SYSTEM_DIALOGS: {
- if (mView != null) {
- mView.onCloseSystemDialogs((String) msg.obj);
- }
- } break;
- case MSG_DISPATCH_DRAG_EVENT: {
- } // fall through
- case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
- DragEvent event = (DragEvent) msg.obj;
- // only present when this app called startDrag()
- event.mLocalState = mLocalDragState;
- handleDragEvent(event);
- } break;
- case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
- handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
- } break;
- case MSG_UPDATE_CONFIGURATION: {
- Configuration config = (Configuration) msg.obj;
- if (config.isOtherSeqNewer(
- mLastReportedMergedConfiguration.getMergedConfiguration())) {
- // If we already have a newer merged config applied - use its global part.
- config = mLastReportedMergedConfiguration.getGlobalConfiguration();
- }
+ }
+ } break;
+ case MSG_DIE:
+ doDie();
+ break;
+ case MSG_DISPATCH_INPUT_EVENT: {
+ SomeArgs args = (SomeArgs)msg.obj;
+ InputEvent event = (InputEvent)args.arg1;
+ InputEventReceiver receiver = (InputEventReceiver)args.arg2;
+ enqueueInputEvent(event, receiver, 0, true);
+ args.recycle();
+ } break;
+ case MSG_SYNTHESIZE_INPUT_EVENT: {
+ InputEvent event = (InputEvent)msg.obj;
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
+ } break;
+ case MSG_DISPATCH_KEY_FROM_IME: {
+ if (LOCAL_LOGV) Log.v(
+ TAG, "Dispatching key "
+ + msg.obj + " from IME to " + mView);
+ KeyEvent event = (KeyEvent)msg.obj;
+ if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
+ // The IME is trying to say this event is from the
+ // system! Bad bad bad!
+ //noinspection UnusedAssignment
+ event = KeyEvent.changeFlags(event, event.getFlags() &
+ ~KeyEvent.FLAG_FROM_SYSTEM);
+ }
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
+ } break;
+ case MSG_CHECK_FOCUS: {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.checkFocus();
+ }
+ } break;
+ case MSG_CLOSE_SYSTEM_DIALOGS: {
+ if (mView != null) {
+ mView.onCloseSystemDialogs((String)msg.obj);
+ }
+ } break;
+ case MSG_DISPATCH_DRAG_EVENT:
+ case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
+ DragEvent event = (DragEvent)msg.obj;
+ event.mLocalState = mLocalDragState; // only present when this app called startDrag()
+ handleDragEvent(event);
+ } break;
+ case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
+ handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
+ } break;
+ case MSG_UPDATE_CONFIGURATION: {
+ Configuration config = (Configuration) msg.obj;
+ if (config.isOtherSeqNewer(
+ mLastReportedMergedConfiguration.getMergedConfiguration())) {
+ // If we already have a newer merged config applied - use its global part.
+ config = mLastReportedMergedConfiguration.getGlobalConfiguration();
+ }
- // Use the newer global config and last reported override config.
- mPendingMergedConfiguration.setConfiguration(config,
- mLastReportedMergedConfiguration.getOverrideConfiguration());
+ // Use the newer global config and last reported override config.
+ mPendingMergedConfiguration.setConfiguration(config,
+ mLastReportedMergedConfiguration.getOverrideConfiguration());
- performConfigurationChange(mPendingMergedConfiguration, false /* force */,
- INVALID_DISPLAY /* same display */);
- } break;
- case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
- setAccessibilityFocus(null, null);
- } break;
- case MSG_INVALIDATE_WORLD: {
- if (mView != null) {
- invalidateWorld(mView);
- }
- } break;
- case MSG_DISPATCH_WINDOW_SHOWN: {
- handleDispatchWindowShown();
- } break;
- case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
- final IResultReceiver receiver = (IResultReceiver) msg.obj;
- final int deviceId = msg.arg1;
- handleRequestKeyboardShortcuts(receiver, deviceId);
- } break;
- case MSG_UPDATE_POINTER_ICON: {
- MotionEvent event = (MotionEvent) msg.obj;
- resetPointerIcon(event);
- } break;
- case MSG_POINTER_CAPTURE_CHANGED: {
- final boolean hasCapture = msg.arg1 != 0;
- handlePointerCaptureChanged(hasCapture);
- } break;
- case MSG_DRAW_FINISHED: {
- pendingDrawFinished();
- } break;
+ performConfigurationChange(mPendingMergedConfiguration, false /* force */,
+ INVALID_DISPLAY /* same display */);
+ } break;
+ case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
+ setAccessibilityFocus(null, null);
+ } break;
+ case MSG_INVALIDATE_WORLD: {
+ if (mView != null) {
+ invalidateWorld(mView);
+ }
+ } break;
+ case MSG_DISPATCH_WINDOW_SHOWN: {
+ handleDispatchWindowShown();
+ } break;
+ case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
+ final IResultReceiver receiver = (IResultReceiver) msg.obj;
+ final int deviceId = msg.arg1;
+ handleRequestKeyboardShortcuts(receiver, deviceId);
+ } break;
+ case MSG_UPDATE_POINTER_ICON: {
+ MotionEvent event = (MotionEvent) msg.obj;
+ resetPointerIcon(event);
+ } break;
+ case MSG_POINTER_CAPTURE_CHANGED: {
+ final boolean hasCapture = msg.arg1 != 0;
+ handlePointerCaptureChanged(hasCapture);
+ } break;
+ case MSG_DRAW_FINISHED: {
+ pendingDrawFinished();
+ } break;
}
}
}
@@ -4208,18 +4203,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- protected void onWindowFocusChanged(boolean hasWindowFocus) {
- if (mNext != null) {
- mNext.onWindowFocusChanged(hasWindowFocus);
- }
- }
-
- protected void onDetachedFromWindow() {
- if (mNext != null) {
- mNext.onDetachedFromWindow();
- }
- }
-
protected boolean shouldDropInputEvent(QueuedInputEvent q) {
if (mView == null || !mAdded) {
Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
@@ -4973,9 +4956,9 @@ public final class ViewRootImpl implements ViewParent,
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- mTrackball.cancel();
+ mTrackball.cancel(event);
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- mJoystick.cancel();
+ mJoystick.cancel(event);
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.cancel(event);
@@ -4984,18 +4967,6 @@ public final class ViewRootImpl implements ViewParent,
}
super.onDeliverToNext(q);
}
-
- @Override
- protected void onWindowFocusChanged(boolean hasWindowFocus) {
- if (!hasWindowFocus) {
- mJoystick.cancel();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mJoystick.cancel();
- }
}
/**
@@ -5108,7 +5079,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- public void cancel() {
+ public void cancel(MotionEvent event) {
mLastTime = Integer.MIN_VALUE;
// If we reach this, we consumed a trackball event.
@@ -5292,11 +5263,14 @@ public final class ViewRootImpl implements ViewParent,
* Creates dpad events from unhandled joystick movements.
*/
final class SyntheticJoystickHandler extends Handler {
+ private final static String TAG = "SyntheticJoystickHandler";
private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
- private final JoystickAxesState mJoystickAxesState = new JoystickAxesState();
- private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>();
+ private int mLastXDirection;
+ private int mLastYDirection;
+ private int mLastXKeyCode;
+ private int mLastYKeyCode;
public SyntheticJoystickHandler() {
super(true);
@@ -5307,10 +5281,11 @@ public final class ViewRootImpl implements ViewParent,
switch (msg.what) {
case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+ KeyEvent oldEvent = (KeyEvent)msg.obj;
+ KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
+ SystemClock.uptimeMillis(),
+ oldEvent.getRepeatCount() + 1);
if (mAttachInfo.mHasWindowFocus) {
- KeyEvent oldEvent = (KeyEvent) msg.obj;
- KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
- SystemClock.uptimeMillis(), oldEvent.getRepeatCount() + 1);
enqueueInputEvent(e);
Message m = obtainMessage(msg.what, e);
m.setAsynchronous(true);
@@ -5322,176 +5297,97 @@ public final class ViewRootImpl implements ViewParent,
public void process(MotionEvent event) {
switch(event.getActionMasked()) {
- case MotionEvent.ACTION_CANCEL:
- cancel();
- break;
- case MotionEvent.ACTION_MOVE:
- update(event);
- break;
- default:
- Log.w(mTag, "Unexpected action: " + event.getActionMasked());
+ case MotionEvent.ACTION_CANCEL:
+ cancel(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ update(event, true);
+ break;
+ default:
+ Log.w(mTag, "Unexpected action: " + event.getActionMasked());
}
}
- private void cancel() {
+ private void cancel(MotionEvent event) {
removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
- for (int i = 0; i < mDeviceKeyEvents.size(); i++) {
- final KeyEvent keyEvent = mDeviceKeyEvents.valueAt(i);
- if (keyEvent != null) {
- enqueueInputEvent(KeyEvent.changeTimeRepeat(keyEvent,
- SystemClock.uptimeMillis(), 0));
- }
- }
- mDeviceKeyEvents.clear();
- mJoystickAxesState.resetState();
- }
-
- private void update(MotionEvent event) {
- final int historySize = event.getHistorySize();
- for (int h = 0; h < historySize; h++) {
- final long time = event.getHistoricalEventTime(h);
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
- event.getHistoricalAxisValue(MotionEvent.AXIS_X, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
- event.getHistoricalAxisValue(MotionEvent.AXIS_Y, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
- event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_X, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
- event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_Y, 0, h));
- }
- final long time = event.getEventTime();
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
- event.getAxisValue(MotionEvent.AXIS_X));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
- event.getAxisValue(MotionEvent.AXIS_Y));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
- event.getAxisValue(MotionEvent.AXIS_HAT_X));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
- event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ update(event, false);
}
- final class JoystickAxesState {
- // State machine: from neutral state (no button press) can go into
- // button STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state, emitting an ACTION_DOWN event.
- // From STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state can go into neutral state,
- // emitting an ACTION_UP event.
- private static final int STATE_UP_OR_LEFT = -1;
- private static final int STATE_NEUTRAL = 0;
- private static final int STATE_DOWN_OR_RIGHT = 1;
-
- final int[] mAxisStatesHat = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_HAT_X, AXIS_HAT_Y}
- final int[] mAxisStatesStick = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_X, AXIS_Y}
-
- void resetState() {
- mAxisStatesHat[0] = STATE_NEUTRAL;
- mAxisStatesHat[1] = STATE_NEUTRAL;
- mAxisStatesStick[0] = STATE_NEUTRAL;
- mAxisStatesStick[1] = STATE_NEUTRAL;
- }
-
- void updateStateForAxis(MotionEvent event, long time, int axis, float value) {
- // Emit KeyEvent if necessary
- // axis can be AXIS_X, AXIS_Y, AXIS_HAT_X, AXIS_HAT_Y
- final int axisStateIndex;
- final int repeatMessage;
- if (isXAxis(axis)) {
- axisStateIndex = 0;
- repeatMessage = MSG_ENQUEUE_X_AXIS_KEY_REPEAT;
- } else if (isYAxis(axis)) {
- axisStateIndex = 1;
- repeatMessage = MSG_ENQUEUE_Y_AXIS_KEY_REPEAT;
- } else {
- Log.e(mTag, "Unexpected axis " + axis + " in updateStateForAxis!");
- return;
- }
- final int newState = joystickAxisValueToState(value);
+ private void update(MotionEvent event, boolean synthesizeNewKeys) {
+ final long time = event.getEventTime();
+ final int metaState = event.getMetaState();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
- final int currentState;
- if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
- currentState = mAxisStatesStick[axisStateIndex];
- } else {
- currentState = mAxisStatesHat[axisStateIndex];
- }
+ int xDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ if (xDirection == 0) {
+ xDirection = joystickAxisValueToDirection(event.getX());
+ }
- if (currentState == newState) {
- return;
+ int yDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (yDirection == 0) {
+ yDirection = joystickAxisValueToDirection(event.getY());
+ }
+
+ if (xDirection != mLastXDirection) {
+ if (mLastXKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastXKeyCode = 0;
}
- final int metaState = event.getMetaState();
- final int deviceId = event.getDeviceId();
- final int source = event.getSource();
+ mLastXDirection = xDirection;
- if (currentState == STATE_DOWN_OR_RIGHT || currentState == STATE_UP_OR_LEFT) {
- // send a button release event
- final int keyCode = joystickAxisAndStateToKeycode(axis, currentState);
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- enqueueInputEvent(new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
- 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- // remove the corresponding pending UP event if focus lost/view detached
- mDeviceKeyEvents.put(deviceId, null);
- }
- removeMessages(repeatMessage);
+ if (xDirection != 0 && synthesizeNewKeys) {
+ mLastXKeyCode = xDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
+ }
- if (newState == STATE_DOWN_OR_RIGHT || newState == STATE_UP_OR_LEFT) {
- // send a button down event
- final int keyCode = joystickAxisAndStateToKeycode(axis, newState);
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- KeyEvent keyEvent = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
- 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(keyEvent);
- Message m = obtainMessage(repeatMessage, keyEvent);
- m.setAsynchronous(true);
- sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
- // store the corresponding ACTION_UP event so that it can be sent
- // if focus is lost or root view is removed
- mDeviceKeyEvents.put(deviceId,
- new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
- 0, metaState, deviceId, 0,
- KeyEvent.FLAG_FALLBACK | KeyEvent.FLAG_CANCELED,
- source));
- }
- }
- if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
- mAxisStatesStick[axisStateIndex] = newState;
- } else {
- mAxisStatesHat[axisStateIndex] = newState;
+ if (yDirection != mLastYDirection) {
+ if (mLastYKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastYKeyCode = 0;
}
- }
- private boolean isXAxis(int axis) {
- return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_HAT_X;
- }
- private boolean isYAxis(int axis) {
- return axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_HAT_Y;
- }
+ mLastYDirection = yDirection;
- private int joystickAxisAndStateToKeycode(int axis, int state) {
- if (isXAxis(axis) && state == STATE_UP_OR_LEFT) {
- return KeyEvent.KEYCODE_DPAD_LEFT;
- }
- if (isXAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
- return KeyEvent.KEYCODE_DPAD_RIGHT;
- }
- if (isYAxis(axis) && state == STATE_UP_OR_LEFT) {
- return KeyEvent.KEYCODE_DPAD_UP;
+ if (yDirection != 0 && synthesizeNewKeys) {
+ mLastYKeyCode = yDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
- if (isYAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
- return KeyEvent.KEYCODE_DPAD_DOWN;
- }
- Log.e(mTag, "Unknown axis " + axis + " or direction " + state);
- return KeyEvent.KEYCODE_UNKNOWN; // should never happen
}
+ }
- private int joystickAxisValueToState(float value) {
- if (value >= 0.5f) {
- return STATE_DOWN_OR_RIGHT;
- } else if (value <= -0.5f) {
- return STATE_UP_OR_LEFT;
- } else {
- return STATE_NEUTRAL;
- }
+ private int joystickAxisValueToDirection(float value) {
+ if (value >= 0.5f) {
+ return 1;
+ } else if (value <= -0.5f) {
+ return -1;
+ } else {
+ return 0;
}
}
}
@@ -6212,6 +6108,7 @@ public final class ViewRootImpl implements ViewParent,
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
}
+ //Log.d(mTag, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
@@ -6232,6 +6129,7 @@ public final class ViewRootImpl implements ViewParent,
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
+ //Log.d(mTag, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
}
diff --git a/android/view/ViewStructure.java b/android/view/ViewStructure.java
index 309366c6..f671c349 100644
--- a/android/view/ViewStructure.java
+++ b/android/view/ViewStructure.java
@@ -365,30 +365,6 @@ public abstract class ViewStructure {
public abstract void setDataIsSensitive(boolean sensitive);
/**
- * Sets the minimum width in ems of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMinTextEms(int minEms);
-
- /**
- * Sets the maximum width in ems of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMaxTextEms(int maxEms);
-
- /**
- * Sets the maximum length of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMaxTextLength(int maxLength);
-
- /**
* Call when done populating a {@link ViewStructure} returned by
* {@link #asyncNewChild}.
*/
diff --git a/android/view/WindowManagerInternal.java b/android/view/WindowManagerInternal.java
index 69cc1002..98f8dc8e 100644
--- a/android/view/WindowManagerInternal.java
+++ b/android/view/WindowManagerInternal.java
@@ -335,8 +335,8 @@ public abstract class WindowManagerInternal {
public abstract void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener);
- /** Returns true if a stack in the windowing mode is currently visible. */
- public abstract boolean isStackVisible(int windowingMode);
+ /** Returns true if the stack with the input Id is currently visible. */
+ public abstract boolean isStackVisible(int stackId);
/**
* @return True if and only if the docked divider is currently in resize mode.
diff --git a/android/view/WindowManagerPolicy.java b/android/view/WindowManagerPolicy.java
index 137e551d..da72535d 100644
--- a/android/view/WindowManagerPolicy.java
+++ b/android/view/WindowManagerPolicy.java
@@ -467,8 +467,11 @@ public interface WindowManagerPolicy {
*/
public boolean isDimming();
- /** @return the current windowing mode of this window. */
- int getWindowingMode();
+ /**
+ * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+ * not attached to any stack.
+ */
+ int getStackId();
/**
* Returns true if the window is current in multi-windowing mode. i.e. it shares the
diff --git a/android/view/accessibility/AccessibilityCache.java b/android/view/accessibility/AccessibilityCache.java
index d7851171..0f21c5c8 100644
--- a/android/view/accessibility/AccessibilityCache.java
+++ b/android/view/accessibility/AccessibilityCache.java
@@ -329,6 +329,8 @@ public final class AccessibilityCache {
final long oldParentId = oldInfo.getParentNodeId();
if (info.getParentNodeId() != oldParentId) {
clearSubTreeLocked(windowId, oldParentId);
+ } else {
+ oldInfo.recycle();
}
}
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index 11cb046a..0b9bc576 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -16,46 +16,152 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
+import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemService;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
import android.view.IWindow;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IntPair;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
- * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
- * Such events are generated when something notable happens in the user interface,
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
+ * and provides facilities for querying the accessibility state of the system.
+ * Accessibility events are generated when something notable happens in the user interface,
* for example an {@link android.app.Activity} starts, the focus or selection of a
* {@link android.view.View} changes etc. Parties interested in handling accessibility
* events implement and register an accessibility service which extends
- * {@code android.accessibilityservice.AccessibilityService}.
+ * {@link android.accessibilityservice.AccessibilityService}.
*
* @see AccessibilityEvent
- * @see android.content.Context#getSystemService
+ * @see AccessibilityNodeInfo
+ * @see android.accessibilityservice.AccessibilityService
+ * @see Context#getSystemService
+ * @see Context#ACCESSIBILITY_SERVICE
*/
-@SuppressWarnings("UnusedDeclaration")
+@SystemService(Context.ACCESSIBILITY_SERVICE)
public final class AccessibilityManager {
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = "AccessibilityManager";
+
+ /** @hide */
+ public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;
+
+ /** @hide */
+ public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;
+
+ /** @hide */
+ public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004;
+
+ /** @hide */
+ public static final int DALTONIZER_DISABLED = -1;
+
+ /** @hide */
+ public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;
+
+ /** @hide */
+ public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
+
+ /** @hide */
+ public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+
+ /**
+ * Activity action: Launch UI to manage which accessibility service or feature is assigned
+ * to the navigation bar Accessibility button.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
+ "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
+
+ static final Object sInstanceSync = new Object();
+
+ private static AccessibilityManager sInstance;
+
+ private final Object mLock = new Object();
+
+ private IAccessibilityManager mService;
+
+ final int mUserId;
+
+ final Handler mHandler;
+
+ final Handler.Callback mCallback;
+
+ boolean mIsEnabled;
- private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
+ int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ boolean mIsTouchExplorationEnabled;
+
+ boolean mIsHighTextContrastEnabled;
+
+ private final ArrayMap<AccessibilityStateChangeListener, Handler>
+ mAccessibilityStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<TouchExplorationStateChangeListener, Handler>
+ mTouchExplorationStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<HighTextContrastChangeListener, Handler>
+ mHighTextContrastStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<AccessibilityServicesStateChangeListener, Handler>
+ mServicesStateChangeListeners = new ArrayMap<>();
+
+ /**
+ * Map from a view's accessibility id to the list of request preparers set for that view
+ */
+ private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists;
/**
- * Listener for the accessibility state.
+ * Listener for the system accessibility state. To listen for changes to the
+ * accessibility state on the device, implement this interface and register
+ * it with the system by calling {@link #addAccessibilityStateChangeListener}.
*/
public interface AccessibilityStateChangeListener {
/**
- * Called back on change in the accessibility state.
+ * Called when the accessibility enabled state changes.
*
* @param enabled Whether accessibility is enabled.
*/
- public void onAccessibilityStateChanged(boolean enabled);
+ void onAccessibilityStateChanged(boolean enabled);
}
/**
@@ -71,7 +177,24 @@ public final class AccessibilityManager {
*
* @param enabled Whether touch exploration is enabled.
*/
- public void onTouchExplorationStateChanged(boolean enabled);
+ void onTouchExplorationStateChanged(boolean enabled);
+ }
+
+ /**
+ * Listener for changes to the state of accessibility services. Changes include services being
+ * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service.
+ * {@see #addAccessibilityServicesStateChangeListener}.
+ *
+ * @hide
+ */
+ public interface AccessibilityServicesStateChangeListener {
+
+ /**
+ * Called when the state of accessibility services changes.
+ *
+ * @param manager The manager that is calling back
+ */
+ void onAccessibilityServicesStateChanged(AccessibilityManager manager);
}
/**
@@ -79,6 +202,8 @@ public final class AccessibilityManager {
* the high text contrast state on the device, implement this interface and
* register it with the system by calling
* {@link #addHighTextContrastStateChangeListener}.
+ *
+ * @hide
*/
public interface HighTextContrastChangeListener {
@@ -87,26 +212,72 @@ public final class AccessibilityManager {
*
* @param enabled Whether high text contrast is enabled.
*/
- public void onHighTextContrastStateChanged(boolean enabled);
+ void onHighTextContrastStateChanged(boolean enabled);
}
private final IAccessibilityManagerClient.Stub mClient =
new IAccessibilityManagerClient.Stub() {
- public void setState(int state) {
- }
+ @Override
+ public void setState(int state) {
+ // We do not want to change this immediately as the application may
+ // have already checked that accessibility is on and fired an event,
+ // that is now propagating up the view tree, Hence, if accessibility
+ // is now off an exception will be thrown. We want to have the exception
+ // enforcement to guard against apps that fire unnecessary accessibility
+ // events when accessibility is off.
+ mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
+ }
- public void notifyServicesStateChanged() {
+ @Override
+ public void notifyServicesStateChanged() {
+ final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mServicesStateChangeListeners.isEmpty()) {
+ return;
}
+ listeners = new ArrayMap<>(mServicesStateChangeListeners);
+ }
- public void setRelevantEventTypes(int eventTypes) {
- }
- };
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final AccessibilityServicesStateChangeListener listener =
+ mServicesStateChangeListeners.keyAt(i);
+ mServicesStateChangeListeners.valueAt(i).post(() -> listener
+ .onAccessibilityServicesStateChanged(AccessibilityManager.this));
+ }
+ }
+
+ @Override
+ public void setRelevantEventTypes(int eventTypes) {
+ mRelevantEventTypes = eventTypes;
+ }
+ };
/**
* Get an AccessibilityManager instance (create one if necessary).
*
+ * @param context Context in which this manager operates.
+ *
+ * @hide
*/
public static AccessibilityManager getInstance(Context context) {
+ synchronized (sInstanceSync) {
+ if (sInstance == null) {
+ final int userId;
+ if (Binder.getCallingUid() == Process.SYSTEM_UID
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ userId = UserHandle.myUserId();
+ }
+ sInstance = new AccessibilityManager(context, null, userId);
+ }
+ }
return sInstance;
}
@@ -114,21 +285,68 @@ public final class AccessibilityManager {
* Create an instance.
*
* @param context A {@link Context}.
+ * @param service An interface to the backing service.
+ * @param userId User id under which to run.
+ *
+ * @hide
*/
public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+ // Constructor can't be chained because we can't create an instance of an inner class
+ // before calling another constructor.
+ mCallback = new MyCallback();
+ mHandler = new Handler(context.getMainLooper(), mCallback);
+ mUserId = userId;
+ synchronized (mLock) {
+ tryConnectToServiceLocked(service);
+ }
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param handler The handler to use
+ * @param service An interface to the backing service.
+ * @param userId User id under which to run.
+ *
+ * @hide
+ */
+ public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+ mCallback = new MyCallback();
+ mHandler = handler;
+ mUserId = userId;
+ synchronized (mLock) {
+ tryConnectToServiceLocked(service);
+ }
}
+ /**
+ * @hide
+ */
public IAccessibilityManagerClient getClient() {
return mClient;
}
/**
- * Returns if the {@link AccessibilityManager} is enabled.
+ * @hide
+ */
+ @VisibleForTesting
+ public Handler.Callback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Returns if the accessibility in the system is enabled.
*
- * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
+ * @return True if accessibility is enabled, false otherwise.
*/
public boolean isEnabled() {
- return false;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsEnabled;
+ }
}
/**
@@ -137,7 +355,13 @@ public final class AccessibilityManager {
* @return True if touch exploration is enabled, false otherwise.
*/
public boolean isTouchExplorationEnabled() {
- return true;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsTouchExplorationEnabled;
+ }
}
/**
@@ -147,35 +371,169 @@ public final class AccessibilityManager {
* doing its own rendering and does not rely on the platform rendering pipeline.
* </p>
*
+ * @return True if high text contrast is enabled, false otherwise.
+ *
+ * @hide
*/
public boolean isHighTextContrastEnabled() {
- return false;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsHighTextContrastEnabled;
+ }
}
/**
* Sends an {@link AccessibilityEvent}.
+ *
+ * @param event The event to send.
+ *
+ * @throws IllegalStateException if accessibility is not enabled.
+ *
+ * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
+ * events is through calling
+ * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+ * instead of this method to allow predecessors to augment/filter events sent by
+ * their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ return;
+ }
+ }
+ if ((event.getEventType() & mRelevantEventTypes) == 0) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+ + " that is not among "
+ + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
+ }
+ return;
+ }
+ userId = mUserId;
+ }
+ try {
+ event.setEventTime(SystemClock.uptimeMillis());
+ // it is possible that this manager is in the same process as the service but
+ // client using it is called through Binder from another process. Example: MMS
+ // app adds a SMS notification and the NotificationManagerService calls this method
+ long identityToken = Binder.clearCallingIdentity();
+ service.sendAccessibilityEvent(event, userId);
+ Binder.restoreCallingIdentity(identityToken);
+ if (DEBUG) {
+ Log.i(LOG_TAG, event + " sent");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ } finally {
+ event.recycle();
+ }
}
/**
- * Requests interruption of the accessibility feedback from all accessibility services.
+ * Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+ return;
+ }
+ }
+ userId = mUserId;
+ }
+ try {
+ service.interrupt(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Requested interrupt from all services");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
+ }
}
/**
* Returns the {@link ServiceInfo}s of the installed accessibility services.
*
* @return An unmodifiable list with {@link ServiceInfo}s.
+ *
+ * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
*/
@Deprecated
public List<ServiceInfo> getAccessibilityServiceList() {
- return Collections.emptyList();
+ List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
+ List<ServiceInfo> services = new ArrayList<>();
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i++) {
+ AccessibilityServiceInfo info = infos.get(i);
+ services.add(info.getResolveInfo().serviceInfo);
+ }
+ return Collections.unmodifiableList(services);
}
+ /**
+ * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
+ *
+ * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+ */
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
- return Collections.emptyList();
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
+ List<AccessibilityServiceInfo> services = null;
+ try {
+ services = service.getInstalledAccessibilityServiceList(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ }
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -190,21 +548,48 @@ public final class AccessibilityManager {
* @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
* @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
* @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
*/
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
int feedbackTypeFlags) {
- return Collections.emptyList();
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
+ List<AccessibilityServiceInfo> services = null;
+ try {
+ services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ }
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
* Registers an {@link AccessibilityStateChangeListener} for changes in
- * the global accessibility state of the system.
+ * the global accessibility state of the system. Equivalent to calling
+ * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)}
+ * with a null handler.
*
* @param listener The listener.
- * @return True if successfully registered.
+ * @return Always returns {@code true}.
*/
public boolean addAccessibilityStateChangeListener(
- AccessibilityStateChangeListener listener) {
+ @NonNull AccessibilityStateChangeListener listener) {
+ addAccessibilityStateChangeListener(listener, null);
return true;
}
@@ -218,22 +603,40 @@ public final class AccessibilityManager {
* for a callback on the process's main handler.
*/
public void addAccessibilityStateChangeListener(
- @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {}
+ @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mAccessibilityStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
+ /**
+ * Unregisters an {@link AccessibilityStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if the listener was previously registered.
+ */
public boolean removeAccessibilityStateChangeListener(
- AccessibilityStateChangeListener listener) {
- return true;
+ @NonNull AccessibilityStateChangeListener listener) {
+ synchronized (mLock) {
+ int index = mAccessibilityStateChangeListeners.indexOfKey(listener);
+ mAccessibilityStateChangeListeners.remove(listener);
+ return (index >= 0);
+ }
}
/**
* Registers a {@link TouchExplorationStateChangeListener} for changes in
- * the global touch exploration state of the system.
+ * the global touch exploration state of the system. Equivalent to calling
+ * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)}
+ * with a null handler.
*
* @param listener The listener.
- * @return True if successfully registered.
+ * @return Always returns {@code true}.
*/
public boolean addTouchExplorationStateChangeListener(
@NonNull TouchExplorationStateChangeListener listener) {
+ addTouchExplorationStateChangeListener(listener, null);
return true;
}
@@ -247,17 +650,103 @@ public final class AccessibilityManager {
* for a callback on the process's main handler.
*/
public void addTouchExplorationStateChangeListener(
- @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {}
+ @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mTouchExplorationStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
/**
* Unregisters a {@link TouchExplorationStateChangeListener}.
*
* @param listener The listener.
- * @return True if successfully unregistered.
+ * @return True if listener was previously registered.
*/
public boolean removeTouchExplorationStateChangeListener(
@NonNull TouchExplorationStateChangeListener listener) {
- return true;
+ synchronized (mLock) {
+ int index = mTouchExplorationStateChangeListeners.indexOfKey(listener);
+ mTouchExplorationStateChangeListeners.remove(listener);
+ return (index >= 0);
+ }
+ }
+
+ /**
+ * Registers a {@link AccessibilityServicesStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @param handler The handler on which the listener should be called back, or {@code null}
+ * for a callback on the process's main handler.
+ * @hide
+ */
+ public void addAccessibilityServicesStateChangeListener(
+ @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mServicesStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
+
+ /**
+ * Unregisters a {@link AccessibilityServicesStateChangeListener}.
+ *
+ * @param listener The listener.
+ *
+ * @hide
+ */
+ public void removeAccessibilityServicesStateChangeListener(
+ @NonNull AccessibilityServicesStateChangeListener listener) {
+ // Final CopyOnWriteArrayList - no lock needed.
+ mServicesStateChangeListeners.remove(listener);
+ }
+
+ /**
+ * Registers a {@link AccessibilityRequestPreparer}.
+ */
+ public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+ if (mRequestPreparerLists == null) {
+ mRequestPreparerLists = new SparseArray<>(1);
+ }
+ int id = preparer.getView().getAccessibilityViewId();
+ List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id);
+ if (requestPreparerList == null) {
+ requestPreparerList = new ArrayList<>(1);
+ mRequestPreparerLists.put(id, requestPreparerList);
+ }
+ requestPreparerList.add(preparer);
+ }
+
+ /**
+ * Unregisters a {@link AccessibilityRequestPreparer}.
+ */
+ public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+ if (mRequestPreparerLists == null) {
+ return;
+ }
+ int viewId = preparer.getView().getAccessibilityViewId();
+ List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId);
+ if (requestPreparerList != null) {
+ requestPreparerList.remove(preparer);
+ if (requestPreparerList.isEmpty()) {
+ mRequestPreparerLists.remove(viewId);
+ }
+ }
+ }
+
+ /**
+ * Get the preparers that are registered for an accessibility ID
+ *
+ * @param id The ID of interest
+ * @return The list of preparers, or {@code null} if there are none.
+ *
+ * @hide
+ */
+ public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) {
+ if (mRequestPreparerLists == null) {
+ return null;
+ }
+ return mRequestPreparerLists.get(id);
}
/**
@@ -269,7 +758,12 @@ public final class AccessibilityManager {
* @hide
*/
public void addHighTextContrastStateChangeListener(
- @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {}
+ @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mHighTextContrastStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
/**
* Unregisters a {@link HighTextContrastChangeListener}.
@@ -279,7 +773,51 @@ public final class AccessibilityManager {
* @hide
*/
public void removeHighTextContrastStateChangeListener(
- @NonNull HighTextContrastChangeListener listener) {}
+ @NonNull HighTextContrastChangeListener listener) {
+ synchronized (mLock) {
+ mHighTextContrastStateChangeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Check if the accessibility volume stream is active.
+ *
+ * @return True if accessibility volume is active (i.e. some service has requested it). False
+ * otherwise.
+ * @hide
+ */
+ public boolean isAccessibilityVolumeStreamActive() {
+ List<AccessibilityServiceInfo> serviceInfos =
+ getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ for (int i = 0; i < serviceInfos.size(); i++) {
+ if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Report a fingerprint gesture to accessibility. Only available for the system process.
+ *
+ * @param keyCode The key code of the gesture
+ * @return {@code true} if accessibility consumes the event. {@code false} if not.
+ * @hide
+ */
+ public boolean sendFingerprintGesture(int keyCode) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ }
+ try {
+ return service.sendFingerprintGesture(keyCode);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
/**
* Sets the current state and notifies listeners, if necessary.
@@ -287,14 +825,314 @@ public final class AccessibilityManager {
* @param stateFlags The state flags.
*/
private void setStateLocked(int stateFlags) {
+ final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
+ final boolean touchExplorationEnabled =
+ (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
+ final boolean highTextContrastEnabled =
+ (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
+
+ final boolean wasEnabled = mIsEnabled;
+ final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
+
+ // Ensure listeners get current state from isZzzEnabled() calls.
+ mIsEnabled = enabled;
+ mIsTouchExplorationEnabled = touchExplorationEnabled;
+ mIsHighTextContrastEnabled = highTextContrastEnabled;
+
+ if (wasEnabled != enabled) {
+ notifyAccessibilityStateChanged();
+ }
+
+ if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+ notifyTouchExplorationStateChanged();
+ }
+
+ if (wasHighTextContrastEnabled != highTextContrastEnabled) {
+ notifyHighTextContrastStateChanged();
+ }
}
+ /**
+ * Find an installed service with the specified {@link ComponentName}.
+ *
+ * @param componentName The name to match to the service.
+ *
+ * @return The info corresponding to the installed service, or {@code null} if no such service
+ * is installed.
+ * @hide
+ */
+ public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
+ ComponentName componentName) {
+ final List<AccessibilityServiceInfo> installedServiceInfos =
+ getInstalledAccessibilityServiceList();
+ if ((installedServiceInfos == null) || (componentName == null)) {
+ return null;
+ }
+ for (int i = 0; i < installedServiceInfos.size(); i++) {
+ if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
+ return installedServiceInfos.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is added.
+ * @param connection The connection.
+ *
+ * @hide
+ */
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return View.NO_ID;
+ }
+ userId = mUserId;
+ }
+ try {
+ return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+ }
return View.NO_ID;
}
+ /**
+ * Removed an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is removed.
+ *
+ * @hide
+ */
public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.removeAccessibilityInteractionConnection(windowToken);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+ }
+ }
+
+ /**
+ * Perform the accessibility shortcut if the caller has permission.
+ *
+ * @hide
+ */
+ public void performAccessibilityShortcut() {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.performAccessibilityShortcut();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
+ }
+ }
+
+ /**
+ * Notifies that the accessibility button in the system's navigation area has been clicked
+ *
+ * @hide
+ */
+ public void notifyAccessibilityButtonClicked() {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.notifyAccessibilityButtonClicked();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
+ }
+ }
+
+ /**
+ * Notifies that the visibility of the accessibility button in the system's navigation area
+ * has changed.
+ *
+ * @param shown {@code true} if the accessibility button is visible within the system
+ * navigation area, {@code false} otherwise
+ * @hide
+ */
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.notifyAccessibilityButtonVisibilityChanged(shown);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
+ }
+ }
+
+ /**
+ * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
+ * window. Intended for use by the System UI only.
+ *
+ * @param connection The connection to handle the actions. Set to {@code null} to avoid
+ * affecting the actions.
+ *
+ * @hide
+ */
+ public void setPictureInPictureActionReplacingConnection(
+ @Nullable IAccessibilityInteractionConnection connection) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.setPictureInPictureActionReplacingConnection(connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting picture in picture action replacement", re);
+ }
}
+ private IAccessibilityManager getServiceLocked() {
+ if (mService == null) {
+ tryConnectToServiceLocked(null);
+ }
+ return mService;
+ }
+
+ private void tryConnectToServiceLocked(IAccessibilityManager service) {
+ if (service == null) {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ if (iBinder == null) {
+ return;
+ }
+ service = IAccessibilityManager.Stub.asInterface(iBinder);
+ }
+
+ try {
+ final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
+ setStateLocked(IntPair.first(userStateAndRelevantEvents));
+ mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
+ mService = service;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link AccessibilityStateChangeListener}s.
+ */
+ private void notifyAccessibilityStateChanged() {
+ final boolean isEnabled;
+ final ArrayMap<AccessibilityStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mAccessibilityStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isEnabled = mIsEnabled;
+ listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final AccessibilityStateChangeListener listener =
+ mAccessibilityStateChangeListeners.keyAt(i);
+ mAccessibilityStateChangeListeners.valueAt(i)
+ .post(() -> listener.onAccessibilityStateChanged(isEnabled));
+ }
+ }
+
+ /**
+ * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+ */
+ private void notifyTouchExplorationStateChanged() {
+ final boolean isTouchExplorationEnabled;
+ final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mTouchExplorationStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final TouchExplorationStateChangeListener listener =
+ mTouchExplorationStateChangeListeners.keyAt(i);
+ mTouchExplorationStateChangeListeners.valueAt(i)
+ .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
+ }
+ }
+
+ /**
+ * Notifies the registered {@link HighTextContrastChangeListener}s.
+ */
+ private void notifyHighTextContrastStateChanged() {
+ final boolean isHighTextContrastEnabled;
+ final ArrayMap<HighTextContrastChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mHighTextContrastStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isHighTextContrastEnabled = mIsHighTextContrastEnabled;
+ listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final HighTextContrastChangeListener listener =
+ mHighTextContrastStateChangeListeners.keyAt(i);
+ mHighTextContrastStateChangeListeners.valueAt(i)
+ .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+ }
+ }
+
+ /**
+ * Determines if the accessibility button within the system navigation area is supported.
+ *
+ * @return {@code true} if the accessibility button is supported on this device,
+ * {@code false} otherwise
+ */
+ public static boolean isAccessibilityButtonSupported() {
+ final Resources res = Resources.getSystem();
+ return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+ }
+
+ private final class MyCallback implements Handler.Callback {
+ public static final int MSG_SET_STATE = 1;
+
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_SET_STATE: {
+ // See comment at mClient
+ final int state = message.arg1;
+ synchronized (mLock) {
+ setStateLocked(state);
+ }
+ } break;
+ }
+ return true;
+ }
+ }
}
diff --git a/android/view/autofill/AutofillManager.java b/android/view/autofill/AutofillManager.java
index e564fa34..4fb2a99a 100644
--- a/android/view/autofill/AutofillManager.java
+++ b/android/view/autofill/AutofillManager.java
@@ -91,10 +91,10 @@ import java.util.Objects;
* </ul>
*
* <p>When the service returns datasets, the Android System displays an autofill dataset picker
- * UI associated with the view, when the view is focused on and is part of a dataset.
- * The application can be notified when the UI is shown by registering an
+ * UI affordance associated with the view, when the view is focused on and is part of a dataset.
+ * The application can be notified when the affordance is shown by registering an
* {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
- * selects a dataset from the UI, all views present in the dataset are autofilled, through
+ * selects a dataset from the affordance, all views present in the dataset are autofilled, through
* calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
*
* <p>When the service returns ids of savable views, the Android System keeps track of changes
@@ -108,7 +108,7 @@ import java.util.Objects;
* </ul>
*
* <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
- * shows an autofill save UI if the value of savable views have changed. If the user selects the
+ * shows a save UI affordance if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
* <p>It is safe to call into its methods from any thread.
@@ -150,12 +150,6 @@ public final class AutofillManager {
* service authentication will contain the Bundle set by
* {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
*
- * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
- * can also add this bundle to the {@link Intent} set as the
- * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
- * so the bundle can be recovered later on
- * {@link android.service.autofill.SaveRequest#getClientState()}.
- *
* <p>
* Type: {@link android.os.Bundle}
*/
@@ -317,14 +311,6 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
- /** If set, session is commited when the field is clicked. */
- @GuardedBy("mLock")
- @Nullable private AutofillId mSaveTriggerId;
-
- /** If set, session is commited when the activity is finished; otherwise session is canceled. */
- @GuardedBy("mLock")
- private boolean mSaveOnFinish;
-
/** @hide */
public interface AutofillClient {
/**
@@ -848,46 +834,6 @@ public final class AutofillManager {
}
}
-
- /**
- * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
- *
- * @hide
- */
- public void notifyViewClicked(View view) {
- final AutofillId id = view.getAutofillId();
-
- if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
-
- synchronized (mLock) {
- if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
- if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
- commitLocked();
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
- mContext.getPackageName());
- }
- }
- }
-
- /**
- * Called by {@link android.app.Activity} to commit or cancel the session on finish.
- *
- * @hide
- */
- public void onActivityFinished() {
- if (!hasAutofillFeature()) {
- return;
- }
- synchronized (mLock) {
- if (mSaveOnFinish) {
- commitLocked();
- } else {
- if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
- cancelLocked();
- }
- }
- }
-
/**
* Called to indicate the current autofill context should be commited.
*
@@ -904,15 +850,12 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- commitLocked();
- }
- }
+ if (!mEnabled && !isActiveLocked()) {
+ return;
+ }
- private void commitLocked() {
- if (!mEnabled && !isActiveLocked()) {
- return;
+ finishSessionLocked();
}
- finishSessionLocked();
}
/**
@@ -931,15 +874,12 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- cancelLocked();
- }
- }
+ if (!mEnabled && !isActiveLocked()) {
+ return;
+ }
- private void cancelLocked() {
- if (!mEnabled && !isActiveLocked()) {
- return;
+ cancelSessionLocked();
}
- cancelSessionLocked();
}
/** @hide */
@@ -997,12 +937,7 @@ public final class AutofillManager {
}
private AutofillClient getClientLocked() {
- final AutofillClient client = mContext.getAutofillClient();
- if (client == null && sDebug) {
- Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
- + mContext);
- }
- return client;
+ return mContext.getAutofillClient();
}
/** @hide */
@@ -1024,10 +959,6 @@ public final class AutofillManager {
final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
final Bundle responseData = new Bundle();
responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
- final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
- if (newClientState != null) {
- responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
- }
try {
mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
mContext.getUserId());
@@ -1107,7 +1038,6 @@ public final class AutofillManager {
mState = STATE_UNKNOWN;
mTrackedViews = null;
mFillableIds = null;
- mSaveTriggerId = null;
}
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
@@ -1359,15 +1289,12 @@ public final class AutofillManager {
/**
* Set the tracked views.
*
- * @param trackedIds The views to be tracked.
+ * @param trackedIds The views to be tracked
* @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
- * @param saveOnFinish Finish the session once the activity is finished.
* @param fillableIds Views that might anchor FillUI.
- * @param saveTriggerId View that when clicked triggers commit().
*/
private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
- boolean saveOnAllViewsInvisible, boolean saveOnFinish,
- @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
+ boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
synchronized (mLock) {
if (mEnabled && mSessionId == sessionId) {
if (saveOnAllViewsInvisible) {
@@ -1375,7 +1302,6 @@ public final class AutofillManager {
} else {
mTrackedViews = null;
}
- mSaveOnFinish = saveOnFinish;
if (fillableIds != null) {
if (mFillableIds == null) {
mFillableIds = new ArraySet<>(fillableIds.length);
@@ -1388,30 +1314,10 @@ public final class AutofillManager {
+ ", mFillableIds" + mFillableIds);
}
}
-
- if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
- // Turn off trigger on previous view id.
- setNotifyOnClickLocked(mSaveTriggerId, false);
- }
-
- if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
- // Turn on trigger on new view id.
- mSaveTriggerId = saveTriggerId;
- setNotifyOnClickLocked(mSaveTriggerId, true);
- }
}
}
}
- private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
- final View view = findView(id);
- if (view == null) {
- Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
- return;
- }
- view.setNotifyAutofillManagerOnClick(notify);
- }
-
private void setSaveUiState(int sessionId, boolean shown) {
if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
synchronized (mLock) {
@@ -1584,7 +1490,6 @@ public final class AutofillManager {
final String pfx = outerPrefix + " ";
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
- pw.print(pfx); pw.print("context: "); pw.println(mContext);
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1599,8 +1504,6 @@ public final class AutofillManager {
pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
- pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
- pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
}
private String getStateAsStringLocked() {
@@ -1849,7 +1752,7 @@ public final class AutofillManager {
* Callback for autofill related events.
*
* <p>Typically used for applications that display their own "auto-complete" views, so they can
- * enable / disable such views when the autofill UI is shown / hidden.
+ * enable / disable such views when the autofill UI affordance is shown / hidden.
*/
public abstract static class AutofillCallback {
@@ -1859,26 +1762,26 @@ public final class AutofillManager {
public @interface AutofillEventType {}
/**
- * The autofill input UI associated with the view was shown.
+ * The autofill input UI affordance associated with the view was shown.
*
- * <p>If the view provides its own auto-complete UI and its currently shown, it
+ * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
* should be hidden upon receiving this event.
*/
public static final int EVENT_INPUT_SHOWN = 1;
/**
- * The autofill input UI associated with the view was hidden.
+ * The autofill input UI affordance associated with the view was hidden.
*
- * <p>If the view provides its own auto-complete UI that was hidden upon a
+ * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
* {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
*/
public static final int EVENT_INPUT_HIDDEN = 2;
/**
- * The autofill input UI associated with the view isn't shown because
+ * The autofill input UI affordance associated with the view isn't shown because
* autofill is not available.
*
- * <p>If the view provides its own auto-complete UI but was not displaying it
+ * <p>If the view provides its own auto-complete UI affordance but was not displaying it
* to avoid flickering, it could shown it upon receiving this event.
*/
public static final int EVENT_INPUT_UNAVAILABLE = 3;
@@ -1980,12 +1883,12 @@ public final class AutofillManager {
@Override
public void setTrackedViews(int sessionId, AutofillId[] ids,
- boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
- AutofillId saveTriggerId) {
+ boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
- saveOnFinish, fillableIds, saveTriggerId));
+ afm.post(() ->
+ afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
+ );
}
}
diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java
index c3601d9d..bb1e693f 100644
--- a/android/view/textclassifier/TextClassifier.java
+++ b/android/view/textclassifier/TextClassifier.java
@@ -152,12 +152,4 @@ public interface TextClassifier {
*/
@WorkerThread
default void logEvent(String source, String event) {}
-
- /**
- * Returns this TextClassifier's settings.
- * @hide
- */
- default TextClassifierConstants getSettings() {
- return TextClassifierConstants.DEFAULT;
- }
}
diff --git a/android/view/textclassifier/TextClassifierConstants.java b/android/view/textclassifier/TextClassifierConstants.java
deleted file mode 100644
index 51e6168e..00000000
--- a/android/view/textclassifier/TextClassifierConstants.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-
-/**
- * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
- * </pre>
- *
- * <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
- * Example of setting the values for testing.
- * adb shell settings put global text_classifier_constants smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true
- * @hide
- */
-public final class TextClassifierConstants {
-
- private static final String LOG_TAG = "TextClassifierConstants";
-
- private static final String SMART_SELECTION_DARK_LAUNCH =
- "smart_selection_dark_launch";
- private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
- "smart_selection_enabled_for_edit_text";
-
- private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
- private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
-
- /** Default settings. */
- static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
- private final boolean mDarkLaunch;
- private final boolean mSuggestSelectionEnabledForEditableText;
-
- private TextClassifierConstants() {
- mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
- mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
- }
-
- private TextClassifierConstants(@Nullable String settings) {
- final KeyValueListParser parser = new KeyValueListParser(',');
- try {
- parser.setString(settings);
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
- }
- mDarkLaunch = parser.getBoolean(
- SMART_SELECTION_DARK_LAUNCH,
- SMART_SELECTION_DARK_LAUNCH_DEFAULT);
- mSuggestSelectionEnabledForEditableText = parser.getBoolean(
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
- }
-
- static TextClassifierConstants loadFromString(String settings) {
- return new TextClassifierConstants(settings);
- }
-
- public boolean isDarkLaunch() {
- return mDarkLaunch;
- }
-
- public boolean isSuggestSelectionEnabledForEditableText() {
- return mSuggestSelectionEnabledForEditableText;
- }
-}
diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java
index ef087472..2aa81a2c 100644
--- a/android/view/textclassifier/TextClassifierImpl.java
+++ b/android/view/textclassifier/TextClassifierImpl.java
@@ -24,12 +24,12 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
+import android.icu.text.BreakIterator;
import android.net.Uri;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.provider.Browser;
import android.provider.ContactsContract;
-import android.provider.Settings;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.method.WordIterator;
@@ -47,7 +47,6 @@ import com.android.internal.util.Preconditions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -92,8 +91,6 @@ final class TextClassifierImpl implements TextClassifier {
@GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
private SmartSelection mSmartSelection;
- private TextClassifierConstants mSettings;
-
TextClassifierImpl(Context context) {
mContext = Preconditions.checkNotNull(context);
}
@@ -192,15 +189,6 @@ final class TextClassifierImpl implements TextClassifier {
}
}
- @Override
- public TextClassifierConstants getSettings() {
- if (mSettings == null) {
- mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
- mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
- }
- return mSettings;
- }
-
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mSmartSelectionLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
diff --git a/android/view/textservice/TextServicesManager.java b/android/view/textservice/TextServicesManager.java
index 8e1f2183..f368c74a 100644
--- a/android/view/textservice/TextServicesManager.java
+++ b/android/view/textservice/TextServicesManager.java
@@ -1,58 +1,213 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2011 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
+ * 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
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
*/
package android.view.textservice;
+import android.annotation.SystemService;
+import android.content.Context;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+
import java.util.Locale;
/**
- * A stub class of TextServicesManager for Layout-Lib.
+ * System API to the overall text services, which arbitrates interaction between applications
+ * and text services.
+ *
+ * The user can change the current text services in Settings. And also applications can specify
+ * the target text services.
+ *
+ * <h3>Architecture Overview</h3>
+ *
+ * <p>There are three primary parties involved in the text services
+ * framework (TSF) architecture:</p>
+ *
+ * <ul>
+ * <li> The <strong>text services manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts. It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> A <strong>text service</strong> implements a particular
+ * interaction model allowing the client application to retrieve information of text.
+ * The system binds to the current text service that is in use, causing it to be created and run.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the text service
+ * manager for connections to text services.
+ * </ul>
+ *
+ * <h3>Text services sessions</h3>
+ * <ul>
+ * <li>The <strong>spell checker session</strong> is one of the text services.
+ * {@link android.view.textservice.SpellCheckerSession}</li>
+ * </ul>
+ *
*/
+@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE)
public final class TextServicesManager {
- private static final TextServicesManager sInstance = new TextServicesManager();
- private static final SpellCheckerInfo[] EMPTY_SPELL_CHECKER_INFO = new SpellCheckerInfo[0];
+ private static final String TAG = TextServicesManager.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static TextServicesManager sInstance;
+
+ private final ITextServicesManager mService;
+
+ private TextServicesManager() throws ServiceNotFoundException {
+ mService = ITextServicesManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
+ }
/**
* Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
* @hide
*/
public static TextServicesManager getInstance() {
- return sInstance;
+ synchronized (TextServicesManager.class) {
+ if (sInstance == null) {
+ try {
+ sInstance = new TextServicesManager();
+ } catch (ServiceNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Returns the language component of a given locale string.
+ */
+ private static String parseLanguageFromLocaleString(String locale) {
+ final int idx = locale.indexOf('_');
+ if (idx < 0) {
+ return locale;
+ } else {
+ return locale.substring(0, idx);
+ }
}
+ /**
+ * Get a spell checker session for the specified spell checker
+ * @param locale the locale for the spell checker. If {@code locale} is null and
+ * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+ * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
+ * the locale specified in Settings will be returned only when it is same as {@code locale}.
+ * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
+ * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+ * selected.
+ * @param listener a spell checker session lister for getting results from a spell checker.
+ * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
+ * languages in settings will be returned.
+ * @return the spell checker session of the spell checker
+ */
public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
- return null;
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ if (!referToSpellCheckerLanguageSettings && locale == null) {
+ throw new IllegalArgumentException("Locale should not be null if you don't refer"
+ + " settings.");
+ }
+
+ if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+ return null;
+ }
+
+ final SpellCheckerInfo sci;
+ try {
+ sci = mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ return null;
+ }
+ if (sci == null) {
+ return null;
+ }
+ SpellCheckerSubtype subtypeInUse = null;
+ if (referToSpellCheckerLanguageSettings) {
+ subtypeInUse = getCurrentSpellCheckerSubtype(true);
+ if (subtypeInUse == null) {
+ return null;
+ }
+ if (locale != null) {
+ final String subtypeLocale = subtypeInUse.getLocale();
+ final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
+ if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
+ return null;
+ }
+ }
+ } else {
+ final String localeStr = locale.toString();
+ for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+ final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
+ final String tempSubtypeLocale = subtype.getLocale();
+ final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
+ if (tempSubtypeLocale.equals(localeStr)) {
+ subtypeInUse = subtype;
+ break;
+ } else if (tempSubtypeLanguage.length() >= 2 &&
+ locale.getLanguage().equals(tempSubtypeLanguage)) {
+ subtypeInUse = subtype;
+ }
+ }
+ }
+ if (subtypeInUse == null) {
+ return null;
+ }
+ final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener);
+ try {
+ mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
+ session.getTextServicesSessionListener(),
+ session.getSpellCheckerSessionListener(), bundle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return session;
}
/**
* @hide
*/
public SpellCheckerInfo[] getEnabledSpellCheckers() {
- return EMPTY_SPELL_CHECKER_INFO;
+ try {
+ final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
+ if (DBG) {
+ Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
+ }
+ return retval;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* @hide
*/
public SpellCheckerInfo getCurrentSpellChecker() {
- return null;
+ try {
+ // Passing null as a locale for ICS
+ return mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -60,13 +215,22 @@ public final class TextServicesManager {
*/
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
boolean allowImplicitlySelectedSubtype) {
- return null;
+ try {
+ // Passing null as a locale until we support multiple enabled spell checker subtypes.
+ return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* @hide
*/
public boolean isSpellCheckerEnabled() {
- return false;
+ try {
+ return mService.isSpellCheckerEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/android/webkit/WebView.java b/android/webkit/WebView.java
index 202f2046..dfc81b2b 100644
--- a/android/webkit/WebView.java
+++ b/android/webkit/WebView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,223 +16,3001 @@
package android.webkit;
-import com.android.layoutlib.bridge.MockView;
-
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.Widget;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Picture;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.http.SslCertificate;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.print.PrintDocumentAdapter;
+import android.security.KeyChain;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.DragEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillValue;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.textclassifier.TextClassifier;
+import android.widget.AbsoluteLayout;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Map;
/**
- * Mock version of the WebView.
- * Only non override public methods from the real WebView have been added in there.
- * Methods that take an unknown class as parameter or as return object, have been removed for now.
- *
- * TODO: generate automatically.
+ * <p>A View that displays web pages. This class is the basis upon which you
+ * can roll your own web browser or simply display some online content within your Activity.
+ * It uses the WebKit rendering engine to display
+ * web pages and includes methods to navigate forward and backward
+ * through a history, zoom in and out, perform text searches and more.
+ *
+ * <p>Note that, in order for your Activity to access the Internet and load web pages
+ * in a WebView, you must add the {@code INTERNET} permissions to your
+ * Android Manifest file:
+ *
+ * <pre>
+ * {@code <uses-permission android:name="android.permission.INTERNET" />}
+ * </pre>
+ *
+ * <p>This must be a child of the <a
+ * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
+ * element.
+ *
+ * <p>For more information, read
+ * <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>By default, a WebView provides no browser-like widgets, does not
+ * enable JavaScript and web page errors are ignored. If your goal is only
+ * to display some HTML as a part of your UI, this is probably fine;
+ * the user won't need to interact with the web page beyond reading
+ * it, and the web page won't need to interact with the user. If you
+ * actually want a full-blown web browser, then you probably want to
+ * invoke the Browser application with a URL Intent rather than show it
+ * with a WebView. For example:
+ * <pre>
+ * Uri uri = Uri.parse("https://www.example.com");
+ * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ * startActivity(intent);
+ * </pre>
+ * <p>See {@link android.content.Intent} for more information.
+ *
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
+ * or set the entire Activity window as a WebView during {@link
+ * android.app.Activity#onCreate(Bundle) onCreate()}:
+ *
+ * <pre class="prettyprint">
+ * WebView webview = new WebView(this);
+ * setContentView(webview);
+ * </pre>
+ *
+ * <p>Then load the desired web page:
+ *
+ * <pre>
+ * // Simplest usage: note that an exception will NOT be thrown
+ * // if there is an error loading this page (see below).
+ * webview.loadUrl("https://example.com/");
+ *
+ * // OR, you can also load from an HTML string:
+ * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
+ * webview.loadData(summary, "text/html", null);
+ * // ... although note that there are restrictions on what this HTML can do.
+ * // See the JavaDocs for {@link #loadData(String,String,String) loadData()} and {@link
+ * #loadDataWithBaseURL(String,String,String,String,String) loadDataWithBaseURL()} for more info.
+ * </pre>
+ *
+ * <p>A WebView has several customization points where you can add your
+ * own behavior. These are:
+ *
+ * <ul>
+ * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
+ * This class is called when something that might impact a
+ * browser UI happens, for instance, progress updates and
+ * JavaScript alerts are sent here (see <a
+ * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
+ * </li>
+ * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
+ * It will be called when things happen that impact the
+ * rendering of the content, eg, errors or form submissions. You
+ * can also intercept URL loading here (via {@link
+ * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
+ * shouldOverrideUrlLoading()}).</li>
+ * <li>Modifying the {@link android.webkit.WebSettings}, such as
+ * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
+ * setJavaScriptEnabled()}. </li>
+ * <li>Injecting Java objects into the WebView using the
+ * {@link android.webkit.WebView#addJavascriptInterface} method. This
+ * method allows you to inject Java objects into a page's JavaScript
+ * context, so that they can be accessed by JavaScript in the page.</li>
+ * </ul>
+ *
+ * <p>Here's a more complicated example, showing error handling,
+ * settings, and progress notification:
+ *
+ * <pre class="prettyprint">
+ * // Let's display the progress in the activity title bar, like the
+ * // browser app does.
+ * getWindow().requestFeature(Window.FEATURE_PROGRESS);
+ *
+ * webview.getSettings().setJavaScriptEnabled(true);
+ *
+ * final Activity activity = this;
+ * webview.setWebChromeClient(new WebChromeClient() {
+ * public void onProgressChanged(WebView view, int progress) {
+ * // Activities and WebViews measure progress with different scales.
+ * // The progress meter will automatically disappear when we reach 100%
+ * activity.setProgress(progress * 1000);
+ * }
+ * });
+ * webview.setWebViewClient(new WebViewClient() {
+ * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
+ * }
+ * });
+ *
+ * webview.loadUrl("https://developer.android.com/");
+ * </pre>
+ *
+ * <h3>Zoom</h3>
+ *
+ * <p>To enable the built-in zoom, set
+ * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
+ * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
+ *
+ * <p>NOTE: Using zoom if either the height or width is set to
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
+ * and should be avoided.
+ *
+ * <h3>Cookie and window management</h3>
+ *
+ * <p>For obvious security reasons, your application has its own
+ * cache, cookie store etc.&mdash;it does not share the Browser
+ * application's data.
+ *
+ * <p>By default, requests by the HTML to open new windows are
+ * ignored. This is {@code true} whether they be opened by JavaScript or by
+ * the target attribute on a link. You can customize your
+ * {@link WebChromeClient} to provide your own behavior for opening multiple windows,
+ * and render them in whatever manner you want.
+ *
+ * <p>The standard behavior for an Activity is to be destroyed and
+ * recreated when the device orientation or any other configuration changes. This will cause
+ * the WebView to reload the current page. If you don't want that, you
+ * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
+ * changes, and then just leave the WebView alone. It'll automatically
+ * re-orient itself as appropriate. Read <a
+ * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
+ * more information about how to handle configuration changes during runtime.
+ *
+ *
+ * <h3>Building web pages to support different screen densities</h3>
+ *
+ * <p>The screen density of a device is based on the screen resolution. A screen with low density
+ * has fewer available pixels per inch, where a screen with high density
+ * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
+ * screen is important because, other things being equal, a UI element (such as a button) whose
+ * height and width are defined in terms of screen pixels will appear larger on the lower density
+ * screen and smaller on the higher density screen.
+ * For simplicity, Android collapses all actual screen densities into three generalized densities:
+ * high, medium, and low.
+ * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
+ * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
+ * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
+ * are bigger).
+ * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, WebView supports DOM, CSS,
+ * and meta tag features to help you (as a web developer) target screens with different screen
+ * densities.
+ * <p>Here's a summary of the features you can use to handle different screen densities:
+ * <ul>
+ * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
+ * default scaling factor used for the current device. For example, if the value of {@code
+ * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
+ * and default scaling is not applied to the web page; if the value is "1.5", then the device is
+ * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
+ * value is "0.75", then the device is considered a low density device (ldpi) and the content is
+ * scaled 0.75x.</li>
+ * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
+ * densities for which this style sheet is to be used. The corresponding value should be either
+ * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
+ * density, or high density screens, respectively. For example:
+ * <pre>
+ * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
+ * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5,
+ * which is the high density pixel ratio.
+ * </li>
+ * </ul>
+ *
+ * <h3>HTML5 Video support</h3>
+ *
+ * <p>In order to support inline HTML5 video in your application you need to have hardware
+ * acceleration turned on.
+ *
+ * <h3>Full screen support</h3>
+ *
+ * <p>In order to support full screen &mdash; for video or other HTML content &mdash; you need to set a
+ * {@link android.webkit.WebChromeClient} and implement both
+ * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
+ * and {@link WebChromeClient#onHideCustomView()}. If the implementation of either of these two methods is
+ * missing then the web contents will not be allowed to enter full screen. Optionally you can implement
+ * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View displayed whilst a video
+ * is loading.
+ *
+ * <h3>HTML5 Geolocation API support</h3>
+ *
+ * <p>For applications targeting Android N and later releases
+ * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
+ * secure origins such as https. For such applications requests to geolocation api on non-secure
+ * origins are automatically denied without invoking the corresponding
+ * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
+ * method.
+ *
+ * <h3>Layout size</h3>
+ * <p>
+ * It is recommended to set the WebView layout height to a fixed value or to
+ * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
+ * When using {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * for the height none of the WebView's parents should use a
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since that could result in
+ * incorrect sizing of the views.
+ *
+ * <p>Setting the WebView's height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * enables the following behaviors:
+ * <ul>
+ * <li>The HTML body layout height is set to a fixed value. This means that elements with a height
+ * relative to the HTML body may not be sized correctly. </li>
+ * <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} and earlier SDKs the
+ * HTML viewport meta tag will be ignored in order to preserve backwards compatibility. </li>
+ * </ul>
+ *
+ * <p>
+ * Using a layout width of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not
+ * supported. If such a width is used the WebView will attempt to use the width of the parent
+ * instead.
+ *
+ * <h3>Metrics</h3>
+ *
+ * <p>
+ * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
+ * helps Google improve WebView. Data is collected on a per-app basis for each app which has
+ * instantiated a WebView. An individual app can opt out of this feature by putting the following
+ * tag in its manifest:
+ * <pre>
+ * &lt;meta-data android:name="android.webkit.WebView.MetricsOptOut"
+ * android:value="true" /&gt;
+ * </pre>
+ * <p>
+ * Data will only be uploaded for a given app if the user has consented AND the app has not opted
+ * out.
+ *
+ * <h3>Safe Browsing</h3>
+ *
+ * <p>
+ * If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
+ * user to allow them to navigate back safely or proceed to the malicious page.
+ * <p>
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * <p>
+ * <pre>
+ * &lt;meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ * android:value="true" /&gt;
+ * </pre>
*
*/
-public class WebView extends MockView {
+// Implementation notes.
+// The WebView is a thin API class that delegates its public API to a backend WebViewProvider
+// class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons.
+// Methods are delegated to the provider implementation: all public API methods introduced in this
+// file are fully delegated, whereas public and protected methods from the View base classes are
+// only delegated where a specific need exists for them to do so.
+@Widget
+public class WebView extends AbsoluteLayout
+ implements ViewTreeObserver.OnGlobalFocusChangeListener,
+ ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
+
+ private static final String LOGTAG = "WebView";
+
+ // Throwing an exception for incorrect thread usage if the
+ // build target is JB MR2 or newer. Defaults to false, and is
+ // set in the WebView constructor.
+ private static volatile boolean sEnforceThreadChecking = false;
+
+ /**
+ * Transportation object for returning WebView across thread boundaries.
+ */
+ public class WebViewTransport {
+ private WebView mWebview;
+ /**
+ * Sets the WebView to the transportation object.
+ *
+ * @param webview the WebView to transport
+ */
+ public synchronized void setWebView(WebView webview) {
+ mWebview = webview;
+ }
+
+ /**
+ * Gets the WebView object.
+ *
+ * @return the transported WebView object
+ */
+ public synchronized WebView getWebView() {
+ return mWebview;
+ }
+ }
+
+ /**
+ * URI scheme for telephone number.
+ */
+ public static final String SCHEME_TEL = "tel:";
/**
- * Construct a new WebView with a Context object.
- * @param context A Context object used to access application assets.
+ * URI scheme for email address.
+ */
+ public static final String SCHEME_MAILTO = "mailto:";
+ /**
+ * URI scheme for map address.
+ */
+ public static final String SCHEME_GEO = "geo:0,0?q=";
+
+ /**
+ * Interface to listen for find results.
+ */
+ public interface FindListener {
+ /**
+ * Notifies the listener about progress made by a find operation.
+ *
+ * @param activeMatchOrdinal the zero-based ordinal of the currently selected match
+ * @param numberOfMatches how many matches have been found
+ * @param isDoneCounting whether the find operation has actually completed. The listener
+ * may be notified multiple times while the
+ * operation is underway, and the numberOfMatches
+ * value should not be considered final unless
+ * isDoneCounting is {@code true}.
+ */
+ public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
+ boolean isDoneCounting);
+ }
+
+ /**
+ * Callback interface supplied to {@link #postVisualStateCallback} for receiving
+ * notifications about the visual state.
+ */
+ public static abstract class VisualStateCallback {
+ /**
+ * Invoked when the visual state is ready to be drawn in the next {@link #onDraw}.
+ *
+ * @param requestId The identifier passed to {@link #postVisualStateCallback} when this
+ * callback was posted.
+ */
+ public abstract void onComplete(long requestId);
+ }
+
+ /**
+ * Interface to listen for new pictures as they change.
+ *
+ * @deprecated This interface is now obsolete.
+ */
+ @Deprecated
+ public interface PictureListener {
+ /**
+ * Used to provide notification that the WebView's picture has changed.
+ * See {@link WebView#capturePicture} for details of the picture.
+ *
+ * @param view the WebView that owns the picture
+ * @param picture the new picture. Applications targeting
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} or above
+ * will always receive a {@code null} Picture.
+ * @deprecated Deprecated due to internal changes.
+ */
+ @Deprecated
+ void onNewPicture(WebView view, @Nullable Picture picture);
+ }
+
+ public static class HitTestResult {
+ /**
+ * Default HitTestResult, where the target is unknown.
+ */
+ public static final int UNKNOWN_TYPE = 0;
+ /**
+ * @deprecated This type is no longer used.
+ */
+ @Deprecated
+ public static final int ANCHOR_TYPE = 1;
+ /**
+ * HitTestResult for hitting a phone number.
+ */
+ public static final int PHONE_TYPE = 2;
+ /**
+ * HitTestResult for hitting a map address.
+ */
+ public static final int GEO_TYPE = 3;
+ /**
+ * HitTestResult for hitting an email address.
+ */
+ public static final int EMAIL_TYPE = 4;
+ /**
+ * HitTestResult for hitting an HTML::img tag.
+ */
+ public static final int IMAGE_TYPE = 5;
+ /**
+ * @deprecated This type is no longer used.
+ */
+ @Deprecated
+ public static final int IMAGE_ANCHOR_TYPE = 6;
+ /**
+ * HitTestResult for hitting a HTML::a tag with src=http.
+ */
+ public static final int SRC_ANCHOR_TYPE = 7;
+ /**
+ * HitTestResult for hitting a HTML::a tag with src=http + HTML::img.
+ */
+ public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
+ /**
+ * HitTestResult for hitting an edit text area.
+ */
+ public static final int EDIT_TEXT_TYPE = 9;
+
+ private int mType;
+ private String mExtra;
+
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ @SystemApi
+ public HitTestResult() {
+ mType = UNKNOWN_TYPE;
+ }
+
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ @SystemApi
+ public void setType(int type) {
+ mType = type;
+ }
+
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ @SystemApi
+ public void setExtra(String extra) {
+ mExtra = extra;
+ }
+
+ /**
+ * Gets the type of the hit test result. See the XXX_TYPE constants
+ * defined in this class.
+ *
+ * @return the type of the hit test result
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets additional type-dependant information about the result. See
+ * {@link WebView#getHitTestResult()} for details. May either be {@code null}
+ * or contain extra information about this result.
+ *
+ * @return additional type-dependant information about the result
+ */
+ @Nullable
+ public String getExtra() {
+ return mExtra;
+ }
+ }
+
+ /**
+ * Constructs a new WebView with a Context object.
+ *
+ * @param context a Context object used to access application assets
*/
public WebView(Context context) {
this(context, null);
}
/**
- * Construct a new WebView with layout parameters.
- * @param context A Context object used to access application assets.
- * @param attrs An AttributeSet passed to our parent.
+ * Constructs a new WebView with layout parameters.
+ *
+ * @param context a Context object used to access application assets
+ * @param attrs an AttributeSet passed to our parent
*/
public WebView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.webViewStyle);
}
/**
- * Construct a new WebView with layout parameters and a default style.
- * @param context A Context object used to access application assets.
- * @param attrs An AttributeSet passed to our parent.
- * @param defStyle The default style resource ID.
+ * Constructs a new WebView with layout parameters and a default style.
+ *
+ * @param context a Context object used to access application assets
+ * @param attrs an AttributeSet passed to our parent
+ * @param defStyleAttr an attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults.
*/
- public WebView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public WebView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
-
- // START FAKE PUBLIC METHODS
-
+
+ /**
+ * Constructs a new WebView with layout parameters and a default style.
+ *
+ * @param context a Context object used to access application assets
+ * @param attrs an AttributeSet passed to our parent
+ * @param defStyleAttr an attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults.
+ * @param defStyleRes a resource identifier of a style resource that
+ * supplies default values for the view, used only if
+ * defStyleAttr is 0 or can not be found in the theme. Can be 0
+ * to not look for defaults.
+ */
+ public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ this(context, attrs, defStyleAttr, defStyleRes, null, false);
+ }
+
+ /**
+ * Constructs a new WebView with layout parameters and a default style.
+ *
+ * @param context a Context object used to access application assets
+ * @param attrs an AttributeSet passed to our parent
+ * @param defStyleAttr an attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults.
+ * @param privateBrowsing whether this WebView will be initialized in
+ * private mode
+ *
+ * @deprecated Private browsing is no longer supported directly via
+ * WebView and will be removed in a future release. Prefer using
+ * {@link WebSettings}, {@link WebViewDatabase}, {@link CookieManager}
+ * and {@link WebStorage} for fine-grained control of privacy data.
+ */
+ @Deprecated
+ public WebView(Context context, AttributeSet attrs, int defStyleAttr,
+ boolean privateBrowsing) {
+ this(context, attrs, defStyleAttr, 0, null, privateBrowsing);
+ }
+
+ /**
+ * Constructs a new WebView with layout parameters, a default style and a set
+ * of custom JavaScript interfaces to be added to this WebView at initialization
+ * time. This guarantees that these interfaces will be available when the JS
+ * context is initialized.
+ *
+ * @param context a Context object used to access application assets
+ * @param attrs an AttributeSet passed to our parent
+ * @param defStyleAttr an attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults.
+ * @param javaScriptInterfaces a Map of interface names, as keys, and
+ * object implementing those interfaces, as
+ * values
+ * @param privateBrowsing whether this WebView will be initialized in
+ * private mode
+ * @hide This is used internally by dumprendertree, as it requires the JavaScript interfaces to
+ * be added synchronously, before a subsequent loadUrl call takes effect.
+ */
+ protected WebView(Context context, AttributeSet attrs, int defStyleAttr,
+ Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+ this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing);
+ }
+
+ /**
+ * @hide
+ */
+ @SuppressWarnings("deprecation") // for super() call into deprecated base class constructor.
+ protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
+ Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ // WebView is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
+ if (context == null) {
+ throw new IllegalArgumentException("Invalid context argument");
+ }
+ sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.JELLY_BEAN_MR2;
+ checkThread();
+
+ ensureProviderCreated();
+ mProvider.init(javaScriptInterfaces, privateBrowsing);
+ // Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
+ CookieSyncManager.setGetInstanceIsAllowed();
+ }
+
+ /**
+ * Specifies whether the horizontal scrollbar has overlay style.
+ *
+ * @deprecated This method has no effect.
+ * @param overlay {@code true} if horizontal scrollbar should have overlay style
+ */
+ @Deprecated
public void setHorizontalScrollbarOverlay(boolean overlay) {
}
+ /**
+ * Specifies whether the vertical scrollbar has overlay style.
+ *
+ * @deprecated This method has no effect.
+ * @param overlay {@code true} if vertical scrollbar should have overlay style
+ */
+ @Deprecated
public void setVerticalScrollbarOverlay(boolean overlay) {
}
+ /**
+ * Gets whether horizontal scrollbar has overlay style.
+ *
+ * @deprecated This method is now obsolete.
+ * @return {@code true}
+ */
+ @Deprecated
public boolean overlayHorizontalScrollbar() {
- return false;
+ // The old implementation defaulted to true, so return true for consistency
+ return true;
}
+ /**
+ * Gets whether vertical scrollbar has overlay style.
+ *
+ * @deprecated This method is now obsolete.
+ * @return {@code false}
+ */
+ @Deprecated
public boolean overlayVerticalScrollbar() {
+ // The old implementation defaulted to false, so return false for consistency
return false;
}
+ /**
+ * Gets the visible height (in pixels) of the embedded title bar (if any).
+ *
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
+ public int getVisibleTitleHeight() {
+ checkThread();
+ return mProvider.getVisibleTitleHeight();
+ }
+
+ /**
+ * Gets the SSL certificate for the main top-level page or {@code null} if there is
+ * no certificate (the site is not secure).
+ *
+ * @return the SSL certificate for the main top-level page
+ */
+ @Nullable
+ public SslCertificate getCertificate() {
+ checkThread();
+ return mProvider.getCertificate();
+ }
+
+ /**
+ * Sets the SSL certificate for the main top-level page.
+ *
+ * @deprecated Calling this function has no useful effect, and will be
+ * ignored in future releases.
+ */
+ @Deprecated
+ public void setCertificate(SslCertificate certificate) {
+ checkThread();
+ mProvider.setCertificate(certificate);
+ }
+
+ //-------------------------------------------------------------------------
+ // Methods called by activity
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets a username and password pair for the specified host. This data is
+ * used by the WebView to autocomplete username and password fields in web
+ * forms. Note that this is unrelated to the credentials used for HTTP
+ * authentication.
+ *
+ * @param host the host that required the credentials
+ * @param username the username for the given host
+ * @param password the password for the given host
+ * @see WebViewDatabase#clearUsernamePassword
+ * @see WebViewDatabase#hasUsernamePassword
+ * @deprecated Saving passwords in WebView will not be supported in future versions.
+ */
+ @Deprecated
public void savePassword(String host, String username, String password) {
+ checkThread();
+ mProvider.savePassword(host, username, password);
}
+ /**
+ * Stores HTTP authentication credentials for a given host and realm to the {@link WebViewDatabase}
+ * instance.
+ *
+ * @param host the host to which the credentials apply
+ * @param realm the realm to which the credentials apply
+ * @param username the username
+ * @param password the password
+ * @deprecated Use {@link WebViewDatabase#setHttpAuthUsernamePassword} instead
+ */
+ @Deprecated
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
+ checkThread();
+ mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
}
+ /**
+ * Retrieves HTTP authentication credentials for a given host and realm from the {@link
+ * WebViewDatabase} instance.
+ * @param host the host to which the credentials apply
+ * @param realm the realm to which the credentials apply
+ * @return the credentials as a String array, if found. The first element
+ * is the username and the second element is the password. {@code null} if
+ * no credentials are found.
+ * @deprecated Use {@link WebViewDatabase#getHttpAuthUsernamePassword} instead
+ */
+ @Deprecated
+ @Nullable
public String[] getHttpAuthUsernamePassword(String host, String realm) {
- return null;
+ checkThread();
+ return mProvider.getHttpAuthUsernamePassword(host, realm);
}
+ /**
+ * Destroys the internal state of this WebView. This method should be called
+ * after this WebView has been removed from the view system. No other
+ * methods may be called on this WebView after destroy.
+ */
public void destroy() {
+ checkThread();
+ mProvider.destroy();
}
+ /**
+ * Enables platform notifications of data state and proxy changes.
+ * Notifications are enabled by default.
+ *
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
public static void enablePlatformNotifications() {
+ // noop
}
+ /**
+ * Disables platform notifications of data state and proxy changes.
+ * Notifications are enabled by default.
+ *
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
public static void disablePlatformNotifications() {
+ // noop
}
+ /**
+ * Used only by internal tests to free up memory.
+ *
+ * @hide
+ */
+ public static void freeMemoryForTests() {
+ getFactory().getStatics().freeMemoryForTests();
+ }
+
+ /**
+ * Informs WebView of the network state. This is used to set
+ * the JavaScript property window.navigator.isOnline and
+ * generates the online/offline event as specified in HTML5, sec. 5.7.7
+ *
+ * @param networkUp a boolean indicating if network is available
+ */
+ public void setNetworkAvailable(boolean networkUp) {
+ checkThread();
+ mProvider.setNetworkAvailable(networkUp);
+ }
+
+ /**
+ * Saves the state of this WebView used in
+ * {@link android.app.Activity#onSaveInstanceState}. Please note that this
+ * method no longer stores the display data for this WebView. The previous
+ * behavior could potentially leak files if {@link #restoreState} was never
+ * called.
+ *
+ * @param outState the Bundle to store this WebView's state
+ * @return the same copy of the back/forward list used to save the state, {@code null} if the
+ * method fails.
+ */
+ @Nullable
+ public WebBackForwardList saveState(Bundle outState) {
+ checkThread();
+ return mProvider.saveState(outState);
+ }
+
+ /**
+ * Saves the current display data to the Bundle given. Used in conjunction
+ * with {@link #saveState}.
+ * @param b a Bundle to store the display data
+ * @param dest the file to store the serialized picture data. Will be
+ * overwritten with this WebView's picture data.
+ * @return {@code true} if the picture was successfully saved
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
+ public boolean savePicture(Bundle b, final File dest) {
+ checkThread();
+ return mProvider.savePicture(b, dest);
+ }
+
+ /**
+ * Restores the display data that was saved in {@link #savePicture}. Used in
+ * conjunction with {@link #restoreState}. Note that this will not work if
+ * this WebView is hardware accelerated.
+ *
+ * @param b a Bundle containing the saved display data
+ * @param src the file where the picture data was stored
+ * @return {@code true} if the picture was successfully restored
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
+ public boolean restorePicture(Bundle b, File src) {
+ checkThread();
+ return mProvider.restorePicture(b, src);
+ }
+
+ /**
+ * Restores the state of this WebView from the given Bundle. This method is
+ * intended for use in {@link android.app.Activity#onRestoreInstanceState}
+ * and should be called to restore the state of this WebView. If
+ * it is called after this WebView has had a chance to build state (load
+ * pages, create a back/forward list, etc.) there may be undesirable
+ * side-effects. Please note that this method no longer restores the
+ * display data for this WebView.
+ *
+ * @param inState the incoming Bundle of state
+ * @return the restored back/forward list or {@code null} if restoreState failed
+ */
+ @Nullable
+ public WebBackForwardList restoreState(Bundle inState) {
+ checkThread();
+ return mProvider.restoreState(inState);
+ }
+
+ /**
+ * Loads the given URL with the specified additional HTTP headers.
+ * <p>
+ * Also see compatibility note on {@link #evaluateJavascript}.
+ *
+ * @param url the URL of the resource to load
+ * @param additionalHttpHeaders the additional headers to be used in the
+ * HTTP request for this URL, specified as a map from name to
+ * value. Note that if this map contains any of the headers
+ * that are set by default by this WebView, such as those
+ * controlling caching, accept types or the User-Agent, their
+ * values may be overridden by this WebView's defaults.
+ */
+ public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+ checkThread();
+ mProvider.loadUrl(url, additionalHttpHeaders);
+ }
+
+ /**
+ * Loads the given URL.
+ * <p>
+ * Also see compatibility note on {@link #evaluateJavascript}.
+ *
+ * @param url the URL of the resource to load
+ */
public void loadUrl(String url) {
+ checkThread();
+ mProvider.loadUrl(url);
+ }
+
+ /**
+ * Loads the URL with postData using "POST" method into this WebView. If url
+ * is not a network URL, it will be loaded with {@link #loadUrl(String)}
+ * instead, ignoring the postData param.
+ *
+ * @param url the URL of the resource to load
+ * @param postData the data will be passed to "POST" request, which must be
+ * be "application/x-www-form-urlencoded" encoded.
+ */
+ public void postUrl(String url, byte[] postData) {
+ checkThread();
+ if (URLUtil.isNetworkUrl(url)) {
+ mProvider.postUrl(url, postData);
+ } else {
+ mProvider.loadUrl(url);
+ }
}
- public void loadData(String data, String mimeType, String encoding) {
+ /**
+ * Loads the given data into this WebView using a 'data' scheme URL.
+ * <p>
+ * Note that JavaScript's same origin policy means that script running in a
+ * page loaded using this method will be unable to access content loaded
+ * using any scheme other than 'data', including 'http(s)'. To avoid this
+ * restriction, use {@link
+ * #loadDataWithBaseURL(String,String,String,String,String)
+ * loadDataWithBaseURL()} with an appropriate base URL.
+ * <p>
+ * The encoding parameter specifies whether the data is base64 or URL
+ * encoded. If the data is base64 encoded, the value of the encoding
+ * parameter must be 'base64'. For all other values of the parameter,
+ * including {@code null}, it is assumed that the data uses ASCII encoding for
+ * octets inside the range of safe URL characters and use the standard %xx
+ * hex encoding of URLs for octets outside that range. For example, '#',
+ * '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
+ * <p>
+ * The 'data' scheme URL formed by this method uses the default US-ASCII
+ * charset. If you need need to set a different charset, you should form a
+ * 'data' scheme URL which explicitly specifies a charset parameter in the
+ * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
+ * Note that the charset obtained from the mediatype portion of a data URL
+ * always overrides that specified in the HTML or XML document itself.
+ *
+ * @param data a String of data in the given encoding
+ * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
+ * defaults to 'text/html'.
+ * @param encoding the encoding of the data
+ */
+ public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
+ checkThread();
+ mProvider.loadData(data, mimeType, encoding);
}
- public void loadDataWithBaseURL(String baseUrl, String data,
- String mimeType, String encoding, String failUrl) {
+ /**
+ * Loads the given data into this WebView, using baseUrl as the base URL for
+ * the content. The base URL is used both to resolve relative URLs and when
+ * applying JavaScript's same origin policy. The historyUrl is used for the
+ * history entry.
+ * <p>
+ * Note that content specified in this way can access local device files
+ * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
+ * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
+ * <p>
+ * If the base URL uses the data scheme, this method is equivalent to
+ * calling {@link #loadData(String,String,String) loadData()} and the
+ * historyUrl is ignored, and the data will be treated as part of a data: URL.
+ * If the base URL uses any other scheme, then the data will be loaded into
+ * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
+ * entities in the string will not be decoded.
+ * <p>
+ * Note that the baseUrl is sent in the 'Referer' HTTP header when
+ * requesting subresources (images, etc.) of the page loaded using this method.
+ *
+ * @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to
+ * 'about:blank'.
+ * @param data a String of data in the given encoding
+ * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
+ * defaults to 'text/html'.
+ * @param encoding the encoding of the data
+ * @param historyUrl the URL to use as the history entry. If {@code null} defaults
+ * to 'about:blank'. If non-null, this must be a valid URL.
+ */
+ public void loadDataWithBaseURL(@Nullable String baseUrl, String data,
+ @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) {
+ checkThread();
+ mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
+ /**
+ * Asynchronously evaluates JavaScript in the context of the currently displayed page.
+ * If non-null, |resultCallback| will be invoked with any result returned from that
+ * execution. This method must be called on the UI thread and the callback will
+ * be made on the UI thread.
+ * <p>
+ * Compatibility note. Applications targeting {@link android.os.Build.VERSION_CODES#N} or
+ * later, JavaScript state from an empty WebView is no longer persisted across navigations like
+ * {@link #loadUrl(String)}. For example, global variables and functions defined before calling
+ * {@link #loadUrl(String)} will not exist in the loaded page. Applications should use
+ * {@link #addJavascriptInterface} instead to persist JavaScript objects across navigations.
+ *
+ * @param script the JavaScript to execute.
+ * @param resultCallback A callback to be invoked when the script execution
+ * completes with the result of the execution (if any).
+ * May be {@code null} if no notification of the result is required.
+ */
+ public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
+ checkThread();
+ mProvider.evaluateJavaScript(script, resultCallback);
+ }
+
+ /**
+ * Saves the current view as a web archive.
+ *
+ * @param filename the filename where the archive should be placed
+ */
+ public void saveWebArchive(String filename) {
+ checkThread();
+ mProvider.saveWebArchive(filename);
+ }
+
+ /**
+ * Saves the current view as a web archive.
+ *
+ * @param basename the filename where the archive should be placed
+ * @param autoname if {@code false}, takes basename to be a file. If {@code true}, basename
+ * is assumed to be a directory in which a filename will be
+ * chosen according to the URL of the current page.
+ * @param callback called after the web archive has been saved. The
+ * parameter for onReceiveValue will either be the filename
+ * under which the file was saved, or {@code null} if saving the
+ * file failed.
+ */
+ public void saveWebArchive(String basename, boolean autoname, @Nullable ValueCallback<String>
+ callback) {
+ checkThread();
+ mProvider.saveWebArchive(basename, autoname, callback);
+ }
+
+ /**
+ * Stops the current load.
+ */
public void stopLoading() {
+ checkThread();
+ mProvider.stopLoading();
}
+ /**
+ * Reloads the current URL.
+ */
public void reload() {
+ checkThread();
+ mProvider.reload();
}
+ /**
+ * Gets whether this WebView has a back history item.
+ *
+ * @return {@code true} iff this WebView has a back history item
+ */
public boolean canGoBack() {
- return false;
+ checkThread();
+ return mProvider.canGoBack();
}
+ /**
+ * Goes back in the history of this WebView.
+ */
public void goBack() {
+ checkThread();
+ mProvider.goBack();
}
+ /**
+ * Gets whether this WebView has a forward history item.
+ *
+ * @return {@code true} iff this WebView has a forward history item
+ */
public boolean canGoForward() {
- return false;
+ checkThread();
+ return mProvider.canGoForward();
}
+ /**
+ * Goes forward in the history of this WebView.
+ */
public void goForward() {
+ checkThread();
+ mProvider.goForward();
}
+ /**
+ * Gets whether the page can go back or forward the given
+ * number of steps.
+ *
+ * @param steps the negative or positive number of steps to move the
+ * history
+ */
public boolean canGoBackOrForward(int steps) {
- return false;
+ checkThread();
+ return mProvider.canGoBackOrForward(steps);
}
+ /**
+ * Goes to the history item that is the number of steps away from
+ * the current item. Steps is negative if backward and positive
+ * if forward.
+ *
+ * @param steps the number of steps to take back or forward in the back
+ * forward list
+ */
public void goBackOrForward(int steps) {
+ checkThread();
+ mProvider.goBackOrForward(steps);
+ }
+
+ /**
+ * Gets whether private browsing is enabled in this WebView.
+ */
+ public boolean isPrivateBrowsingEnabled() {
+ checkThread();
+ return mProvider.isPrivateBrowsingEnabled();
}
+ /**
+ * Scrolls the contents of this WebView up by half the view size.
+ *
+ * @param top {@code true} to jump to the top of the page
+ * @return {@code true} if the page was scrolled
+ */
public boolean pageUp(boolean top) {
- return false;
+ checkThread();
+ return mProvider.pageUp(top);
}
-
+
+ /**
+ * Scrolls the contents of this WebView down by half the page size.
+ *
+ * @param bottom {@code true} to jump to bottom of page
+ * @return {@code true} if the page was scrolled
+ */
public boolean pageDown(boolean bottom) {
- return false;
+ checkThread();
+ return mProvider.pageDown(bottom);
}
+ /**
+ * Posts a {@link VisualStateCallback}, which will be called when
+ * the current state of the WebView is ready to be drawn.
+ *
+ * <p>Because updates to the DOM are processed asynchronously, updates to the DOM may not
+ * immediately be reflected visually by subsequent {@link WebView#onDraw} invocations. The
+ * {@link VisualStateCallback} provides a mechanism to notify the caller when the contents of
+ * the DOM at the current time are ready to be drawn the next time the {@link WebView}
+ * draws.
+ *
+ * <p>The next draw after the callback completes is guaranteed to reflect all the updates to the
+ * DOM up to the point at which the {@link VisualStateCallback} was posted, but it may also
+ * contain updates applied after the callback was posted.
+ *
+ * <p>The state of the DOM covered by this API includes the following:
+ * <ul>
+ * <li>primitive HTML elements (div, img, span, etc..)</li>
+ * <li>images</li>
+ * <li>CSS animations</li>
+ * <li>WebGL</li>
+ * <li>canvas</li>
+ * </ul>
+ * It does not include the state of:
+ * <ul>
+ * <li>the video tag</li>
+ * </ul>
+ *
+ * <p>To guarantee that the {@link WebView} will successfully render the first frame
+ * after the {@link VisualStateCallback#onComplete} method has been called a set of conditions
+ * must be met:
+ * <ul>
+ * <li>If the {@link WebView}'s visibility is set to {@link View#VISIBLE VISIBLE} then
+ * the {@link WebView} must be attached to the view hierarchy.</li>
+ * <li>If the {@link WebView}'s visibility is set to {@link View#INVISIBLE INVISIBLE}
+ * then the {@link WebView} must be attached to the view hierarchy and must be made
+ * {@link View#VISIBLE VISIBLE} from the {@link VisualStateCallback#onComplete} method.</li>
+ * <li>If the {@link WebView}'s visibility is set to {@link View#GONE GONE} then the
+ * {@link WebView} must be attached to the view hierarchy and its
+ * {@link AbsoluteLayout.LayoutParams LayoutParams}'s width and height need to be set to fixed
+ * values and must be made {@link View#VISIBLE VISIBLE} from the
+ * {@link VisualStateCallback#onComplete} method.</li>
+ * </ul>
+ *
+ * <p>When using this API it is also recommended to enable pre-rasterization if the {@link
+ * WebView} is off screen to avoid flickering. See {@link WebSettings#setOffscreenPreRaster} for
+ * more details and do consider its caveats.
+ *
+ * @param requestId An id that will be returned in the callback to allow callers to match
+ * requests with callbacks.
+ * @param callback The callback to be invoked.
+ */
+ public void postVisualStateCallback(long requestId, VisualStateCallback callback) {
+ checkThread();
+ mProvider.insertVisualStateCallback(requestId, callback);
+ }
+
+ /**
+ * Clears this WebView so that onDraw() will draw nothing but white background,
+ * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY.
+ * @deprecated Use WebView.loadUrl("about:blank") to reliably reset the view state
+ * and release page resources (including any running JavaScript).
+ */
+ @Deprecated
public void clearView() {
+ checkThread();
+ mProvider.clearView();
}
-
+
+ /**
+ * Gets a new picture that captures the current contents of this WebView.
+ * The picture is of the entire document being displayed, and is not
+ * limited to the area currently displayed by this WebView. Also, the
+ * picture is a static copy and is unaffected by later changes to the
+ * content being displayed.
+ * <p>
+ * Note that due to internal changes, for API levels between
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and
+ * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} inclusive, the
+ * picture does not include fixed position elements or scrollable divs.
+ * <p>
+ * Note that from {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} the returned picture
+ * should only be drawn into bitmap-backed Canvas - using any other type of Canvas will involve
+ * additional conversion at a cost in memory and performance. Also the
+ * {@link android.graphics.Picture#createFromStream} and
+ * {@link android.graphics.Picture#writeToStream} methods are not supported on the
+ * returned object.
+ *
+ * @deprecated Use {@link #onDraw} to obtain a bitmap snapshot of the WebView, or
+ * {@link #saveWebArchive} to save the content to a file.
+ *
+ * @return a picture that captures the current contents of this WebView
+ */
+ @Deprecated
public Picture capturePicture() {
- return null;
+ checkThread();
+ return mProvider.capturePicture();
+ }
+
+ /**
+ * @deprecated Use {@link #createPrintDocumentAdapter(String)} which requires user
+ * to provide a print document name.
+ */
+ @Deprecated
+ public PrintDocumentAdapter createPrintDocumentAdapter() {
+ checkThread();
+ return mProvider.createPrintDocumentAdapter("default");
}
+ /**
+ * Creates a PrintDocumentAdapter that provides the content of this WebView for printing.
+ *
+ * The adapter works by converting the WebView contents to a PDF stream. The WebView cannot
+ * be drawn during the conversion process - any such draws are undefined. It is recommended
+ * to use a dedicated off screen WebView for the printing. If necessary, an application may
+ * temporarily hide a visible WebView by using a custom PrintDocumentAdapter instance
+ * wrapped around the object returned and observing the onStart and onFinish methods. See
+ * {@link android.print.PrintDocumentAdapter} for more information.
+ *
+ * @param documentName The user-facing name of the printed document. See
+ * {@link android.print.PrintDocumentInfo}
+ */
+ public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
+ checkThread();
+ return mProvider.createPrintDocumentAdapter(documentName);
+ }
+
+ /**
+ * Gets the current scale of this WebView.
+ *
+ * @return the current scale
+ *
+ * @deprecated This method is prone to inaccuracy due to race conditions
+ * between the web rendering and UI threads; prefer
+ * {@link WebViewClient#onScaleChanged}.
+ */
+ @Deprecated
+ @ViewDebug.ExportedProperty(category = "webview")
public float getScale() {
- return 0;
+ checkThread();
+ return mProvider.getScale();
}
+ /**
+ * Sets the initial scale for this WebView. 0 means default.
+ * The behavior for the default scale depends on the state of
+ * {@link WebSettings#getUseWideViewPort()} and
+ * {@link WebSettings#getLoadWithOverviewMode()}.
+ * If the content fits into the WebView control by width, then
+ * the zoom is set to 100%. For wide content, the behavior
+ * depends on the state of {@link WebSettings#getLoadWithOverviewMode()}.
+ * If its value is {@code true}, the content will be zoomed out to be fit
+ * by width into the WebView control, otherwise not.
+ *
+ * If initial scale is greater than 0, WebView starts with this value
+ * as initial scale.
+ * Please note that unlike the scale properties in the viewport meta tag,
+ * this method doesn't take the screen density into account.
+ *
+ * @param scaleInPercent the initial scale in percent
+ */
public void setInitialScale(int scaleInPercent) {
+ checkThread();
+ mProvider.setInitialScale(scaleInPercent);
}
+ /**
+ * Invokes the graphical zoom picker widget for this WebView. This will
+ * result in the zoom widget appearing on the screen to control the zoom
+ * level of this WebView.
+ */
public void invokeZoomPicker() {
+ checkThread();
+ mProvider.invokeZoomPicker();
+ }
+
+ /**
+ * Gets a HitTestResult based on the current cursor node. If a HTML::a
+ * tag is found and the anchor has a non-JavaScript URL, the HitTestResult
+ * type is set to SRC_ANCHOR_TYPE and the URL is set in the "extra" field.
+ * If the anchor does not have a URL or if it is a JavaScript URL, the type
+ * will be UNKNOWN_TYPE and the URL has to be retrieved through
+ * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
+ * found, the HitTestResult type is set to IMAGE_TYPE and the URL is set in
+ * the "extra" field. A type of
+ * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a URL that has an image as
+ * a child node. If a phone number is found, the HitTestResult type is set
+ * to PHONE_TYPE and the phone number is set in the "extra" field of
+ * HitTestResult. If a map address is found, the HitTestResult type is set
+ * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
+ * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
+ * and the email is set in the "extra" field of HitTestResult. Otherwise,
+ * HitTestResult type is set to UNKNOWN_TYPE.
+ */
+ public HitTestResult getHitTestResult() {
+ checkThread();
+ return mProvider.getHitTestResult();
}
- public void requestFocusNodeHref(Message hrefMsg) {
+ /**
+ * Requests the anchor or image element URL at the last tapped point.
+ * If hrefMsg is {@code null}, this method returns immediately and does not
+ * dispatch hrefMsg to its target. If the tapped point hits an image,
+ * an anchor, or an image in an anchor, the message associates
+ * strings in named keys in its data. The value paired with the key
+ * may be an empty string.
+ *
+ * @param hrefMsg the message to be dispatched with the result of the
+ * request. The message data contains three keys. "url"
+ * returns the anchor's href attribute. "title" returns the
+ * anchor's text. "src" returns the image's src attribute.
+ */
+ public void requestFocusNodeHref(@Nullable Message hrefMsg) {
+ checkThread();
+ mProvider.requestFocusNodeHref(hrefMsg);
}
+ /**
+ * Requests the URL of the image last touched by the user. msg will be sent
+ * to its target with a String representing the URL as its object.
+ *
+ * @param msg the message to be dispatched with the result of the request
+ * as the data member with "url" as key. The result can be {@code null}.
+ */
public void requestImageRef(Message msg) {
+ checkThread();
+ mProvider.requestImageRef(msg);
}
+ /**
+ * Gets the URL for the current page. This is not always the same as the URL
+ * passed to WebViewClient.onPageStarted because although the load for
+ * that URL has begun, the current page may not have changed.
+ *
+ * @return the URL for the current page
+ */
+ @ViewDebug.ExportedProperty(category = "webview")
public String getUrl() {
- return null;
+ checkThread();
+ return mProvider.getUrl();
}
+ /**
+ * Gets the original URL for the current page. This is not always the same
+ * as the URL passed to WebViewClient.onPageStarted because although the
+ * load for that URL has begun, the current page may not have changed.
+ * Also, there may have been redirects resulting in a different URL to that
+ * originally requested.
+ *
+ * @return the URL that was originally requested for the current page
+ */
+ @ViewDebug.ExportedProperty(category = "webview")
+ public String getOriginalUrl() {
+ checkThread();
+ return mProvider.getOriginalUrl();
+ }
+
+ /**
+ * Gets the title for the current page. This is the title of the current page
+ * until WebViewClient.onReceivedTitle is called.
+ *
+ * @return the title for the current page
+ */
+ @ViewDebug.ExportedProperty(category = "webview")
public String getTitle() {
- return null;
+ checkThread();
+ return mProvider.getTitle();
}
+ /**
+ * Gets the favicon for the current page. This is the favicon of the current
+ * page until WebViewClient.onReceivedIcon is called.
+ *
+ * @return the favicon for the current page
+ */
public Bitmap getFavicon() {
- return null;
+ checkThread();
+ return mProvider.getFavicon();
}
+ /**
+ * Gets the touch icon URL for the apple-touch-icon <link> element, or
+ * a URL on this site's server pointing to the standard location of a
+ * touch icon.
+ *
+ * @hide
+ */
+ public String getTouchIconUrl() {
+ return mProvider.getTouchIconUrl();
+ }
+
+ /**
+ * Gets the progress for the current page.
+ *
+ * @return the progress for the current page between 0 and 100
+ */
public int getProgress() {
- return 0;
+ checkThread();
+ return mProvider.getProgress();
}
-
+
+ /**
+ * Gets the height of the HTML content.
+ *
+ * @return the height of the HTML content
+ */
+ @ViewDebug.ExportedProperty(category = "webview")
public int getContentHeight() {
- return 0;
+ checkThread();
+ return mProvider.getContentHeight();
+ }
+
+ /**
+ * Gets the width of the HTML content.
+ *
+ * @return the width of the HTML content
+ * @hide
+ */
+ @ViewDebug.ExportedProperty(category = "webview")
+ public int getContentWidth() {
+ return mProvider.getContentWidth();
}
+ /**
+ * Pauses all layout, parsing, and JavaScript timers for all WebViews. This
+ * is a global requests, not restricted to just this WebView. This can be
+ * useful if the application has been paused.
+ */
public void pauseTimers() {
+ checkThread();
+ mProvider.pauseTimers();
}
+ /**
+ * Resumes all layout, parsing, and JavaScript timers for all WebViews.
+ * This will resume dispatching all timers.
+ */
public void resumeTimers() {
+ checkThread();
+ mProvider.resumeTimers();
+ }
+
+ /**
+ * Does a best-effort attempt to pause any processing that can be paused
+ * safely, such as animations and geolocation. Note that this call
+ * does not pause JavaScript. To pause JavaScript globally, use
+ * {@link #pauseTimers}.
+ *
+ * To resume WebView, call {@link #onResume}.
+ */
+ public void onPause() {
+ checkThread();
+ mProvider.onPause();
+ }
+
+ /**
+ * Resumes a WebView after a previous call to {@link #onPause}.
+ */
+ public void onResume() {
+ checkThread();
+ mProvider.onResume();
+ }
+
+ /**
+ * Gets whether this WebView is paused, meaning onPause() was called.
+ * Calling onResume() sets the paused state back to {@code false}.
+ *
+ * @hide
+ */
+ public boolean isPaused() {
+ return mProvider.isPaused();
+ }
+
+ /**
+ * Informs this WebView that memory is low so that it can free any available
+ * memory.
+ * @deprecated Memory caches are automatically dropped when no longer needed, and in response
+ * to system memory pressure.
+ */
+ @Deprecated
+ public void freeMemory() {
+ checkThread();
+ mProvider.freeMemory();
}
- public void clearCache() {
+ /**
+ * Clears the resource cache. Note that the cache is per-application, so
+ * this will clear the cache for all WebViews used.
+ *
+ * @param includeDiskFiles if {@code false}, only the RAM cache is cleared
+ */
+ public void clearCache(boolean includeDiskFiles) {
+ checkThread();
+ mProvider.clearCache(includeDiskFiles);
}
+ /**
+ * Removes the autocomplete popup from the currently focused form field, if
+ * present. Note this only affects the display of the autocomplete popup,
+ * it does not remove any saved form data from this WebView's store. To do
+ * that, use {@link WebViewDatabase#clearFormData}.
+ */
public void clearFormData() {
+ checkThread();
+ mProvider.clearFormData();
}
+ /**
+ * Tells this WebView to clear its internal back/forward list.
+ */
public void clearHistory() {
+ checkThread();
+ mProvider.clearHistory();
}
+ /**
+ * Clears the SSL preferences table stored in response to proceeding with
+ * SSL certificate errors.
+ */
public void clearSslPreferences() {
+ checkThread();
+ mProvider.clearSslPreferences();
+ }
+
+ /**
+ * Clears the client certificate preferences stored in response
+ * to proceeding/cancelling client cert requests. Note that WebView
+ * automatically clears these preferences when it receives a
+ * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
+ * shared by all the WebViews that are created by the embedder application.
+ *
+ * @param onCleared A runnable to be invoked when client certs are cleared.
+ * The runnable will be called in UI thread.
+ */
+ public static void clearClientCertPreferences(@Nullable Runnable onCleared) {
+ getFactory().getStatics().clearClientCertPreferences(onCleared);
+ }
+
+ /**
+ * Starts Safe Browsing initialization.
+ * <p>
+ * URL loads are not guaranteed to be protected by Safe Browsing until after {@code callback} is
+ * invoked with {@code true}. Safe Browsing is not fully supported on all devices. For those
+ * devices {@code callback} will receive {@code false}.
+ * <p>
+ * This does not enable the Safe Browsing feature itself, and should only be called if Safe
+ * Browsing is enabled by the manifest tag or {@link WebSettings#setSafeBrowsingEnabled}. This
+ * prepares resources used for Safe Browsing.
+ * <p>
+ * This should be called with the Application Context (and will always use the Application
+ * context to do its work regardless).
+ *
+ * @param context Application Context.
+ * @param callback will be called on the UI thread with {@code true} if initialization is
+ * successful, {@code false} otherwise.
+ */
+ public static void startSafeBrowsing(Context context,
+ @Nullable ValueCallback<Boolean> callback) {
+ getFactory().getStatics().initSafeBrowsing(context, callback);
+ }
+
+ /**
+ * Sets the list of domains that are exempt from SafeBrowsing checks. The list is
+ * global for all the WebViews.
+ * <p>
+ * Each rule should take one of these:
+ * <table>
+ * <tr><th> Rule </th> <th> Example </th> <th> Matches Subdomain</th> </tr>
+ * <tr><td> HOSTNAME </td> <td> example.com </td> <td> Yes </td> </tr>
+ * <tr><td> .HOSTNAME </td> <td> .example.com </td> <td> No </td> </tr>
+ * <tr><td> IPV4_LITERAL </td> <td> 192.168.1.1 </td> <td> No </td></tr>
+ * <tr><td> IPV6_LITERAL_WITH_BRACKETS </td><td>[10:20:30:40:50:60:70:80]</td><td>No</td></tr>
+ * </table>
+ * <p>
+ * All other rules, including wildcards, are invalid.
+ *
+ * @param urls the list of URLs
+ * @param callback will be called with {@code true} if URLs are successfully added to the
+ * whitelist. It will be called with {@code false} if any URLs are malformed. The callback will
+ * be run on the UI thread
+ */
+ public static void setSafeBrowsingWhitelist(@NonNull List<String> urls,
+ @Nullable ValueCallback<Boolean> callback) {
+ getFactory().getStatics().setSafeBrowsingWhitelist(urls, callback);
+ }
+
+ /**
+ * Returns a URL pointing to the privacy policy for Safe Browsing reporting.
+ *
+ * @return the url pointing to a privacy policy document which can be displayed to users.
+ */
+ @NonNull
+ public static Uri getSafeBrowsingPrivacyPolicyUrl() {
+ return getFactory().getStatics().getSafeBrowsingPrivacyPolicyUrl();
+ }
+
+ /**
+ * Gets the WebBackForwardList for this WebView. This contains the
+ * back/forward list for use in querying each item in the history stack.
+ * This is a copy of the private WebBackForwardList so it contains only a
+ * snapshot of the current state. Multiple calls to this method may return
+ * different objects. The object returned from this method will not be
+ * updated to reflect any new state.
+ */
+ public WebBackForwardList copyBackForwardList() {
+ checkThread();
+ return mProvider.copyBackForwardList();
+
+ }
+
+ /**
+ * Registers the listener to be notified as find-on-page operations
+ * progress. This will replace the current listener.
+ *
+ * @param listener an implementation of {@link FindListener}
+ */
+ public void setFindListener(FindListener listener) {
+ checkThread();
+ setupFindListenerIfNeeded();
+ mFindListener.mUserFindListener = listener;
+ }
+
+ /**
+ * Highlights and scrolls to the next match found by
+ * {@link #findAllAsync}, wrapping around page boundaries as necessary.
+ * Notifies any registered {@link FindListener}. If {@link #findAllAsync(String)}
+ * has not been called yet, or if {@link #clearMatches} has been called since the
+ * last find operation, this function does nothing.
+ *
+ * @param forward the direction to search
+ * @see #setFindListener
+ */
+ public void findNext(boolean forward) {
+ checkThread();
+ mProvider.findNext(forward);
+ }
+
+ /**
+ * Finds all instances of find on the page and highlights them.
+ * Notifies any registered {@link FindListener}.
+ *
+ * @param find the string to find
+ * @return the number of occurrences of the String "find" that were found
+ * @deprecated {@link #findAllAsync} is preferred.
+ * @see #setFindListener
+ */
+ @Deprecated
+ public int findAll(String find) {
+ checkThread();
+ StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
+ return mProvider.findAll(find);
+ }
+
+ /**
+ * Finds all instances of find on the page and highlights them,
+ * asynchronously. Notifies any registered {@link FindListener}.
+ * Successive calls to this will cancel any pending searches.
+ *
+ * @param find the string to find.
+ * @see #setFindListener
+ */
+ public void findAllAsync(String find) {
+ checkThread();
+ mProvider.findAllAsync(find);
+ }
+
+ /**
+ * Starts an ActionMode for finding text in this WebView. Only works if this
+ * WebView is attached to the view system.
+ *
+ * @param text if non-null, will be the initial text to search for.
+ * Otherwise, the last String searched for in this WebView will
+ * be used to start.
+ * @param showIme if {@code true}, show the IME, assuming the user will begin typing.
+ * If {@code false} and text is non-null, perform a find all.
+ * @return {@code true} if the find dialog is shown, {@code false} otherwise
+ * @deprecated This method does not work reliably on all Android versions;
+ * implementing a custom find dialog using WebView.findAllAsync()
+ * provides a more robust solution.
+ */
+ @Deprecated
+ public boolean showFindDialog(@Nullable String text, boolean showIme) {
+ checkThread();
+ return mProvider.showFindDialog(text, showIme);
}
+ /**
+ * Gets the first substring consisting of the address of a physical
+ * location. Currently, only addresses in the United States are detected,
+ * and consist of:
+ * <ul>
+ * <li>a house number</li>
+ * <li>a street name</li>
+ * <li>a street type (Road, Circle, etc), either spelled out or
+ * abbreviated</li>
+ * <li>a city name</li>
+ * <li>a state or territory, either spelled out or two-letter abbr</li>
+ * <li>an optional 5 digit or 9 digit zip code</li>
+ * </ul>
+ * All names must be correctly capitalized, and the zip code, if present,
+ * must be valid for the state. The street type must be a standard USPS
+ * spelling or abbreviation. The state or territory must also be spelled
+ * or abbreviated using USPS standards. The house number may not exceed
+ * five digits.
+ *
+ * @param addr the string to search for addresses
+ * @return the address, or if no address is found, {@code null}
+ */
+ @Nullable
public static String findAddress(String addr) {
- return null;
+ // TODO: Rewrite this in Java so it is not needed to start up chromium
+ // Could also be deprecated
+ return getFactory().getStatics().findAddress(addr);
+ }
+
+ /**
+ * For apps targeting the L release, WebView has a new default behavior that reduces
+ * memory footprint and increases performance by intelligently choosing
+ * the portion of the HTML document that needs to be drawn. These
+ * optimizations are transparent to the developers. However, under certain
+ * circumstances, an App developer may want to disable them:
+ * <ol>
+ * <li>When an app uses {@link #onDraw} to do own drawing and accesses portions
+ * of the page that is way outside the visible portion of the page.</li>
+ * <li>When an app uses {@link #capturePicture} to capture a very large HTML document.
+ * Note that capturePicture is a deprecated API.</li>
+ * </ol>
+ * Enabling drawing the entire HTML document has a significant performance
+ * cost. This method should be called before any WebViews are created.
+ */
+ public static void enableSlowWholeDocumentDraw() {
+ getFactory().getStatics().enableSlowWholeDocumentDraw();
}
+ /**
+ * Clears the highlighting surrounding text matches created by
+ * {@link #findAllAsync}.
+ */
+ public void clearMatches() {
+ checkThread();
+ mProvider.clearMatches();
+ }
+
+ /**
+ * Queries the document to see if it contains any image references. The
+ * message object will be dispatched with arg1 being set to 1 if images
+ * were found and 0 if the document does not reference any images.
+ *
+ * @param response the message that will be dispatched with the result
+ */
public void documentHasImages(Message response) {
+ checkThread();
+ mProvider.documentHasImages(response);
}
+ /**
+ * Sets the WebViewClient that will receive various notifications and
+ * requests. This will replace the current handler.
+ *
+ * @param client an implementation of WebViewClient
+ * @see #getWebViewClient
+ */
public void setWebViewClient(WebViewClient client) {
+ checkThread();
+ mProvider.setWebViewClient(client);
+ }
+
+ /**
+ * Gets the WebViewClient.
+ *
+ * @return the WebViewClient, or a default client if not yet set
+ * @see #setWebViewClient
+ */
+ public WebViewClient getWebViewClient() {
+ checkThread();
+ return mProvider.getWebViewClient();
}
+ /**
+ * Registers the interface to be used when content can not be handled by
+ * the rendering engine, and should be downloaded instead. This will replace
+ * the current handler.
+ *
+ * @param listener an implementation of DownloadListener
+ */
public void setDownloadListener(DownloadListener listener) {
+ checkThread();
+ mProvider.setDownloadListener(listener);
}
+ /**
+ * Sets the chrome handler. This is an implementation of WebChromeClient for
+ * use in handling JavaScript dialogs, favicons, titles, and the progress.
+ * This will replace the current handler.
+ *
+ * @param client an implementation of WebChromeClient
+ * @see #getWebChromeClient
+ */
public void setWebChromeClient(WebChromeClient client) {
+ checkThread();
+ mProvider.setWebChromeClient(client);
}
- public void addJavascriptInterface(Object obj, String interfaceName) {
+ /**
+ * Gets the chrome handler.
+ *
+ * @return the WebChromeClient, or {@code null} if not yet set
+ * @see #setWebChromeClient
+ */
+ @Nullable
+ public WebChromeClient getWebChromeClient() {
+ checkThread();
+ return mProvider.getWebChromeClient();
+ }
+
+ /**
+ * Sets the Picture listener. This is an interface used to receive
+ * notifications of a new Picture.
+ *
+ * @param listener an implementation of WebView.PictureListener
+ * @deprecated This method is now obsolete.
+ */
+ @Deprecated
+ public void setPictureListener(PictureListener listener) {
+ checkThread();
+ mProvider.setPictureListener(listener);
+ }
+
+ /**
+ * Injects the supplied Java object into this WebView. The object is
+ * injected into the JavaScript context of the main frame, using the
+ * supplied name. This allows the Java object's methods to be
+ * accessed from JavaScript. For applications targeted to API
+ * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ * and above, only public methods that are annotated with
+ * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
+ * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
+ * all public methods (including the inherited ones) can be accessed, see the
+ * important security note below for implications.
+ * <p> Note that injected objects will not appear in JavaScript until the page is next
+ * (re)loaded. JavaScript should be enabled before injecting the object. For example:
+ * <pre>
+ * class JsObject {
+ * {@literal @}JavascriptInterface
+ * public String toString() { return "injectedObject"; }
+ * }
+ * webview.getSettings().setJavaScriptEnabled(true);
+ * webView.addJavascriptInterface(new JsObject(), "injectedObject");
+ * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
+ * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
+ * <p>
+ * <strong>IMPORTANT:</strong>
+ * <ul>
+ * <li> This method can be used to allow JavaScript to control the host
+ * application. This is a powerful feature, but also presents a security
+ * risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier.
+ * Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+ * are still vulnerable if the app runs on a device running Android earlier than 4.2.
+ * The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ * and to ensure the method is called only when running on Android 4.2 or later.
+ * With these older versions, JavaScript could use reflection to access an
+ * injected object's public fields. Use of this method in a WebView
+ * containing untrusted content could allow an attacker to manipulate the
+ * host application in unintended ways, executing Java code with the
+ * permissions of the host application. Use extreme care when using this
+ * method in a WebView which could contain untrusted content.</li>
+ * <li> JavaScript interacts with Java object on a private, background
+ * thread of this WebView. Care is therefore required to maintain thread
+ * safety.
+ * </li>
+ * <li> The Java object's fields are not accessible.</li>
+ * <li> For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP}
+ * and above, methods of injected Java objects are enumerable from
+ * JavaScript.</li>
+ * </ul>
+ *
+ * @param object the Java object to inject into this WebView's JavaScript
+ * context. {@code null} values are ignored.
+ * @param name the name used to expose the object in JavaScript
+ */
+ public void addJavascriptInterface(Object object, String name) {
+ checkThread();
+ mProvider.addJavascriptInterface(object, name);
+ }
+
+ /**
+ * Removes a previously injected Java object from this WebView. Note that
+ * the removal will not be reflected in JavaScript until the page is next
+ * (re)loaded. See {@link #addJavascriptInterface}.
+ *
+ * @param name the name used to expose the object in JavaScript
+ */
+ public void removeJavascriptInterface(@NonNull String name) {
+ checkThread();
+ mProvider.removeJavascriptInterface(name);
+ }
+
+ /**
+ * Creates a message channel to communicate with JS and returns the message
+ * ports that represent the endpoints of this message channel. The HTML5 message
+ * channel functionality is described
+ * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
+ * </a>
+ *
+ * <p>The returned message channels are entangled and already in started state.
+ *
+ * @return the two message ports that form the message channel.
+ */
+ public WebMessagePort[] createWebMessageChannel() {
+ checkThread();
+ return mProvider.createWebMessageChannel();
+ }
+
+ /**
+ * Post a message to main frame. The embedded application can restrict the
+ * messages to a certain target origin. See
+ * <a href="https://html.spec.whatwg.org/multipage/comms.html#posting-messages">
+ * HTML5 spec</a> for how target origin can be used.
+ * <p>
+ * A target origin can be set as a wildcard ("*"). However this is not recommended.
+ * See the page above for security issues.
+ *
+ * @param message the WebMessage
+ * @param targetOrigin the target origin.
+ */
+ public void postWebMessage(WebMessage message, Uri targetOrigin) {
+ checkThread();
+ mProvider.postMessageToMainFrame(message, targetOrigin);
+ }
+
+ /**
+ * Gets the WebSettings object used to control the settings for this
+ * WebView.
+ *
+ * @return a WebSettings object that can be used to control this WebView's
+ * settings
+ */
+ public WebSettings getSettings() {
+ checkThread();
+ return mProvider.getSettings();
+ }
+
+ /**
+ * Enables debugging of web contents (HTML / CSS / JavaScript)
+ * loaded into any WebViews of this application. This flag can be enabled
+ * in order to facilitate debugging of web layouts and JavaScript
+ * code running inside WebViews. Please refer to WebView documentation
+ * for the debugging guide.
+ *
+ * The default is {@code false}.
+ *
+ * @param enabled whether to enable web contents debugging
+ */
+ public static void setWebContentsDebuggingEnabled(boolean enabled) {
+ getFactory().getStatics().setWebContentsDebuggingEnabled(enabled);
+ }
+
+ /**
+ * Gets the list of currently loaded plugins.
+ *
+ * @return the list of currently loaded plugins
+ * @deprecated This was used for Gears, which has been deprecated.
+ * @hide
+ */
+ @Deprecated
+ public static synchronized PluginList getPluginList() {
+ return new PluginList();
+ }
+
+ /**
+ * @deprecated This was used for Gears, which has been deprecated.
+ * @hide
+ */
+ @Deprecated
+ public void refreshPlugins(boolean reloadOpenPages) {
+ checkThread();
+ }
+
+ /**
+ * Puts this WebView into text selection mode. Do not rely on this
+ * functionality; it will be deprecated in the future.
+ *
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
+ public void emulateShiftHeld() {
+ checkThread();
+ }
+
+ /**
+ * @deprecated WebView no longer needs to implement
+ * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
+ */
+ @Override
+ // Cannot add @hide as this can always be accessed via the interface.
+ @Deprecated
+ public void onChildViewAdded(View parent, View child) {}
+
+ /**
+ * @deprecated WebView no longer needs to implement
+ * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
+ */
+ @Override
+ // Cannot add @hide as this can always be accessed via the interface.
+ @Deprecated
+ public void onChildViewRemoved(View p, View child) {}
+
+ /**
+ * @deprecated WebView should not have implemented
+ * ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.
+ */
+ @Override
+ // Cannot add @hide as this can always be accessed via the interface.
+ @Deprecated
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ }
+
+ /**
+ * @deprecated Only the default case, {@code true}, will be supported in a future version.
+ */
+ @Deprecated
+ public void setMapTrackballToArrowKeys(boolean setMap) {
+ checkThread();
+ mProvider.setMapTrackballToArrowKeys(setMap);
}
+
+ public void flingScroll(int vx, int vy) {
+ checkThread();
+ mProvider.flingScroll(vx, vy);
+ }
+
+ /**
+ * Gets the zoom controls for this WebView, as a separate View. The caller
+ * is responsible for inserting this View into the layout hierarchy.
+ * <p/>
+ * API level {@link android.os.Build.VERSION_CODES#CUPCAKE} introduced
+ * built-in zoom mechanisms for the WebView, as opposed to these separate
+ * zoom controls. The built-in mechanisms are preferred and can be enabled
+ * using {@link WebSettings#setBuiltInZoomControls}.
+ *
+ * @deprecated the built-in zoom mechanisms are preferred
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+ */
+ @Deprecated
public View getZoomControls() {
- return null;
+ checkThread();
+ return mProvider.getZoomControls();
}
+ /**
+ * Gets whether this WebView can be zoomed in.
+ *
+ * @return {@code true} if this WebView can be zoomed in
+ *
+ * @deprecated This method is prone to inaccuracy due to race conditions
+ * between the web rendering and UI threads; prefer
+ * {@link WebViewClient#onScaleChanged}.
+ */
+ @Deprecated
+ public boolean canZoomIn() {
+ checkThread();
+ return mProvider.canZoomIn();
+ }
+
+ /**
+ * Gets whether this WebView can be zoomed out.
+ *
+ * @return {@code true} if this WebView can be zoomed out
+ *
+ * @deprecated This method is prone to inaccuracy due to race conditions
+ * between the web rendering and UI threads; prefer
+ * {@link WebViewClient#onScaleChanged}.
+ */
+ @Deprecated
+ public boolean canZoomOut() {
+ checkThread();
+ return mProvider.canZoomOut();
+ }
+
+ /**
+ * Performs a zoom operation in this WebView.
+ *
+ * @param zoomFactor the zoom factor to apply. The zoom factor will be clamped to the WebView's
+ * zoom limits. This value must be in the range 0.01 to 100.0 inclusive.
+ */
+ public void zoomBy(float zoomFactor) {
+ checkThread();
+ if (zoomFactor < 0.01)
+ throw new IllegalArgumentException("zoomFactor must be greater than 0.01.");
+ if (zoomFactor > 100.0)
+ throw new IllegalArgumentException("zoomFactor must be less than 100.");
+ mProvider.zoomBy(zoomFactor);
+ }
+
+ /**
+ * Performs zoom in in this WebView.
+ *
+ * @return {@code true} if zoom in succeeds, {@code false} if no zoom changes
+ */
public boolean zoomIn() {
- return false;
+ checkThread();
+ return mProvider.zoomIn();
}
+ /**
+ * Performs zoom out in this WebView.
+ *
+ * @return {@code true} if zoom out succeeds, {@code false} if no zoom changes
+ */
public boolean zoomOut() {
- return false;
+ checkThread();
+ return mProvider.zoomOut();
+ }
+
+ /**
+ * @deprecated This method is now obsolete.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+ @Deprecated
+ public void debugDump() {
+ checkThread();
+ }
+
+ /**
+ * See {@link ViewDebug.HierarchyHandler#dumpViewHierarchyWithProperties(BufferedWriter, int)}
+ * @hide
+ */
+ @Override
+ public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
+ mProvider.dumpViewHierarchyWithProperties(out, level);
+ }
+
+ /**
+ * See {@link ViewDebug.HierarchyHandler#findHierarchyView(String, int)}
+ * @hide
+ */
+ @Override
+ public View findHierarchyView(String className, int hashCode) {
+ return mProvider.findHierarchyView(className, hashCode);
+ }
+
+ /** @hide */
+ @IntDef({
+ RENDERER_PRIORITY_WAIVED,
+ RENDERER_PRIORITY_BOUND,
+ RENDERER_PRIORITY_IMPORTANT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RendererPriority {}
+
+ /**
+ * The renderer associated with this WebView is bound with
+ * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
+ * {@link WebView} renderers will be strong targets for out of memory
+ * killing.
+ *
+ * Use with {@link #setRendererPriorityPolicy}.
+ */
+ public static final int RENDERER_PRIORITY_WAIVED = 0;
+ /**
+ * The renderer associated with this WebView is bound with
+ * the default priority for services.
+ *
+ * Use with {@link #setRendererPriorityPolicy}.
+ */
+ public static final int RENDERER_PRIORITY_BOUND = 1;
+ /**
+ * The renderer associated with this WebView is bound with
+ * {@link Context#BIND_IMPORTANT}.
+ *
+ * Use with {@link #setRendererPriorityPolicy}.
+ */
+ public static final int RENDERER_PRIORITY_IMPORTANT = 2;
+
+ /**
+ * Set the renderer priority policy for this {@link WebView}. The
+ * priority policy will be used to determine whether an out of
+ * process renderer should be considered to be a target for OOM
+ * killing.
+ *
+ * Because a renderer can be associated with more than one
+ * WebView, the final priority it is computed as the maximum of
+ * any attached WebViews. When a WebView is destroyed it will
+ * cease to be considerered when calculating the renderer
+ * priority. Once no WebViews remain associated with the renderer,
+ * the priority of the renderer will be reduced to
+ * {@link #RENDERER_PRIORITY_WAIVED}.
+ *
+ * The default policy is to set the priority to
+ * {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
+ * and this should not be changed unless the caller also handles
+ * renderer crashes with
+ * {@link WebViewClient#onRenderProcessGone}. Any other setting
+ * will result in WebView renderers being killed by the system
+ * more aggressively than the application.
+ *
+ * @param rendererRequestedPriority the minimum priority at which
+ * this WebView desires the renderer process to be bound.
+ * @param waivedWhenNotVisible if {@code true}, this flag specifies that
+ * when this WebView is not visible, it will be treated as
+ * if it had requested a priority of
+ * {@link #RENDERER_PRIORITY_WAIVED}.
+ */
+ public void setRendererPriorityPolicy(
+ @RendererPriority int rendererRequestedPriority,
+ boolean waivedWhenNotVisible) {
+ mProvider.setRendererPriorityPolicy(rendererRequestedPriority, waivedWhenNotVisible);
+ }
+
+ /**
+ * Get the requested renderer priority for this WebView.
+ *
+ * @return the requested renderer priority policy.
+ */
+ @RendererPriority
+ public int getRendererRequestedPriority() {
+ return mProvider.getRendererRequestedPriority();
+ }
+
+ /**
+ * Return whether this WebView requests a priority of
+ * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+ *
+ * @return whether this WebView requests a priority of
+ * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+ */
+ public boolean getRendererPriorityWaivedWhenNotVisible() {
+ return mProvider.getRendererPriorityWaivedWhenNotVisible();
+ }
+
+ /**
+ * Sets the {@link TextClassifier} for this WebView.
+ */
+ public void setTextClassifier(@Nullable TextClassifier textClassifier) {
+ mProvider.setTextClassifier(textClassifier);
+ }
+
+ /**
+ * Returns the {@link TextClassifier} used by this WebView.
+ * If no TextClassifier has been set, this WebView uses the default set by the system.
+ */
+ @NonNull
+ public TextClassifier getTextClassifier() {
+ return mProvider.getTextClassifier();
+ }
+
+ //-------------------------------------------------------------------------
+ // Interface for WebView providers
+ //-------------------------------------------------------------------------
+
+ /**
+ * Gets the WebViewProvider. Used by providers to obtain the underlying
+ * implementation, e.g. when the application responds to
+ * WebViewClient.onCreateWindow() request.
+ *
+ * @hide WebViewProvider is not public API.
+ */
+ @SystemApi
+ public WebViewProvider getWebViewProvider() {
+ return mProvider;
+ }
+
+ /**
+ * Callback interface, allows the provider implementation to access non-public methods
+ * and fields, and make super-class calls in this WebView instance.
+ * @hide Only for use by WebViewProvider implementations
+ */
+ @SystemApi
+ public class PrivateAccess {
+ // ---- Access to super-class methods ----
+ public int super_getScrollBarStyle() {
+ return WebView.super.getScrollBarStyle();
+ }
+
+ public void super_scrollTo(int scrollX, int scrollY) {
+ WebView.super.scrollTo(scrollX, scrollY);
+ }
+
+ public void super_computeScroll() {
+ WebView.super.computeScroll();
+ }
+
+ public boolean super_onHoverEvent(MotionEvent event) {
+ return WebView.super.onHoverEvent(event);
+ }
+
+ public boolean super_performAccessibilityAction(int action, Bundle arguments) {
+ return WebView.super.performAccessibilityActionInternal(action, arguments);
+ }
+
+ public boolean super_performLongClick() {
+ return WebView.super.performLongClick();
+ }
+
+ public boolean super_setFrame(int left, int top, int right, int bottom) {
+ return WebView.super.setFrame(left, top, right, bottom);
+ }
+
+ public boolean super_dispatchKeyEvent(KeyEvent event) {
+ return WebView.super.dispatchKeyEvent(event);
+ }
+
+ public boolean super_onGenericMotionEvent(MotionEvent event) {
+ return WebView.super.onGenericMotionEvent(event);
+ }
+
+ public boolean super_requestFocus(int direction, Rect previouslyFocusedRect) {
+ return WebView.super.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ public void super_setLayoutParams(ViewGroup.LayoutParams params) {
+ WebView.super.setLayoutParams(params);
+ }
+
+ public void super_startActivityForResult(Intent intent, int requestCode) {
+ WebView.super.startActivityForResult(intent, requestCode);
+ }
+
+ // ---- Access to non-public methods ----
+ public void overScrollBy(int deltaX, int deltaY,
+ int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY,
+ int maxOverScrollX, int maxOverScrollY,
+ boolean isTouchEvent) {
+ WebView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+ maxOverScrollX, maxOverScrollY, isTouchEvent);
+ }
+
+ public void awakenScrollBars(int duration) {
+ WebView.this.awakenScrollBars(duration);
+ }
+
+ public void awakenScrollBars(int duration, boolean invalidate) {
+ WebView.this.awakenScrollBars(duration, invalidate);
+ }
+
+ public float getVerticalScrollFactor() {
+ return WebView.this.getVerticalScrollFactor();
+ }
+
+ public float getHorizontalScrollFactor() {
+ return WebView.this.getHorizontalScrollFactor();
+ }
+
+ public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+ WebView.this.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ WebView.this.onScrollChanged(l, t, oldl, oldt);
+ }
+
+ public int getHorizontalScrollbarHeight() {
+ return WebView.this.getHorizontalScrollbarHeight();
+ }
+
+ public void super_onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
+ WebView.super.onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+ }
+
+ // ---- Access to (non-public) fields ----
+ /** Raw setter for the scroll X value, without invoking onScrollChanged handlers etc. */
+ public void setScrollXRaw(int scrollX) {
+ WebView.this.mScrollX = scrollX;
+ }
+
+ /** Raw setter for the scroll Y value, without invoking onScrollChanged handlers etc. */
+ public void setScrollYRaw(int scrollY) {
+ WebView.this.mScrollY = scrollY;
+ }
+
+ }
+
+ //-------------------------------------------------------------------------
+ // Package-private internal stuff
+ //-------------------------------------------------------------------------
+
+ // Only used by android.webkit.FindActionModeCallback.
+ void setFindDialogFindListener(FindListener listener) {
+ checkThread();
+ setupFindListenerIfNeeded();
+ mFindListener.mFindDialogFindListener = listener;
+ }
+
+ // Only used by android.webkit.FindActionModeCallback.
+ void notifyFindDialogDismissed() {
+ checkThread();
+ mProvider.notifyFindDialogDismissed();
+ }
+
+ //-------------------------------------------------------------------------
+ // Private internal stuff
+ //-------------------------------------------------------------------------
+
+ private WebViewProvider mProvider;
+
+ /**
+ * In addition to the FindListener that the user may set via the WebView.setFindListener
+ * API, FindActionModeCallback will register it's own FindListener. We keep them separate
+ * via this class so that the two FindListeners can potentially exist at once.
+ */
+ private class FindListenerDistributor implements FindListener {
+ private FindListener mFindDialogFindListener;
+ private FindListener mUserFindListener;
+
+ @Override
+ public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
+ boolean isDoneCounting) {
+ if (mFindDialogFindListener != null) {
+ mFindDialogFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
+ isDoneCounting);
+ }
+
+ if (mUserFindListener != null) {
+ mUserFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
+ isDoneCounting);
+ }
+ }
+ }
+ private FindListenerDistributor mFindListener;
+
+ private void setupFindListenerIfNeeded() {
+ if (mFindListener == null) {
+ mFindListener = new FindListenerDistributor();
+ mProvider.setFindListener(mFindListener);
+ }
+ }
+
+ private void ensureProviderCreated() {
+ checkThread();
+ if (mProvider == null) {
+ // As this can get called during the base class constructor chain, pass the minimum
+ // number of dependencies here; the rest are deferred to init().
+ mProvider = getFactory().createWebView(this, new PrivateAccess());
+ }
+ }
+
+ private static WebViewFactoryProvider getFactory() {
+ return WebViewFactory.getProvider();
+ }
+
+ private final Looper mWebViewThread = Looper.myLooper();
+
+ private void checkThread() {
+ // Ignore mWebViewThread == null because this can be called during in the super class
+ // constructor, before this class's own constructor has even started.
+ if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) {
+ Throwable throwable = new Throwable(
+ "A WebView method was called on thread '" +
+ Thread.currentThread().getName() + "'. " +
+ "All WebView methods must be called on the same thread. " +
+ "(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() +
+ ", FYI main Looper is " + Looper.getMainLooper() + ")");
+ Log.w(LOGTAG, Log.getStackTraceString(throwable));
+ StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
+
+ if (sEnforceThreadChecking) {
+ throw new RuntimeException(throwable);
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Override View methods
+ //-------------------------------------------------------------------------
+
+ // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures
+ // there's a corresponding override (or better, caller) for each of them in here.
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mProvider.getViewDelegate().onAttachedToWindow();
+ }
+
+ /** @hide */
+ @Override
+ protected void onDetachedFromWindowInternal() {
+ mProvider.getViewDelegate().onDetachedFromWindow();
+ super.onDetachedFromWindowInternal();
+ }
+
+ /** @hide */
+ @Override
+ public void onMovedToDisplay(int displayId, Configuration config) {
+ mProvider.getViewDelegate().onMovedToDisplay(displayId, config);
+ }
+
+ @Override
+ public void setLayoutParams(ViewGroup.LayoutParams params) {
+ mProvider.getViewDelegate().setLayoutParams(params);
+ }
+
+ @Override
+ public void setOverScrollMode(int mode) {
+ super.setOverScrollMode(mode);
+ // This method may be called in the constructor chain, before the WebView provider is
+ // created.
+ ensureProviderCreated();
+ mProvider.getViewDelegate().setOverScrollMode(mode);
+ }
+
+ @Override
+ public void setScrollBarStyle(int style) {
+ mProvider.getViewDelegate().setScrollBarStyle(style);
+ super.setScrollBarStyle(style);
+ }
+
+ @Override
+ protected int computeHorizontalScrollRange() {
+ return mProvider.getScrollDelegate().computeHorizontalScrollRange();
+ }
+
+ @Override
+ protected int computeHorizontalScrollOffset() {
+ return mProvider.getScrollDelegate().computeHorizontalScrollOffset();
+ }
+
+ @Override
+ protected int computeVerticalScrollRange() {
+ return mProvider.getScrollDelegate().computeVerticalScrollRange();
+ }
+
+ @Override
+ protected int computeVerticalScrollOffset() {
+ return mProvider.getScrollDelegate().computeVerticalScrollOffset();
+ }
+
+ @Override
+ protected int computeVerticalScrollExtent() {
+ return mProvider.getScrollDelegate().computeVerticalScrollExtent();
+ }
+
+ @Override
+ public void computeScroll() {
+ mProvider.getScrollDelegate().computeScroll();
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ return mProvider.getViewDelegate().onHoverEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mProvider.getViewDelegate().onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return mProvider.getViewDelegate().onGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ return mProvider.getViewDelegate().onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event);
+ }
+
+ /*
+ TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not
+ to be delegating them too.
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyPreIme(keyCode, event);
+ }
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyLongPress(keyCode, event);
+ }
+ @Override
+ public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+ return mProvider.getViewDelegate().onKeyShortcut(keyCode, event);
+ }
+ */
+
+ @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ AccessibilityNodeProvider provider =
+ mProvider.getViewDelegate().getAccessibilityNodeProvider();
+ return provider == null ? super.getAccessibilityNodeProvider() : provider;
+ }
+
+ @Deprecated
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return mProvider.getViewDelegate().shouldDelayChildPressedState();
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return WebView.class.getName();
+ }
+
+ @Override
+ public void onProvideVirtualStructure(ViewStructure structure) {
+ mProvider.getViewDelegate().onProvideVirtualStructure(structure);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The {@link ViewStructure} traditionally represents a {@link View}, while for web pages
+ * it represent HTML nodes. Hence, it's necessary to "map" the HTML properties in a way that is
+ * understood by the {@link android.service.autofill.AutofillService} implementations:
+ *
+ * <ol>
+ * <li>Only the HTML nodes inside a {@code FORM} are generated.
+ * <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the
+ * node representing the WebView.
+ * <li>If a web page has multiple {@code FORM}s, only the data for the current form is
+ * represented&mdash;if the user taps a field from another form, then the current autofill
+ * context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and
+ * a new context is created for that {@code FORM}.
+ * <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in
+ * the view structure until the user taps a field from a {@code FORM} inside the
+ * {@code IFRAME}, in which case it would be treated the same way as multiple forms described
+ * above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
+ * {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
+ * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
+ * {@link ViewStructure#setAutofillHints(String[])}.
+ * <li>If the view is editable, the {@link ViewStructure#setAutofillType(int)} and
+ * {@link ViewStructure#setAutofillValue(AutofillValue)} must be set.
+ * <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}.
+ * <li>Other HTML attributes can be represented through
+ * {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}.
+ * </ol>
+ *
+ * <p>If the WebView implementation can determine that the value of a field was set statically
+ * (for example, not through Javascript), it should also call
+ * {@code structure.setDataIsSensitive(false)}.
+ *
+ * <p>For example, an HTML form with 2 fields for username and password:
+ *
+ * <pre class="prettyprint">
+ * &lt;input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username"&gt;
+ * &lt;input type="password" name="password" id="pass" autocomplete="current-password" placeholder="Password"&gt;
+ * </pre>
+ *
+ * <p>Would map to:
+ *
+ * <pre class="prettyprint">
+ * int index = structure.addChildCount(2);
+ * ViewStructure username = structure.newChild(index);
+ * username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child
+ * username.setAutofillHints("username");
+ * username.setHtmlInfo(username.newHtmlInfoBuilder("input")
+ * .addAttribute("type", "text")
+ * .addAttribute("name", "username")
+ * .build());
+ * username.setHint("Email or username");
+ * username.setAutofillType(View.AUTOFILL_TYPE_TEXT);
+ * username.setAutofillValue(AutofillValue.forText("Type your username"));
+ * // Value of the field is not sensitive because it was created statically and not changed.
+ * username.setDataIsSensitive(false);
+ *
+ * ViewStructure password = structure.newChild(index + 1);
+ * username.setAutofillId(structure, 2); // id 2 - second child
+ * password.setAutofillHints("current-password");
+ * password.setHtmlInfo(password.newHtmlInfoBuilder("input")
+ * .addAttribute("type", "password")
+ * .addAttribute("name", "password")
+ * .build());
+ * password.setHint("Password");
+ * password.setAutofillType(View.AUTOFILL_TYPE_TEXT);
+ * </pre>
+ */
+ @Override
+ public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
+ mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
+ }
+
+ @Override
+ public void autofill(SparseArray<AutofillValue>values) {
+ mProvider.getViewDelegate().autofill(values);
+ }
+
+ /** @hide */
+ @Override
+ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoInternal(info);
+ mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
+ }
+
+ /** @hide */
+ @Override
+ public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEventInternal(event);
+ mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
+ }
+
+ /** @hide */
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ return mProvider.getViewDelegate().performAccessibilityAction(action, arguments);
+ }
+
+ /** @hide */
+ @Override
+ protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
+ mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+ }
+
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+ mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mProvider.getViewDelegate().onWindowVisibilityChanged(visibility);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mProvider.getViewDelegate().onDraw(canvas);
+ }
+
+ @Override
+ public boolean performLongClick() {
+ return mProvider.getViewDelegate().performLongClick();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ mProvider.getViewDelegate().onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Creates a new InputConnection for an InputMethod to interact with the WebView.
+ * This is similar to {@link View#onCreateInputConnection} but note that WebView
+ * calls InputConnection methods on a thread other than the UI thread.
+ * If these methods are overridden, then the overriding methods should respect
+ * thread restrictions when calling View methods or accessing data.
+ */
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ return mProvider.getViewDelegate().onDragEvent(event);
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // This method may be called in the constructor chain, before the WebView provider is
+ // created.
+ ensureProviderCreated();
+ mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus);
+ super.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect);
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ }
+
+ /** @hide */
+ @Override
+ protected boolean setFrame(int left, int top, int right, int bottom) {
+ return mProvider.getViewDelegate().setFrame(left, top, right, bottom);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int ow, int oh) {
+ super.onSizeChanged(w, h, ow, oh);
+ mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh);
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return mProvider.getViewDelegate().dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
+ return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate);
+ }
+
+ @Override
+ public void setBackgroundColor(int color) {
+ mProvider.getViewDelegate().setBackgroundColor(color);
+ }
+
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ super.setLayerType(layerType, paint);
+ mProvider.getViewDelegate().setLayerType(layerType, paint);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mProvider.getViewDelegate().preDispatchDraw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ public void onStartTemporaryDetach() {
+ super.onStartTemporaryDetach();
+ mProvider.getViewDelegate().onStartTemporaryDetach();
+ }
+
+ @Override
+ public void onFinishTemporaryDetach() {
+ super.onFinishTemporaryDetach();
+ mProvider.getViewDelegate().onFinishTemporaryDetach();
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mProvider.getViewDelegate().getHandler(super.getHandler());
+ }
+
+ @Override
+ public View findFocus() {
+ return mProvider.getViewDelegate().findFocus(super.findFocus());
+ }
+
+ /**
+ * If WebView has already been loaded into the current process this method will return the
+ * package that was used to load it. Otherwise, the package that would be used if the WebView
+ * was loaded right now will be returned; this does not cause WebView to be loaded, so this
+ * information may become outdated at any time.
+ * The WebView package changes either when the current WebView package is updated, disabled, or
+ * uninstalled. It can also be changed through a Developer Setting.
+ * If the WebView package changes, any app process that has loaded WebView will be killed. The
+ * next time the app starts and loads WebView it will use the new WebView package instead.
+ * @return the current WebView package, or {@code null} if there is none.
+ */
+ @Nullable
+ public static PackageInfo getCurrentWebViewPackage() {
+ PackageInfo webviewPackage = WebViewFactory.getLoadedPackageInfo();
+ if (webviewPackage != null) {
+ return webviewPackage;
+ }
+
+ IWebViewUpdateService service = WebViewFactory.getUpdateService();
+ if (service == null) {
+ return null;
+ }
+ try {
+ return service.getCurrentWebViewPackage();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @hide
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
+ }
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ checkThread();
+ encoder.addProperty("webview:contentHeight", mProvider.getContentHeight());
+ encoder.addProperty("webview:contentWidth", mProvider.getContentWidth());
+ encoder.addProperty("webview:scale", mProvider.getScale());
+ encoder.addProperty("webview:title", mProvider.getTitle());
+ encoder.addProperty("webview:url", mProvider.getUrl());
+ encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl());
}
}
diff --git a/android/widget/Editor.java b/android/widget/Editor.java
index d4be7e57..afd11881 100644
--- a/android/widget/Editor.java
+++ b/android/widget/Editor.java
@@ -165,7 +165,7 @@ public class Editor {
private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11;
private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
- private static final float MAGNIFIER_ZOOM = 1.25f;
+ private static final float MAGNIFIER_ZOOM = 1.5f;
@IntDef({MagnifierHandleTrigger.SELECTION_START,
MagnifierHandleTrigger.SELECTION_END,
MagnifierHandleTrigger.INSERTION})
@@ -476,17 +476,6 @@ public class Editor {
stopTextActionModeWithPreservingSelection();
}
- void invalidateMagnifier() {
- final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics();
- invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels);
- }
-
- void invalidateMagnifier(final float l, final float t, final float r, final float b) {
- if (mMagnifier != null) {
- mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b)));
- }
- }
-
private void discardTextDisplayLists() {
if (mTextRenderNodes != null) {
for (int i = 0; i < mTextRenderNodes.length; i++) {
@@ -4556,17 +4545,17 @@ public class Editor {
+ mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f;
final int[] coordinatesOnScreen = new int[2];
mTextView.getLocationOnScreen(coordinatesOnScreen);
- final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true);
- final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false);
+ final float centerXOnScreen = xPosInView + mTextView.getTotalPaddingLeft()
+ - mTextView.getScrollX() + coordinatesOnScreen[0];
+ final float centerYOnScreen = yPosInView + mTextView.getTotalPaddingTop()
+ - mTextView.getScrollY() + coordinatesOnScreen[1];
- suspendBlink();
mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM);
}
protected final void dismissMagnifier() {
if (mMagnifier != null) {
mMagnifier.dismiss();
- resumeBlink();
}
}
diff --git a/android/widget/RemoteViews.java b/android/widget/RemoteViews.java
index 199b596a..1b26f8e2 100644
--- a/android/widget/RemoteViews.java
+++ b/android/widget/RemoteViews.java
@@ -2653,11 +2653,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Equivalent to calling
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
- * to launch the provided {@link PendingIntent}. The source bounds
- * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
- * view in screen space.
- * Note that any activity options associated with the pendingIntent may get overridden
- * before starting the intent.
+ * to launch the provided {@link PendingIntent}.
*
* When setting the on-click action of items within collections (eg. {@link ListView},
* {@link StackView} etc.), this method will not work. Instead, use {@link
diff --git a/android/widget/SelectionActionModeHelper.java b/android/widget/SelectionActionModeHelper.java
index 5e22650a..3be42a5b 100644
--- a/android/widget/SelectionActionModeHelper.java
+++ b/android/widget/SelectionActionModeHelper.java
@@ -95,15 +95,11 @@ public final class SelectionActionModeHelper {
}
public void startActionModeAsync(boolean adjustSelection) {
- // Check if the smart selection should run for editable text.
- adjustSelection &= !mTextView.isTextEditable()
- || mTextView.getTextClassifier().getSettings()
- .isSuggestSelectionEnabledForEditableText();
-
mSelectionTracker.onOriginalSelection(
getText(mTextView),
mTextView.getSelectionStart(),
- mTextView.getSelectionEnd());
+ mTextView.getSelectionEnd(),
+ mTextView.isTextEditable());
cancelAsyncTask();
if (skipTextClassification()) {
startActionMode(null);
@@ -200,10 +196,7 @@ public final class SelectionActionModeHelper {
private void startActionMode(@Nullable SelectionResult result) {
final CharSequence text = getText(mTextView);
if (result != null && text instanceof Spannable) {
- // Do not change the selection if TextClassifier should be dark launched.
- if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
- Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
- }
+ Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextClassification = result.mClassification;
} else {
mTextClassification = null;
@@ -384,7 +377,7 @@ public final class SelectionActionModeHelper {
}
private void resetTextClassificationHelper() {
- mTextClassificationHelper.init(
+ mTextClassificationHelper.reset(
mTextView.getTextClassifier(),
getText(mTextView),
mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
@@ -422,7 +415,8 @@ public final class SelectionActionModeHelper {
/**
* Called when the original selection happens, before smart selection is triggered.
*/
- public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) {
+ public void onOriginalSelection(
+ CharSequence text, int selectionStart, int selectionEnd, boolean editableText) {
// If we abandoned a selection and created a new one very shortly after, we may still
// have a pending request to log ABANDON, which we flush here.
mDelayedLogAbandon.flush();
@@ -818,11 +812,11 @@ public final class SelectionActionModeHelper {
TextClassificationHelper(TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- init(textClassifier, text, selectionStart, selectionEnd, locales);
+ reset(textClassifier, text, selectionStart, selectionEnd, locales);
}
@UiThread
- public void init(TextClassifier textClassifier,
+ public void reset(TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
@@ -845,12 +839,8 @@ public final class SelectionActionModeHelper {
trimText();
final TextSelection selection = mTextClassifier.suggestSelection(
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
- // Do not classify new selection boundaries if TextClassifier should be dark launched.
- if (!mTextClassifier.getSettings().isDarkLaunch()) {
- mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
- mSelectionEnd = Math.min(
- mText.length(), selection.getSelectionEndIndex() + mTrimStart);
- }
+ mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
+ mSelectionEnd = Math.min(mText.length(), selection.getSelectionEndIndex() + mTrimStart);
return performClassification(selection);
}
diff --git a/android/widget/TextView.java b/android/widget/TextView.java
index ce805526..24ae03c3 100644
--- a/android/widget/TextView.java
+++ b/android/widget/TextView.java
@@ -9219,36 +9219,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- @Override
- public void invalidate() {
- super.invalidate();
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier();
- }
- }
-
- @Override
- public void invalidate(int l, int t, int r, int b) {
- super.invalidate(l, t, r, b);
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier(
- convertViewToScreenCoord(l, true /* isHorizontal */),
- convertViewToScreenCoord(t, false /* isHorizontal */),
- convertViewToScreenCoord(r, true /* isHorizontal */),
- convertViewToScreenCoord(b, false /* isHorizontal */));
- }
- }
-
- float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) {
- final int[] coordinatesOnScreen = new int[2];
- getLocationOnScreen(coordinatesOnScreen);
- return isHorizontal
- ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0]
- : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1];
- }
-
/**
* @return whether or not the cursor is visible (assuming this TextView is editable)
*
@@ -10368,17 +10338,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
structure.setTextStyle(getTextSize(), getCurrentTextColor(),
AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
- } else {
- structure.setMinTextEms(getMinEms());
- structure.setMaxTextEms(getMaxEms());
- int maxLength = -1;
- for (InputFilter filter: getFilters()) {
- if (filter instanceof InputFilter.LengthFilter) {
- maxLength = ((InputFilter.LengthFilter) filter).getMax();
- break;
- }
- }
- structure.setMaxTextLength(maxLength);
}
}
structure.setHint(getHint());
diff --git a/com/android/commands/pm/Pm.java b/com/android/commands/pm/Pm.java
index 60ec8a96..c5c38f53 100644
--- a/com/android/commands/pm/Pm.java
+++ b/com/android/commands/pm/Pm.java
@@ -1570,19 +1570,11 @@ public final class Pm {
private static int showUsage() {
System.err.println("usage: pm path [--user USER_ID] PACKAGE");
System.err.println(" pm dump PACKAGE");
- System.err.println(" pm install [-lrtsfdg] [-i PACKAGE] [--user USER_ID]");
- System.err.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- System.err.println(" [--originating-uri URI] [---referrer URI]");
- System.err.println(" [--abi ABI_NAME] [--force-sdk]");
- System.err.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- System.err.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
- System.err.println(" pm install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID]");
- System.err.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
- System.err.println(" [--originating-uri URI] [---referrer URI]");
- System.err.println(" [--abi ABI_NAME] [--force-sdk]");
- System.err.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- System.err.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
- System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]");
+ System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]");
+ System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
+ System.err.println(" [--install-location 0/1/2]");
+ System.err.println(" [--force-uuid internal|UUID]");
+ System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
System.err.println(" pm install-commit SESSION_ID");
System.err.println(" pm install-abandon SESSION_ID");
System.err.println(" pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE");
@@ -1621,27 +1613,15 @@ public final class Pm {
System.err.println("pm install: install a single legacy package");
System.err.println("pm install-create: create an install session");
System.err.println(" -l: forward lock application");
- System.err.println(" -r: allow replacement of existing application");
+ System.err.println(" -r: replace existing application");
System.err.println(" -t: allow test packages");
- System.err.println(" -i: specify package name of installer owning the app");
+ System.err.println(" -i: specify the installer package name");
System.err.println(" -s: install application on sdcard");
System.err.println(" -f: install application on internal flash");
System.err.println(" -d: allow version code downgrade (debuggable packages only)");
- System.err.println(" -p: partial application install (new split on top of existing pkg)");
+ System.err.println(" -p: partial application install");
System.err.println(" -g: grant all runtime permissions");
System.err.println(" -S: size in bytes of entire session");
- System.err.println(" --dont-kill: installing a new feature split, don't kill running app");
- System.err.println(" --originating-uri: set URI where app was downloaded from");
- System.err.println(" --referrer: set URI that instigated the install of the app");
- System.err.println(" --pkg: specify expected package name of app being installed");
- System.err.println(" --abi: override the default ABI of the platform");
- System.err.println(" --instantapp: cause the app to be installed as an ephemeral install app");
- System.err.println(" --full: cause the app to be installed as a non-ephemeral full app");
- System.err.println(" --install-location: force the install location:");
- System.err.println(" 0=auto, 1=internal only, 2=prefer external");
- System.err.println(" --force-uuid: force install on to disk volume with given UUID");
- System.err.println(" --force-sdk: allow install even when existing app targets platform");
- System.err.println(" codename but new one targets a final API level");
System.err.println("");
System.err.println("pm install-write: write a package into existing session; path may");
System.err.println(" be '-' to read from stdin");
diff --git a/com/android/ex/photo/ActionBarWrapper.java b/com/android/ex/photo/ActionBarWrapper.java
index 6d4d4d20..ae621979 100644
--- a/com/android/ex/photo/ActionBarWrapper.java
+++ b/com/android/ex/photo/ActionBarWrapper.java
@@ -1,7 +1,8 @@
package com.android.ex.photo;
-import android.app.ActionBar;
+
import android.graphics.drawable.Drawable;
+import android.support.v7.app.ActionBar;
/**
* Wrapper around {@link ActionBar}.
diff --git a/com/android/ex/photo/PhotoViewActivity.java b/com/android/ex/photo/PhotoViewActivity.java
index 7b53918f..a5c4a438 100644
--- a/com/android/ex/photo/PhotoViewActivity.java
+++ b/com/android/ex/photo/PhotoViewActivity.java
@@ -21,14 +21,14 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
-import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
/**
* Activity to view the contents of an album.
*/
-public class PhotoViewActivity extends FragmentActivity
+public class PhotoViewActivity extends AppCompatActivity
implements PhotoViewController.ActivityInterface {
private PhotoViewController mController;
@@ -41,7 +41,7 @@ public class PhotoViewActivity extends FragmentActivity
mController.onCreate(savedInstanceState);
}
- public PhotoViewController createController() {
+ protected PhotoViewController createController() {
return new PhotoViewController(this);
}
@@ -122,7 +122,7 @@ public class PhotoViewActivity extends FragmentActivity
@Override
public ActionBarInterface getActionBarInterface() {
if (mActionBar == null) {
- mActionBar = new ActionBarWrapper(getActionBar());
+ mActionBar = new ActionBarWrapper(getSupportActionBar());
}
return mActionBar;
}
diff --git a/com/android/internal/alsa/AlsaCardsParser.java b/com/android/internal/alsa/AlsaCardsParser.java
index bb75bf6e..5b92a173 100644
--- a/com/android/internal/alsa/AlsaCardsParser.java
+++ b/com/android/internal/alsa/AlsaCardsParser.java
@@ -37,12 +37,6 @@ public class AlsaCardsParser {
private ArrayList<AlsaCardRecord> mCardRecords = new ArrayList<AlsaCardRecord>();
- public static final int SCANSTATUS_NOTSCANNED = -1;
- public static final int SCANSTATUS_SUCCESS = 0;
- public static final int SCANSTATUS_FAIL = 1;
- public static final int SCANSTATUS_EMPTY = 2;
- private int mScanStatus = SCANSTATUS_NOTSCANNED;
-
public class AlsaCardRecord {
private static final String TAG = "AlsaCardRecord";
private static final String kUsbCardKeyStr = "at usb-";
@@ -110,11 +104,10 @@ public class AlsaCardsParser {
public AlsaCardsParser() {}
- public int scan() {
+ public void scan() {
if (DEBUG) {
- Slog.i(TAG, "AlsaCardsParser.scan()....");
+ Slog.i(TAG, "AlsaCardsParser.scan()");
}
-
mCardRecords = new ArrayList<AlsaCardRecord>();
File cardsFile = new File(kCardsFilePath);
@@ -141,26 +134,11 @@ public class AlsaCardsParser {
mCardRecords.add(cardRecord);
}
reader.close();
- if (mCardRecords.size() > 0) {
- mScanStatus = SCANSTATUS_SUCCESS;
- } else {
- mScanStatus = SCANSTATUS_EMPTY;
- }
} catch (FileNotFoundException e) {
e.printStackTrace();
- mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
e.printStackTrace();
- mScanStatus = SCANSTATUS_FAIL;
- }
- if (DEBUG) {
- Slog.i(TAG, " status:" + mScanStatus);
}
- return mScanStatus;
- }
-
- public int getScanStatus() {
- return mScanStatus;
}
public ArrayList<AlsaCardRecord> getScanRecords() {
@@ -204,11 +182,7 @@ public class AlsaCardsParser {
}
// get the new list of devices
- if (scan() != SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning Alsa cards file.");
- return -1;
- }
-
+ scan();
if (DEBUG) {
LogDevices("Current Devices:", mCardRecords);
}
diff --git a/com/android/internal/alsa/AlsaDevicesParser.java b/com/android/internal/alsa/AlsaDevicesParser.java
index 15261baf..6e3d5966 100644
--- a/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/com/android/internal/alsa/AlsaDevicesParser.java
@@ -46,12 +46,6 @@ public class AlsaDevicesParser {
private boolean mHasPlaybackDevices = false;
private boolean mHasMIDIDevices = false;
- public static final int SCANSTATUS_NOTSCANNED = -1;
- public static final int SCANSTATUS_SUCCESS = 0;
- public static final int SCANSTATUS_FAIL = 1;
- public static final int SCANSTATUS_EMPTY = 2;
- private int mScanStatus = SCANSTATUS_NOTSCANNED;
-
public class AlsaDeviceRecord {
public static final int kDeviceType_Unknown = -1;
public static final int kDeviceType_Audio = 0;
@@ -264,11 +258,7 @@ public class AlsaDevicesParser {
return line.charAt(kIndex_CardDeviceField) == '[';
}
- public int scan() {
- if (DEBUG) {
- Slog.i(TAG, "AlsaDevicesParser.scan()....");
- }
-
+ public boolean scan() {
mDeviceRecords.clear();
File devicesFile = new File(kDevicesFilePath);
@@ -284,27 +274,13 @@ public class AlsaDevicesParser {
}
}
reader.close();
- // success if we add at least 1 record
- if (mDeviceRecords.size() > 0) {
- mScanStatus = SCANSTATUS_SUCCESS;
- } else {
- mScanStatus = SCANSTATUS_EMPTY;
- }
+ return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
- mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
e.printStackTrace();
- mScanStatus = SCANSTATUS_FAIL;
}
- if (DEBUG) {
- Slog.i(TAG, " status:" + mScanStatus);
- }
- return mScanStatus;
- }
-
- public int getScanStatus() {
- return mScanStatus;
+ return false;
}
//
diff --git a/com/android/internal/notification/SystemNotificationChannels.java b/com/android/internal/notification/SystemNotificationChannels.java
index 4a181b27..d64c9a1d 100644
--- a/com/android/internal/notification/SystemNotificationChannels.java
+++ b/com/android/internal/notification/SystemNotificationChannels.java
@@ -20,7 +20,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
-import android.media.AudioAttributes;
import android.os.RemoteException;
import android.provider.Settings;
@@ -48,7 +47,6 @@ public class SystemNotificationChannels {
public static String RETAIL_MODE = "RETAIL_MODE";
public static String USB = "USB";
public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
- public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -141,17 +139,6 @@ public class SystemNotificationChannels {
foregroundChannel.setBlockableSystem(true);
channelsList.add(foregroundChannel);
- NotificationChannel heavyWeightChannel = new NotificationChannel(
- HEAVY_WEIGHT_APP,
- context.getString(R.string.notification_channel_heavy_weight_app),
- NotificationManager.IMPORTANCE_DEFAULT);
- heavyWeightChannel.setShowBadge(false);
- heavyWeightChannel.setSound(null, new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
- .build());
- channelsList.add(heavyWeightChannel);
-
nm.createNotificationChannels(channelsList);
}
diff --git a/com/android/internal/os/BatteryStatsImpl.java b/com/android/internal/os/BatteryStatsImpl.java
index 5c310b15..36fd991c 100644
--- a/com/android/internal/os/BatteryStatsImpl.java
+++ b/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 168 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 167 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -681,17 +681,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
- public long getUahDischarge(int which) {
+ public long getMahDischarge(int which) {
return mDischargeCounter.getCountLocked(which);
}
@Override
- public long getUahDischargeScreenOff(int which) {
+ public long getMahDischargeScreenOff(int which) {
return mDischargeScreenOffCounter.getCountLocked(which);
}
@Override
- public long getUahDischargeScreenDoze(int which) {
+ public long getMahDischargeScreenDoze(int which) {
return mDischargeScreenDozeCounter.getCountLocked(which);
}
@@ -3588,7 +3588,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
- final boolean screenOff = !isScreenOn(screenState);
+ final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState);
final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
final boolean updateOnBatteryScreenOffTimeBase =
(unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -5427,10 +5427,6 @@ public class BatteryStatsImpl extends BatteryStats {
elapsedRealtimeUs, which);
}
- @Override public Timer getScreenBrightnessTimer(int brightnessBin) {
- return mScreenBrightnessTimer[brightnessBin];
- }
-
@Override public long getInteractiveTime(long elapsedRealtimeUs, int which) {
return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -5524,18 +5520,10 @@ public class BatteryStatsImpl extends BatteryStats {
elapsedRealtimeUs, which);
}
- @Override public Timer getPhoneSignalScanningTimer() {
- return mPhoneSignalScanningTimer;
- }
-
@Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
- @Override public Timer getPhoneSignalStrengthTimer(int strengthBin) {
- return mPhoneSignalStrengthsTimer[strengthBin];
- }
-
@Override public long getPhoneDataConnectionTime(int dataType,
long elapsedRealtimeUs, int which) {
return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
@@ -5546,10 +5534,6 @@ public class BatteryStatsImpl extends BatteryStats {
return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
}
- @Override public Timer getPhoneDataConnectionTimer(int dataType) {
- return mPhoneDataConnectionsTimer[dataType];
- }
-
@Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -5588,10 +5572,6 @@ public class BatteryStatsImpl extends BatteryStats {
return mWifiStateTimer[wifiState].getCountLocked(which);
}
- @Override public Timer getWifiStateTimer(int wifiState) {
- return mWifiStateTimer[wifiState];
- }
-
@Override public long getWifiSupplStateTime(int state,
long elapsedRealtimeUs, int which) {
return mWifiSupplStateTimer[state].getTotalTimeLocked(
@@ -5602,10 +5582,6 @@ public class BatteryStatsImpl extends BatteryStats {
return mWifiSupplStateTimer[state].getCountLocked(which);
}
- @Override public Timer getWifiSupplStateTimer(int state) {
- return mWifiSupplStateTimer[state];
- }
-
@Override public long getWifiSignalStrengthTime(int strengthBin,
long elapsedRealtimeUs, int which) {
return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
@@ -5616,10 +5592,6 @@ public class BatteryStatsImpl extends BatteryStats {
return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
- @Override public Timer getWifiSignalStrengthTimer(int strengthBin) {
- return mWifiSignalStrengthsTimer[strengthBin];
- }
-
@Override
public ControllerActivityCounter getBluetoothControllerActivity() {
return mBluetoothActivity;
@@ -9491,7 +9463,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public boolean isScreenOn(int state) {
- return state == Display.STATE_ON || state == Display.STATE_VR;
+ return state == Display.STATE_ON;
}
public boolean isScreenOff(int state) {
@@ -12819,7 +12791,7 @@ public class BatteryStatsImpl extends BatteryStats {
mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null,
mOnBatteryTimeBase, in);
- mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
mOnBatteryTimeBase, in);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
@@ -13118,7 +13090,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
} else {
- out.writeInt(0);
+ // TODO: There should be two 0's printed here, not just one.
out.writeInt(0);
}
diff --git a/com/android/internal/os/LoggingPrintStream.java b/com/android/internal/os/LoggingPrintStream.java
index d27874cd..f14394ad 100644
--- a/com/android/internal/os/LoggingPrintStream.java
+++ b/com/android/internal/os/LoggingPrintStream.java
@@ -28,15 +28,12 @@ import java.nio.charset.CodingErrorAction;
import java.util.Formatter;
import java.util.Locale;
-import com.android.internal.annotations.VisibleForTesting;
-
/**
* A print stream which logs output line by line.
*
* {@hide}
*/
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public abstract class LoggingPrintStream extends PrintStream {
+abstract class LoggingPrintStream extends PrintStream {
private final StringBuilder builder = new StringBuilder();
diff --git a/com/android/internal/os/ZygoteInit.java b/com/android/internal/os/ZygoteInit.java
index 2be6212b..4abab283 100644
--- a/com/android/internal/os/ZygoteInit.java
+++ b/com/android/internal/os/ZygoteInit.java
@@ -549,7 +549,7 @@ public class ZygoteInit {
try {
dexoptNeeded = DexFile.getDexOptNeeded(
classPathElement, instructionSet, systemServerFilter,
- null /* classLoaderContext */, false /* newProfile */, false /* downgrade */);
+ false /* newProfile */, false /* downgrade */);
} catch (FileNotFoundException ignored) {
// Do not add to the classpath.
Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
diff --git a/com/android/internal/telephony/CarrierKeyDownloadManager.java b/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 66bc5291..606f7ffd 100644
--- a/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,10 +16,6 @@
package com.android.internal.telephony;
-import static android.preference.PreferenceManager.getDefaultSharedPreferences;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import android.app.AlarmManager;
import android.app.DownloadManager;
import android.app.PendingIntent;
@@ -57,7 +53,8 @@ import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
-import java.util.zip.GZIPInputStream;
+
+import static android.preference.PreferenceManager.getDefaultSharedPreferences;
/**
* This class contains logic to get Certificates and keep them current.
@@ -85,7 +82,7 @@ public class CarrierKeyDownloadManager {
private static final String SEPARATOR = ":";
private static final String JSON_CERTIFICATE = "certificate";
- // This is a hack to accommodate certain Carriers who insists on using the public-key
+ // This is a hack to accomodate Verizon. Verizon insists on using the public-key
// field to store the certificate. We'll just use which-ever is not null.
private static final String JSON_CERTIFICATE_ALTERNATE = "public-key";
private static final String JSON_TYPE = "key-type";
@@ -299,7 +296,6 @@ public class CarrierKeyDownloadManager {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(carrierKeyDownloadIdentifier);
Cursor cursor = mDownloadManager.query(query);
- InputStream source = null;
if (cursor == null) {
return;
@@ -308,7 +304,7 @@ public class CarrierKeyDownloadManager {
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
try {
- source = new FileInputStream(
+ final InputStream source = new FileInputStream(
mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier)
.getFileDescriptor());
jsonStr = convertToString(source);
@@ -318,11 +314,6 @@ public class CarrierKeyDownloadManager {
+ ". " + e);
} finally {
mDownloadManager.remove(carrierKeyDownloadIdentifier);
- try {
- source.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
}
}
Log.d(LOG_TAG, "Completed downloading keys");
@@ -362,23 +353,24 @@ public class CarrierKeyDownloadManager {
}
private static String convertToString(InputStream is) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+
+ String line;
try {
- // The current implementation at certain Carriers has the data gzipped, which requires
- // us to unzip the contents. Longer term, we want to add a flag in carrier config which
- // determines if the data needs to be zipped or not.
- GZIPInputStream gunzip = new GZIPInputStream(is);
- BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8));
- StringBuilder sb = new StringBuilder();
-
- String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}
- return sb.toString();
} catch (IOException e) {
e.printStackTrace();
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
- return null;
+ return sb.toString();
}
/**
@@ -409,7 +401,7 @@ public class CarrierKeyDownloadManager {
JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS);
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
- // This is a hack to accommodate certain carriers who insist on using the public-key
+ // This is a hack to accomodate Verizon. Verizon insists on using the public-key
// field to store the certificate. We'll just use which-ever is not null.
String cert = null;
if (key.has(JSON_CERTIFICATE)) {
diff --git a/com/android/internal/telephony/NetworkScanRequestTracker.java b/com/android/internal/telephony/NetworkScanRequestTracker.java
index 46b1eef9..14c6810c 100644
--- a/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -125,34 +125,6 @@ public final class NetworkScanRequestTracker {
return false;
}
}
-
- if ((nsri.mRequest.searchPeriodicity < NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC)
- || (nsri.mRequest.searchPeriodicity
- > NetworkScanRequest.MAX_SEARCH_PERIODICITY_SEC)) {
- return false;
- }
-
- if ((nsri.mRequest.maxSearchTime < NetworkScanRequest.MIN_SEARCH_MAX_SEC)
- || (nsri.mRequest.maxSearchTime > NetworkScanRequest.MAX_SEARCH_MAX_SEC)) {
- return false;
- }
-
- if ((nsri.mRequest.incrementalResultsPeriodicity
- < NetworkScanRequest.MIN_INCREMENTAL_PERIODICITY_SEC)
- || (nsri.mRequest.incrementalResultsPeriodicity
- > NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC)) {
- return false;
- }
-
- if ((nsri.mRequest.searchPeriodicity > nsri.mRequest.maxSearchTime)
- || (nsri.mRequest.incrementalResultsPeriodicity > nsri.mRequest.maxSearchTime)) {
- return false;
- }
-
- if ((nsri.mRequest.mccMncs != null)
- && (nsri.mRequest.mccMncs.size() > NetworkScanRequest.MAX_MCC_MNC_LIST_SIZE)) {
- return false;
- }
return true;
}
diff --git a/com/android/internal/telephony/Phone.java b/com/android/internal/telephony/Phone.java
index 16f816f4..6acc8743 100644
--- a/com/android/internal/telephony/Phone.java
+++ b/com/android/internal/telephony/Phone.java
@@ -3319,9 +3319,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mRadioCapability.set(rc);
if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
- boolean restoreSelection = !mContext.getResources().getBoolean(
- com.android.internal.R.bool.skip_restoring_network_selection);
- sendSubscriptionSettings(restoreSelection);
+ sendSubscriptionSettings(true);
}
}
diff --git a/com/android/internal/telephony/RIL.java b/com/android/internal/telephony/RIL.java
index 9007f14d..84c2b659 100644
--- a/com/android/internal/telephony/RIL.java
+++ b/com/android/internal/telephony/RIL.java
@@ -1655,69 +1655,56 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- private android.hardware.radio.V1_1.RadioAccessSpecifier convertRadioAccessSpecifierToRadioHAL(
- RadioAccessSpecifier ras) {
- android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
- new android.hardware.radio.V1_1.RadioAccessSpecifier();
- rasInHalFormat.radioAccessNetwork = ras.radioAccessNetwork;
- List<Integer> bands = null;
- switch (ras.radioAccessNetwork) {
- case RadioAccessNetworks.GERAN:
- bands = rasInHalFormat.geranBands;
- break;
- case RadioAccessNetworks.UTRAN:
- bands = rasInHalFormat.utranBands;
- break;
- case RadioAccessNetworks.EUTRAN:
- bands = rasInHalFormat.eutranBands;
- break;
- default:
- Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
- + " not supported!");
- return null;
- }
-
- if (ras.bands != null) {
- for (int band : ras.bands) {
- bands.add(band);
- }
- }
- if (ras.channels != null) {
- for (int channel : ras.channels) {
- rasInHalFormat.channels.add(channel);
- }
- }
-
- return rasInHalFormat;
- }
-
@Override
public void startNetworkScan(NetworkScanRequest nsr, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
- android.hardware.radio.V1_2.IRadio radioProxy12 =
- android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
- if (radioProxy12 != null) {
- android.hardware.radio.V1_2.NetworkScanRequest request =
- new android.hardware.radio.V1_2.NetworkScanRequest();
+ android.hardware.radio.V1_1.IRadio radioProxy11 =
+ android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+ if (radioProxy11 == null) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ } else {
+ android.hardware.radio.V1_1.NetworkScanRequest request =
+ new android.hardware.radio.V1_1.NetworkScanRequest();
request.type = nsr.scanType;
- request.interval = nsr.searchPeriodicity;
- request.maxSearchTime = nsr.maxSearchTime;
- request.incrementalResultsPeriodicity = nsr.incrementalResultsPeriodicity;
- request.incrementalResults = nsr.incrementalResults;
-
+ request.interval = 60;
for (RadioAccessSpecifier ras : nsr.specifiers) {
-
- android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
- convertRadioAccessSpecifierToRadioHAL(ras);
- if (rasInHalFormat == null) {
- return;
+ android.hardware.radio.V1_1.RadioAccessSpecifier s =
+ new android.hardware.radio.V1_1.RadioAccessSpecifier();
+ s.radioAccessNetwork = ras.radioAccessNetwork;
+ List<Integer> bands = null;
+ switch (ras.radioAccessNetwork) {
+ case RadioAccessNetworks.GERAN:
+ bands = s.geranBands;
+ break;
+ case RadioAccessNetworks.UTRAN:
+ bands = s.utranBands;
+ break;
+ case RadioAccessNetworks.EUTRAN:
+ bands = s.eutranBands;
+ break;
+ default:
+ Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
+ + " not supported!");
+ return;
}
-
- request.specifiers.add(rasInHalFormat);
+ if (ras.bands != null) {
+ for (int band : ras.bands) {
+ bands.add(band);
+ }
+ }
+ if (ras.channels != null) {
+ for (int channel : ras.channels) {
+ s.channels.add(channel);
+ }
+ }
+ request.specifiers.add(s);
}
- request.mccMncs.addAll(nsr.mccMncs);
RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
mRILDefaultWorkSource);
@@ -1726,47 +1713,10 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
try {
- radioProxy12.startNetworkScan_1_2(rr.mSerial, request);
+ radioProxy11.startNetworkScan(rr.mSerial, request);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
}
- } else {
- android.hardware.radio.V1_1.IRadio radioProxy11 =
- android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
- if (radioProxy11 == null) {
- if (result != null) {
- AsyncResult.forMessage(result, null,
- CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
- result.sendToTarget();
- }
- } else {
- android.hardware.radio.V1_1.NetworkScanRequest request =
- new android.hardware.radio.V1_1.NetworkScanRequest();
- request.type = nsr.scanType;
- request.interval = nsr.searchPeriodicity;
- for (RadioAccessSpecifier ras : nsr.specifiers) {
- android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
- convertRadioAccessSpecifierToRadioHAL(ras);
- if (rasInHalFormat == null) {
- return;
- }
-
- request.specifiers.add(rasInHalFormat);
- }
-
- RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
- mRILDefaultWorkSource);
-
- if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- }
-
- try {
- radioProxy11.startNetworkScan(rr.mSerial, request);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
- }
- }
}
}
}
diff --git a/com/android/internal/telephony/cat/CatService.java b/com/android/internal/telephony/cat/CatService.java
index 802944d8..cd7a7561 100644
--- a/com/android/internal/telephony/cat/CatService.java
+++ b/com/android/internal/telephony/cat/CatService.java
@@ -1071,13 +1071,6 @@ public class CatService extends Handler implements AppInterface {
}
break;
case NO_RESPONSE_FROM_USER:
- // No need to send terminal response for SET UP CALL on user timeout,
- // instead use dedicated API
- if (type == CommandType.SET_UP_CALL) {
- mCmdIf.handleCallSetupRequestFromSim(false, null);
- mCurrntCmd = null;
- return;
- }
case UICC_SESSION_TERM_BY_USER:
resp = null;
break;
diff --git a/com/android/internal/telephony/uicc/UiccCardApplication.java b/com/android/internal/telephony/uicc/UiccCardApplication.java
index fa6bc3a6..e2904dfd 100644
--- a/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -382,8 +382,11 @@ public class UiccCardApplication {
case EVENT_CHANGE_PIN2_DONE:
// a PIN/PUK/PIN2/PUK2 complete
// request has completed. ar.userObj is the response Message
+ int attemptsRemaining = -1;
ar = (AsyncResult)msg.obj;
- int attemptsRemaining = parsePinPukErrorResult(ar);
+ if ((ar.exception != null) && (ar.result != null)) {
+ attemptsRemaining = parsePinPukErrorResult(ar);
+ }
Message response = (Message)ar.userObj;
AsyncResult.forMessage(response).exception = ar.exception;
response.arg1 = attemptsRemaining;
diff --git a/com/android/internal/util/MemInfoReader.java b/com/android/internal/util/MemInfoReader.java
index 8d716667..b71fa067 100644
--- a/com/android/internal/util/MemInfoReader.java
+++ b/com/android/internal/util/MemInfoReader.java
@@ -82,7 +82,7 @@ public final class MemInfoReader {
* that are mapped in to processes.
*/
public long getCachedSizeKb() {
- return mInfos[Debug.MEMINFO_BUFFERS] + mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE]
+ return mInfos[Debug.MEMINFO_BUFFERS]
+ mInfos[Debug.MEMINFO_CACHED] - mInfos[Debug.MEMINFO_MAPPED];
}
@@ -90,7 +90,7 @@ public final class MemInfoReader {
* Amount of RAM that is in use by the kernel for actual allocations.
*/
public long getKernelUsedSizeKb() {
- return mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
+ return mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB]
+ mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES]
+ mInfos[Debug.MEMINFO_KERNEL_STACK];
}
diff --git a/com/android/internal/view/menu/ListMenuItemView.java b/com/android/internal/view/menu/ListMenuItemView.java
index 8f80bfe3..f76c7247 100644
--- a/com/android/internal/view/menu/ListMenuItemView.java
+++ b/com/android/internal/view/menu/ListMenuItemView.java
@@ -319,15 +319,13 @@ public class ListMenuItemView extends LinearLayout
public void setGroupDividerEnabled(boolean groupDividerEnabled) {
// If mHasListDivider is true, disabling the groupDivider.
// Otherwise, checking enbling it according to groupDividerEnabled flag.
- if (mGroupDivider != null) {
- mGroupDivider.setVisibility(!mHasListDivider
- && groupDividerEnabled ? View.VISIBLE : View.GONE);
- }
+ mGroupDivider.setVisibility(!mHasListDivider
+ && groupDividerEnabled ? View.VISIBLE : View.GONE);
}
@Override
public void adjustListItemSelectionBounds(Rect rect) {
- if (mGroupDivider != null && mGroupDivider.getVisibility() == View.VISIBLE) {
+ if (mGroupDivider.getVisibility() == View.VISIBLE) {
// groupDivider is a part of MenuItemListView.
// If ListMenuItem with divider enabled is hovered/clicked, divider also gets selected.
// Clipping the selector bounds from the top divider portion when divider is enabled,
diff --git a/com/android/internal/widget/Magnifier.java b/com/android/internal/widget/Magnifier.java
index 9bc0778d..86e7b38a 100644
--- a/com/android/internal/widget/Magnifier.java
+++ b/com/android/internal/widget/Magnifier.java
@@ -22,9 +22,7 @@ import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
@@ -43,8 +41,6 @@ import com.android.internal.util.Preconditions;
*/
public final class Magnifier {
private static final String LOG_TAG = "magnifier";
- // Use this to specify that a previous configuration value does not exist.
- private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
// The view for which this magnifier is attached.
private final View mView;
// The window containing the magnifier.
@@ -63,15 +59,6 @@ public final class Magnifier {
// the copy is finished.
private final Handler mPixelCopyHandler = Handler.getMain();
- private RectF mTmpRectF;
-
- // Variables holding previous states, used for detecting redundant calls and invalidation.
- private Point mPrevStartCoordsOnScreen = new Point(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private PointF mPrevCenterCoordsOnScreen = new PointF(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
-
/**
* Initializes a magnifier.
*
@@ -101,45 +88,16 @@ public final class Magnifier {
/**
* Shows the magnifier on the screen.
*
- * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
- * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
- * @param scale the scale at which the magnifier zooms on the source content. The
- * lower end is clamped to 1 and the higher end to 4
+ * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source
+ * @param centerYOnScreen vertical coordinate of the center point of the magnifier source
+ * @param scale the scale at which the magnifier zooms on the source content
*/
public void show(@FloatRange(from=0) float centerXOnScreen,
@FloatRange(from=0) float centerYOnScreen,
- @FloatRange(from=1, to=4) float scale) {
- if (scale > 4) {
- scale = 4;
- }
-
- if (scale < 1) {
- scale = 1;
- }
-
- if (centerXOnScreen < 0) {
- centerXOnScreen = 0;
- }
-
- if (centerYOnScreen < 0) {
- centerYOnScreen = 0;
- }
-
- showInternal(centerXOnScreen, centerYOnScreen, scale, false);
- }
-
- private void showInternal(@FloatRange(from=0) float centerXOnScreen,
- @FloatRange(from=0) float centerYOnScreen,
- @FloatRange(from=1, to=4) float scale,
- boolean forceShow) {
- if (mPrevScale != scale) {
- resizeBitmap(scale);
- mPrevScale = scale;
- }
+ @FloatRange(from=1, to=10) float scale) {
+ maybeResizeBitmap(scale);
configureCoordinates(centerXOnScreen, centerYOnScreen);
- maybePerformPixelCopy(scale, forceShow);
+ performPixelCopy();
if (mWindow.isShowing()) {
mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
@@ -148,9 +106,6 @@ public final class Magnifier {
mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
mWindowCoords.x, mWindowCoords.y);
}
-
- mPrevCenterCoordsOnScreen.x = centerXOnScreen;
- mPrevCenterCoordsOnScreen.y = centerYOnScreen;
}
/**
@@ -158,38 +113,6 @@ public final class Magnifier {
*/
public void dismiss() {
mWindow.dismiss();
-
- mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- }
-
- /**
- * Forces the magnifier to update content by taking and showing a new snapshot using the
- * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle
- * intersects the rectangle which holds the content to be magnified.
- *
- * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region
- */
- public void invalidate(RectF dirtyRectOnScreen) {
- if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) {
- // Update the current showing RectF.
- mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x,
- mPrevStartCoordsOnScreen.y,
- mPrevStartCoordsOnScreen.x + mBitmap.getWidth(),
- mPrevStartCoordsOnScreen.y + mBitmap.getHeight());
-
- // Update only if we are currently showing content that has been declared as invalid.
- if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) {
- // Update the contents shown in the magnifier.
- showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale,
- true /* forceShow */);
- }
- }
}
/**
@@ -206,11 +129,13 @@ public final class Magnifier {
return mWindowWidth;
}
- private void resizeBitmap(float scale) {
+ private void maybeResizeBitmap(float scale) {
final int bitmapWidth = (int) (mWindowWidth / scale);
final int bitmapHeight = (int) (mWindowHeight / scale);
- mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
- getImageView().setImageBitmap(mBitmap);
+ if (mBitmap.getWidth() != bitmapWidth || mBitmap.getHeight() != bitmapHeight) {
+ mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+ getImageView().setImageBitmap(mBitmap);
+ }
}
private void configureCoordinates(float posXOnScreen, float posYOnScreen) {
@@ -219,29 +144,24 @@ public final class Magnifier {
final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
R.dimen.magnifier_offset);
+ final int availableTopSpace = (mCenterZoomCoords.y - mWindowHeight / 2)
+ - verticalMagnifierOffset - (mBitmap.getHeight() / 2);
+
mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
- mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
+ mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2
+ + verticalMagnifierOffset * (availableTopSpace > 0 ? -1 : 1);
}
- private void maybePerformPixelCopy(final float scale, final boolean forceShow) {
- final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
- int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
-
+ private void performPixelCopy() {
+ int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
// Clamp startX value to avoid distorting the rendering of the magnifier content.
- if (rawStartX < 0) {
- rawStartX = 0;
- } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) {
- rawStartX = mView.getWidth() - mBitmap.getWidth();
+ if (startX < 0) {
+ startX = 0;
+ } else if (startX + mBitmap.getWidth() > mView.getWidth()) {
+ startX = mView.getWidth() - mBitmap.getWidth();
}
- if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x
- && startY == mPrevStartCoordsOnScreen.y
- && scale == mPrevScale) {
- // Skip, we are already showing the desired content.
- return;
- }
-
- final int startX = rawStartX;
+ final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
if (viewRootImpl != null && viewRootImpl.mSurface != null
@@ -251,11 +171,7 @@ public final class Magnifier {
new Rect(startX, startY, startX + mBitmap.getWidth(),
startY + mBitmap.getHeight()),
mBitmap,
- result -> {
- getImageView().invalidate();
- mPrevStartCoordsOnScreen.x = startX;
- mPrevStartCoordsOnScreen.y = startY;
- },
+ result -> getImageView().invalidate(),
mPixelCopyHandler);
} else {
Log.d(LOG_TAG, "Could not perform PixelCopy request");
diff --git a/com/android/keyguard/CarrierText.java b/com/android/keyguard/CarrierText.java
index 13c48d0d..159ac4cc 100644
--- a/com/android/keyguard/CarrierText.java
+++ b/com/android/keyguard/CarrierText.java
@@ -39,7 +39,6 @@ import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.WirelessUtils;
-import android.telephony.TelephonyManager;
public class CarrierText extends TextView {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -53,8 +52,6 @@ public class CarrierText extends TextView {
private WifiManager mWifiManager;
- private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
-
private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshCarrierInfo() {
@@ -68,22 +65,6 @@ public class CarrierText extends TextView {
public void onStartedWakingUp() {
setSelected(true);
};
-
- public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
- if (slotId < 0) {
- Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId);
- return;
- }
-
- if (DEBUG) Log.d(TAG,"onSimStateChanged: " + getStatusForIccState(simState));
- if (getStatusForIccState(simState) == StatusMode.SimIoError) {
- mSimErrorState[slotId] = true;
- updateCarrierText();
- } else if (mSimErrorState[slotId]) {
- mSimErrorState[slotId] = false;
- updateCarrierText();
- }
- };
};
/**
* The status of this lock screen. Primarily used for widgets on LockScreen.
@@ -96,8 +77,7 @@ public class CarrierText extends TextView {
SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
SimLocked, // SIM card is currently locked
SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
- SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
- SimIoError; // SIM card is faulty
+ SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
}
public CarrierText(Context context) {
@@ -121,35 +101,6 @@ public class CarrierText extends TextView {
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
- /**
- * Checks if there are faulty cards. Adds the text depending on the slot of the card
- * @param text: current carrier text based on the sim state
- * @param noSims: whether a valid sim card is inserted
- * @return text
- */
- private CharSequence updateCarrierTextWithSimIoError(CharSequence text, boolean noSims) {
- final CharSequence carrier = "";
- CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
- IccCardConstants.State.CARD_IO_ERROR, carrier);
- for (int index = 0; index < mSimErrorState.length; index++) {
- if (mSimErrorState[index]) {
- // In the case when no sim cards are detected but a faulty card is inserted
- // overwrite the text and only show "Invalid card"
- if (noSims) {
- return concatenate(carrierTextForSimIOError,
- getContext().getText(com.android.internal.R.string.emergency_calls_only));
- } else if (index == 0) {
- // prepend "Invalid card" when faulty card is inserted in slot 0
- text = concatenate(carrierTextForSimIOError, text);
- } else {
- // concatenate "Invalid card" when faulty card is inserted in slot 1
- text = concatenate(text, carrierTextForSimIOError);
- }
- }
- }
- return text;
- }
-
protected void updateCarrierText() {
boolean allSimsMissing = true;
boolean anySimReadyAndInService = false;
@@ -228,7 +179,6 @@ public class CarrierText extends TextView {
}
}
- displayText = updateCarrierTextWithSimIoError(displayText, allSimsMissing);
// APM (airplane mode) != no carrier state. There are carrier services
// (e.g. WFC = Wi-Fi calling) which may operate in APM.
if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
@@ -320,11 +270,6 @@ public class CarrierText extends TextView {
getContext().getText(R.string.keyguard_sim_puk_locked_message),
text);
break;
- case SimIoError:
- carrierText = makeCarrierStringOnEmergencyCapable(
- getContext().getText(R.string.keyguard_sim_error_message_short),
- text);
- break;
}
return carrierText;
@@ -374,8 +319,6 @@ public class CarrierText extends TextView {
return StatusMode.SimPermDisabled;
case UNKNOWN:
return StatusMode.SimMissing;
- case CARD_IO_ERROR:
- return StatusMode.SimIoError;
}
return StatusMode.SimMissing;
}
diff --git a/com/android/keyguard/KeyguardSecurityModel.java b/com/android/keyguard/KeyguardSecurityModel.java
index 0cb64230..7baa57e7 100644
--- a/com/android/keyguard/KeyguardSecurityModel.java
+++ b/com/android/keyguard/KeyguardSecurityModel.java
@@ -57,16 +57,16 @@ public class KeyguardSecurityModel {
SecurityMode getSecurityMode(int userId) {
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
- monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
- return SecurityMode.SimPuk;
- }
-
if (SubscriptionManager.isValidSubscriptionId(
monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
return SecurityMode.SimPin;
}
+ if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
+ monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
+ return SecurityMode.SimPuk;
+ }
+
final int security = mLockPatternUtils.getActivePasswordQuality(userId);
switch (security) {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
diff --git a/com/android/keyguard/KeyguardUpdateMonitor.java b/com/android/keyguard/KeyguardUpdateMonitor.java
index 2bb992c4..d83a6c60 100644
--- a/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -52,6 +52,7 @@ import android.media.AudioManager;
import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Message;
import android.os.RemoteException;
@@ -77,7 +78,7 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.google.android.collect.Lists;
@@ -901,8 +902,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
} else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) {
state = IccCardConstants.State.NETWORK_LOCKED;
- } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
- state = IccCardConstants.State.CARD_IO_ERROR;
} else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(stateExtra)
|| IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(stateExtra)) {
// This is required because telephony doesn't return to "READY" after
@@ -1772,7 +1771,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ private final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChangedBackground() {
try {
diff --git a/com/android/layoutlib/bridge/Bridge.java b/com/android/layoutlib/bridge/Bridge.java
index 5dca8e7f..0cfc1811 100644
--- a/com/android/layoutlib/bridge/Bridge.java
+++ b/com/android/layoutlib/bridge/Bridge.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,659 +14,62 @@
* limitations under the License.
*/
-package com.android.layoutlib.bridge;
-
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.DrawableParams;
-import com.android.ide.common.rendering.api.Features;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
+package com.android.layoutlib.bridge;import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.impl.RenderDrawable;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-import com.android.layoutlib.bridge.util.DynamicIdMap;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.resources.ResourceType;
-import com.android.tools.layoutlib.create.MethodAdapter;
-import com.android.tools.layoutlib.create.OverrideMethod;
-import com.android.util.Pair;
-
-import android.annotation.NonNull;
-import android.content.res.BridgeAssetManager;
-import android.graphics.Bitmap;
-import android.graphics.FontFamily_Delegate;
-import android.graphics.Typeface;
-import android.graphics.Typeface_Delegate;
-import android.icu.util.ULocale;
-import android.os.Looper;
-import android.os.Looper_Accessor;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import java.io.File;
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.concurrent.locks.ReentrantLock;
-import libcore.io.MemoryMappedFile_Delegate;
-
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
/**
- * Main entry point of the LayoutLib Bridge.
- * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createSession(SessionParams)}
+ * Legacy Bridge used in the SDK version of layoutlib
*/
public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
+ private static final String SDK_NOT_SUPPORTED = "The SDK layoutlib version is not supported";
+ private static final Result NOT_SUPPORTED_RESULT =
+ Status.NOT_IMPLEMENTED.createResult(SDK_NOT_SUPPORTED);
+ private static BufferedImage sImage;
- private static final String ICU_LOCALE_DIRECTION_RTL = "right-to-left";
+ private static class BridgeRenderSession extends RenderSession {
- public static class StaticMethodNotImplementedException extends RuntimeException {
- private static final long serialVersionUID = 1L;
+ @Override
+ public synchronized BufferedImage getImage() {
+ if (sImage == null) {
+ sImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = sImage.createGraphics();
+ g.clearRect(0, 0, 500, 500);
+ g.drawString(SDK_NOT_SUPPORTED, 20, 20);
+ g.dispose();
+ }
- public StaticMethodNotImplementedException(String msg) {
- super(msg);
+ return sImage;
}
- }
-
- /**
- * Lock to ensure only one rendering/inflating happens at a time.
- * This is due to some singleton in the Android framework.
- */
- private final static ReentrantLock sLock = new ReentrantLock();
-
- /**
- * Maps from id to resource type/name. This is for com.android.internal.R
- */
- @SuppressWarnings("deprecation")
- private final static Map<Integer, Pair<ResourceType, String>> sRMap = new HashMap<>();
- /**
- * Reverse map compared to sRMap, resource type -> (resource name -> id).
- * This is for com.android.internal.R.
- */
- private final static Map<ResourceType, Map<String, Integer>> sRevRMap = new EnumMap<>(ResourceType.class);
-
- // framework resources are defined as 0x01XX#### where XX is the resource type (layout,
- // drawable, etc...). Using FF as the type allows for 255 resource types before we get a
- // collision which should be fine.
- private final static int DYNAMIC_ID_SEED_START = 0x01ff0000;
- private final static DynamicIdMap sDynamicIds = new DynamicIdMap(DYNAMIC_ID_SEED_START);
-
- private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
- new WeakHashMap<>();
- private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
- new WeakHashMap<>();
-
- private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = new HashMap<>();
- private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
- new HashMap<>();
-
- private static Map<String, Map<String, Integer>> sEnumValueMap;
- private static Map<String, String> sPlatformProperties;
-
- /**
- * A default log than prints to stdout/stderr.
- */
- private final static LayoutLog sDefaultLog = new LayoutLog() {
@Override
- public void error(String tag, String message, Object data) {
- System.err.println(message);
+ public Result render(long timeout, boolean forceMeasure) {
+ return NOT_SUPPORTED_RESULT;
}
@Override
- public void error(String tag, String message, Throwable throwable, Object data) {
- System.err.println(message);
+ public Result measure(long timeout) {
+ return NOT_SUPPORTED_RESULT;
}
@Override
- public void warning(String tag, String message, Object data) {
- System.out.println(message);
- }
- };
-
- /**
- * Current log.
- */
- private static LayoutLog sCurrentLog = sDefaultLog;
-
- private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR;
-
- @Override
- public int getApiLevel() {
- return com.android.ide.common.rendering.api.Bridge.API_CURRENT;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- @Deprecated
- public EnumSet<Capability> getCapabilities() {
- // The Capability class is deprecated and frozen. All Capabilities enumerated there are
- // supported by this version of LayoutLibrary. So, it's safe to use EnumSet.allOf()
- return EnumSet.allOf(Capability.class);
- }
-
- @Override
- public boolean supports(int feature) {
- return feature <= LAST_SUPPORTED_FEATURE;
- }
-
- @Override
- public boolean init(Map<String,String> platformProperties,
- File fontLocation,
- Map<String, Map<String, Integer>> enumValueMap,
- LayoutLog log) {
- sPlatformProperties = platformProperties;
- sEnumValueMap = enumValueMap;
-
- BridgeAssetManager.initSystem();
-
- // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
- // on static (native) methods which prints the signature on the console and
- // throws an exception.
- // This is useful when testing the rendering in ADT to identify static native
- // methods that are ignored -- layoutlib_create makes them returns 0/false/null
- // which is generally OK yet might be a problem, so this is how you'd find out.
- //
- // Currently layoutlib_create only overrides static native method.
- // Static non-natives are not overridden and thus do not get here.
- final String debug = System.getenv("DEBUG_LAYOUT");
- if (debug != null && !debug.equals("0") && !debug.equals("false")) {
-
- OverrideMethod.setDefaultListener(new MethodAdapter() {
- @Override
- public void onInvokeV(String signature, boolean isNative, Object caller) {
- sDefaultLog.error(null, "Missing Stub: " + signature +
- (isNative ? " (native)" : ""), null /*data*/);
-
- if (debug.equalsIgnoreCase("throw")) {
- // Throwing this exception doesn't seem that useful. It breaks
- // the layout editor yet doesn't display anything meaningful to the
- // user. Having the error in the console is just as useful. We'll
- // throw it only if the environment variable is "throw" or "THROW".
- throw new StaticMethodNotImplementedException(signature);
- }
- }
- });
- }
-
- // load the fonts.
- FontFamily_Delegate.setFontLocation(fontLocation.getAbsolutePath());
- MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
-
- // now parse com.android.internal.R (and only this one as android.R is a subset of
- // the internal version), and put the content in the maps.
- try {
- Class<?> r = com.android.internal.R.class;
- // Parse the styleable class first, since it may contribute to attr values.
- parseStyleable();
-
- for (Class<?> inner : r.getDeclaredClasses()) {
- if (inner == com.android.internal.R.styleable.class) {
- // Already handled the styleable case. Not skipping attr, as there may be attrs
- // that are not referenced from styleables.
- continue;
- }
- String resTypeName = inner.getSimpleName();
- ResourceType resType = ResourceType.getEnum(resTypeName);
- if (resType != null) {
- Map<String, Integer> fullMap = null;
- switch (resType) {
- case ATTR:
- fullMap = sRevRMap.get(ResourceType.ATTR);
- break;
- case STRING:
- case STYLE:
- // Slightly less than thousand entries in each.
- fullMap = new HashMap<>(1280);
- // no break.
- default:
- if (fullMap == null) {
- fullMap = new HashMap<>();
- }
- sRevRMap.put(resType, fullMap);
- }
-
- for (Field f : inner.getDeclaredFields()) {
- // only process static final fields. Since the final attribute may have
- // been altered by layoutlib_create, we only check static
- if (!isValidRField(f)) {
- continue;
- }
- Class<?> type = f.getType();
- if (!type.isArray()) {
- Integer value = (Integer) f.get(null);
- //noinspection deprecation
- sRMap.put(value, Pair.of(resType, f.getName()));
- fullMap.put(f.getName(), value);
- }
- }
- }
- }
- } catch (Exception throwable) {
- if (log != null) {
- log.error(LayoutLog.TAG_BROKEN,
- "Failed to load com.android.internal.R from the layout library jar",
- throwable, null);
- }
- return false;
+ public Result getResult() {
+ return NOT_SUPPORTED_RESULT;
}
-
- return true;
- }
-
- /**
- * Tests if the field is pubic, static and one of int or int[].
- */
- private static boolean isValidRField(Field field) {
- int modifiers = field.getModifiers();
- boolean isAcceptable = Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
- Class<?> type = field.getType();
- return isAcceptable && type == int.class ||
- (type.isArray() && type.getComponentType() == int.class);
-
}
- private static void parseStyleable() throws Exception {
- // R.attr doesn't contain all the needed values. There are too many resources in the
- // framework for all to be in the R class. Only the ones specified manually in
- // res/values/symbols.xml are put in R class. Since, we need to create a map of all attr
- // values, we try and find them from the styleables.
-
- // There were 1500 elements in this map at M timeframe.
- Map<String, Integer> revRAttrMap = new HashMap<>(2048);
- sRevRMap.put(ResourceType.ATTR, revRAttrMap);
- // There were 2000 elements in this map at M timeframe.
- Map<String, Integer> revRStyleableMap = new HashMap<>(3072);
- sRevRMap.put(ResourceType.STYLEABLE, revRStyleableMap);
- Class<?> c = com.android.internal.R.styleable.class;
- Field[] fields = c.getDeclaredFields();
- // Sort the fields to bring all arrays to the beginning, so that indices into the array are
- // able to refer back to the arrays (i.e. no forward references).
- Arrays.sort(fields, (o1, o2) -> {
- if (o1 == o2) {
- return 0;
- }
- Class<?> t1 = o1.getType();
- Class<?> t2 = o2.getType();
- if (t1.isArray() && !t2.isArray()) {
- return -1;
- } else if (t2.isArray() && !t1.isArray()) {
- return 1;
- }
- return o1.getName().compareTo(o2.getName());
- });
- Map<String, int[]> styleables = new HashMap<>();
- for (Field field : fields) {
- if (!isValidRField(field)) {
- // Only consider public static fields that are int or int[].
- // Don't check the final flag as it may have been modified by layoutlib_create.
- continue;
- }
- String name = field.getName();
- if (field.getType().isArray()) {
- int[] styleableValue = (int[]) field.get(null);
- styleables.put(name, styleableValue);
- continue;
- }
- // Not an array.
- String arrayName = name;
- int[] arrayValue = null;
- int index;
- while ((index = arrayName.lastIndexOf('_')) >= 0) {
- // Find the name of the corresponding styleable.
- // Search in reverse order so that attrs like LinearLayout_Layout_layout_gravity
- // are mapped to LinearLayout_Layout and not to LinearLayout.
- arrayName = arrayName.substring(0, index);
- arrayValue = styleables.get(arrayName);
- if (arrayValue != null) {
- break;
- }
- }
- index = (Integer) field.get(null);
- if (arrayValue != null) {
- String attrName = name.substring(arrayName.length() + 1);
- int attrValue = arrayValue[index];
- //noinspection deprecation
- sRMap.put(attrValue, Pair.of(ResourceType.ATTR, attrName));
- revRAttrMap.put(attrName, attrValue);
- }
- //noinspection deprecation
- sRMap.put(index, Pair.of(ResourceType.STYLEABLE, name));
- revRStyleableMap.put(name, index);
- }
- }
@Override
- public boolean dispose() {
- BridgeAssetManager.clearSystem();
-
- // dispose of the default typeface.
- Typeface_Delegate.resetDefaults();
- Typeface.sDynamicTypefaceCache.evictAll();
- sProject9PatchCache.clear();
- sProjectBitmapCache.clear();
-
- return true;
- }
-
- /**
- * Starts a layout session by inflating and rendering it. The method returns a
- * {@link RenderSession} on which further actions can be taken.
- * <p/>
- * If {@link SessionParams} includes the {@link RenderParamsFlags#FLAG_DO_NOT_RENDER_ON_CREATE},
- * this method will only inflate the layout but will NOT render it.
- * @param params the {@link SessionParams} object with all the information necessary to create
- * the scene.
- * @return a new {@link RenderSession} object that contains the result of the layout.
- * @since 5
- */
- @Override
public RenderSession createSession(SessionParams params) {
- try {
- Result lastResult;
- RenderSessionImpl scene = new RenderSessionImpl(params);
- try {
- prepareThread();
- lastResult = scene.init(params.getTimeout());
- if (lastResult.isSuccess()) {
- lastResult = scene.inflate();
-
- boolean doNotRenderOnCreate = Boolean.TRUE.equals(
- params.getFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE));
- if (lastResult.isSuccess() && !doNotRenderOnCreate) {
- lastResult = scene.render(true /*freshRender*/);
- }
- }
- } finally {
- scene.release();
- cleanupThread();
- }
-
- return new BridgeRenderSession(scene, lastResult);
- } catch (Throwable t) {
- // get the real cause of the exception.
- Throwable t2 = t;
- while (t2.getCause() != null) {
- t2 = t2.getCause();
- }
- return new BridgeRenderSession(null,
- ERROR_UNKNOWN.createResult(t2.getMessage(), t));
- }
- }
-
- @Override
- public Result renderDrawable(DrawableParams params) {
- try {
- Result lastResult;
- RenderDrawable action = new RenderDrawable(params);
- try {
- prepareThread();
- lastResult = action.init(params.getTimeout());
- if (lastResult.isSuccess()) {
- lastResult = action.render();
- }
- } finally {
- action.release();
- cleanupThread();
- }
-
- return lastResult;
- } catch (Throwable t) {
- // get the real cause of the exception.
- Throwable t2 = t;
- while (t2.getCause() != null) {
- t2 = t.getCause();
- }
- return ERROR_UNKNOWN.createResult(t2.getMessage(), t);
- }
+ return new BridgeRenderSession();
}
@Override
- public void clearCaches(Object projectKey) {
- if (projectKey != null) {
- sProjectBitmapCache.remove(projectKey);
- sProject9PatchCache.remove(projectKey);
- }
- }
-
- @Override
- public Result getViewParent(Object viewObject) {
- if (viewObject instanceof View) {
- return Status.SUCCESS.createResult(((View)viewObject).getParent());
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
- @Override
- public Result getViewIndex(Object viewObject) {
- if (viewObject instanceof View) {
- View view = (View) viewObject;
- ViewParent parentView = view.getParent();
-
- if (parentView instanceof ViewGroup) {
- Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view));
- }
-
- return Status.SUCCESS.createResult();
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
- @Override
- public boolean isRtl(String locale) {
- return isLocaleRtl(locale);
- }
-
- public static boolean isLocaleRtl(String locale) {
- if (locale == null) {
- locale = "";
- }
- ULocale uLocale = new ULocale(locale);
- return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL);
- }
-
- /**
- * Returns the lock for the bridge
- */
- public static ReentrantLock getLock() {
- return sLock;
- }
-
- /**
- * Prepares the current thread for rendering.
- *
- * Note that while this can be called several time, the first call to {@link #cleanupThread()}
- * will do the clean-up, and make the thread unable to do further scene actions.
- */
- public synchronized static void prepareThread() {
- // we need to make sure the Looper has been initialized for this thread.
- // this is required for View that creates Handler objects.
- if (Looper.myLooper() == null) {
- Looper.prepareMainLooper();
- }
- }
-
- /**
- * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
- * <p>
- * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
- * call to this will prevent the thread from doing further scene actions
- */
- public synchronized static void cleanupThread() {
- // clean up the looper
- Looper_Accessor.cleanupThread();
- }
-
- public static LayoutLog getLog() {
- return sCurrentLog;
- }
-
- public static void setLog(LayoutLog log) {
- // check only the thread currently owning the lock can do this.
- if (!sLock.isHeldByCurrentThread()) {
- throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
- }
-
- if (log != null) {
- sCurrentLog = log;
- } else {
- sCurrentLog = sDefaultLog;
- }
- }
-
- /**
- * Returns details of a framework resource from its integer value.
- * @param value the integer value
- * @return a Pair containing the resource type and name, or null if the id
- * does not match any resource.
- */
- @SuppressWarnings("deprecation")
- public static Pair<ResourceType, String> resolveResourceId(int value) {
- Pair<ResourceType, String> pair = sRMap.get(value);
- if (pair == null) {
- pair = sDynamicIds.resolveId(value);
- }
- return pair;
- }
-
- /**
- * Returns the integer id of a framework resource, from a given resource type and resource name.
- * <p/>
- * If no resource is found, it creates a dynamic id for the resource.
- *
- * @param type the type of the resource
- * @param name the name of the resource.
- *
- * @return an {@link Integer} containing the resource id.
- */
- @NonNull
- public static Integer getResourceId(ResourceType type, String name) {
- Map<String, Integer> map = sRevRMap.get(type);
- Integer value = null;
- if (map != null) {
- value = map.get(name);
- }
-
- return value == null ? sDynamicIds.getId(type, name) : value;
-
- }
-
- /**
- * Returns the list of possible enums for a given attribute name.
- */
- public static Map<String, Integer> getEnumValues(String attributeName) {
- if (sEnumValueMap != null) {
- return sEnumValueMap.get(attributeName);
- }
-
- return null;
- }
-
- /**
- * Returns the platform build properties.
- */
- public static Map<String, String> getPlatformProperties() {
- return sPlatformProperties;
- }
-
- /**
- * Returns the bitmap for a specific path, from a specific project cache, or from the
- * framework cache.
- * @param value the path of the bitmap
- * @param projectKey the key of the project, or null to query the framework cache.
- * @return the cached Bitmap or null if not found.
- */
- public static Bitmap getCachedBitmap(String value, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey);
- if (map != null) {
- SoftReference<Bitmap> ref = map.get(value);
- if (ref != null) {
- return ref.get();
- }
- }
- } else {
- SoftReference<Bitmap> ref = sFrameworkBitmapCache.get(value);
- if (ref != null) {
- return ref.get();
- }
- }
-
- return null;
- }
-
- /**
- * Sets a bitmap in a project cache or in the framework cache.
- * @param value the path of the bitmap
- * @param bmp the Bitmap object
- * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
- */
- public static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<Bitmap>> map =
- sProjectBitmapCache.computeIfAbsent(projectKey, k -> new HashMap<>());
-
- map.put(value, new SoftReference<>(bmp));
- } else {
- sFrameworkBitmapCache.put(value, new SoftReference<>(bmp));
- }
- }
-
- /**
- * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
- * framework cache.
- * @param value the path of the 9 patch
- * @param projectKey the key of the project, or null to query the framework cache.
- * @return the cached 9 patch or null if not found.
- */
- public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
-
- if (map != null) {
- SoftReference<NinePatchChunk> ref = map.get(value);
- if (ref != null) {
- return ref.get();
- }
- }
- } else {
- SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
- if (ref != null) {
- return ref.get();
- }
- }
-
- return null;
- }
-
- /**
- * Sets a 9 patch chunk in a project cache or in the framework cache.
- * @param value the path of the 9 patch
- * @param ninePatch the 9 patch object
- * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
- */
- public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<NinePatchChunk>> map =
- sProject9PatchCache.computeIfAbsent(projectKey, k -> new HashMap<>());
-
- map.put(value, new SoftReference<>(ninePatch));
- } else {
- sFramework9PatchCache.put(value, new SoftReference<>(ninePatch));
- }
+ public int getApiLevel() {
+ return 0;
}
}
diff --git a/com/android/server/AppOpsService.java b/com/android/server/AppOpsService.java
index 4ffa5f1f..50b8df2a 100644
--- a/com/android/server/AppOpsService.java
+++ b/com/android/server/AppOpsService.java
@@ -491,8 +491,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return Collections.emptyList();
}
synchronized (this) {
- Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
- false /* uidMismatchExpected */);
+ Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
if (pkgOps == null) {
return null;
}
@@ -531,8 +530,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private void pruneOp(Op op, int uid, String packageName) {
if (op.time == 0 && op.rejectTime == 0) {
- Ops ops = getOpsRawLocked(uid, packageName, false /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, false);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
@@ -1048,9 +1046,7 @@ public class AppOpsService extends IAppOpsService.Stub {
public int checkPackage(int uid, String packageName) {
Preconditions.checkNotNull(packageName);
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- true /* uidMismatchExpected */);
- if (ops != null) {
+ if (getOpsRawLocked(uid, packageName, true) != null) {
return AppOpsManager.MODE_ALLOWED;
} else {
return AppOpsManager.MODE_ERRORED;
@@ -1094,8 +1090,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
@@ -1153,8 +1148,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
ClientState client = (ClientState)token;
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + resolvedPackageName);
@@ -1280,8 +1274,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return uidState;
}
- private Ops getOpsRawLocked(int uid, String packageName, boolean edit,
- boolean uidMismatchExpected) {
+ private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
return null;
@@ -1333,12 +1326,10 @@ public class AppOpsService extends IAppOpsService.Stub {
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
// under. Abort.
- if (!uidMismatchExpected) {
- RuntimeException ex = new RuntimeException("here");
- ex.fillInStackTrace();
- Slog.w(TAG, "Bad call: specified package " + packageName
- + " under uid " + uid + " but it is really " + pkgUid, ex);
- }
+ RuntimeException ex = new RuntimeException("here");
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Bad call: specified package " + packageName
+ + " under uid " + uid + " but it is really " + pkgUid, ex);
return null;
}
} finally {
@@ -1368,8 +1359,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
- Ops ops = getOpsRawLocked(uid, packageName, edit,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, edit);
if (ops == null) {
return null;
}
@@ -1403,8 +1393,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
// If we are the system, bypass user restrictions for certain codes
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
return false;
}
@@ -1724,8 +1713,7 @@ public class AppOpsService extends IAppOpsService.Stub {
out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid()));
synchronized (this) {
- Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(),
- false /* edit */, false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
// Should always be present as the list of PackageOps is generated
// from Ops.
if (ops != null) {
diff --git a/com/android/server/BatteryService.java b/com/android/server/BatteryService.java
index 47be0a70..5106c8d7 100644
--- a/com/android/server/BatteryService.java
+++ b/com/android/server/BatteryService.java
@@ -24,7 +24,6 @@ import android.os.PowerManager;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
@@ -36,10 +35,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
-import android.hardware.health.V2_0.HealthInfo;
-import android.hardware.health.V2_0.IHealth;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryProperties;
@@ -67,9 +62,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.NoSuchElementException;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -126,8 +118,8 @@ public final class BatteryService extends SystemService {
private final Object mLock = new Object();
- private HealthInfo mHealthInfo;
- private final HealthInfo mLastHealthInfo = new HealthInfo();
+ private BatteryProperties mBatteryProps;
+ private final BatteryProperties mLastBatteryProps = new BatteryProperties();
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -259,16 +251,16 @@ public final class BatteryService extends SystemService {
private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
- if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
+ if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.legacy.chargerAcOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.legacy.chargerUsbOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.legacy.chargerWirelessOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
return true;
}
return false;
@@ -285,15 +277,15 @@ public final class BatteryService extends SystemService {
* (becomes <= mLowBatteryWarningLevel).
*/
return !plugged
- && mHealthInfo.legacy.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel
+ && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mHealthInfo.legacy.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+ if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -314,7 +306,7 @@ public final class BatteryService extends SystemService {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
- if (mHealthInfo.legacy.batteryTemperature > mShutdownBatteryTemperature) {
+ if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -334,66 +326,25 @@ public final class BatteryService extends SystemService {
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = new HealthInfo();
- copy(mHealthInfo, props);
+ mBatteryProps = props;
// Process the new values.
processValuesLocked(false);
} else {
- copy(mLastHealthInfo, props);
+ mLastBatteryProps.set(props);
}
}
}
- private static void copy(HealthInfo dst, HealthInfo src) {
- dst.legacy.chargerAcOnline = src.legacy.chargerAcOnline;
- dst.legacy.chargerUsbOnline = src.legacy.chargerUsbOnline;
- dst.legacy.chargerWirelessOnline = src.legacy.chargerWirelessOnline;
- dst.legacy.maxChargingCurrent = src.legacy.maxChargingCurrent;
- dst.legacy.maxChargingVoltage = src.legacy.maxChargingVoltage;
- dst.legacy.batteryStatus = src.legacy.batteryStatus;
- dst.legacy.batteryHealth = src.legacy.batteryHealth;
- dst.legacy.batteryPresent = src.legacy.batteryPresent;
- dst.legacy.batteryLevel = src.legacy.batteryLevel;
- dst.legacy.batteryVoltage = src.legacy.batteryVoltage;
- dst.legacy.batteryTemperature = src.legacy.batteryTemperature;
- dst.legacy.batteryCurrent = src.legacy.batteryCurrent;
- dst.legacy.batteryCycleCount = src.legacy.batteryCycleCount;
- dst.legacy.batteryFullCharge = src.legacy.batteryFullCharge;
- dst.legacy.batteryChargeCounter = src.legacy.batteryChargeCounter;
- dst.legacy.batteryTechnology = src.legacy.batteryTechnology;
- dst.batteryCurrentAverage = src.batteryCurrentAverage;
- dst.batteryCapacity = src.batteryCapacity;
- dst.energyCounter = src.energyCounter;
- }
-
- // TODO(b/62229583): remove this function when BatteryProperties are completely replaced.
- private static void copy(HealthInfo dst, BatteryProperties src) {
- dst.legacy.chargerAcOnline = src.chargerAcOnline;
- dst.legacy.chargerUsbOnline = src.chargerUsbOnline;
- dst.legacy.chargerWirelessOnline = src.chargerWirelessOnline;
- dst.legacy.maxChargingCurrent = src.maxChargingCurrent;
- dst.legacy.maxChargingVoltage = src.maxChargingVoltage;
- dst.legacy.batteryStatus = src.batteryStatus;
- dst.legacy.batteryHealth = src.batteryHealth;
- dst.legacy.batteryPresent = src.batteryPresent;
- dst.legacy.batteryLevel = src.batteryLevel;
- dst.legacy.batteryVoltage = src.batteryVoltage;
- dst.legacy.batteryTemperature = src.batteryTemperature;
- dst.legacy.batteryFullCharge = src.batteryFullCharge;
- dst.legacy.batteryChargeCounter = src.batteryChargeCounter;
- dst.legacy.batteryTechnology = src.batteryTechnology;
- }
-
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
- mBatteryLevelCritical = (mHealthInfo.legacy.batteryLevel <= mCriticalBatteryLevel);
- if (mHealthInfo.legacy.chargerAcOnline) {
+ mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
+ if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
- } else if (mHealthInfo.legacy.chargerUsbOnline) {
+ } else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
- } else if (mHealthInfo.legacy.chargerWirelessOnline) {
+ } else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
@@ -401,17 +352,30 @@ public final class BatteryService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "Processing new values: "
- + "info=" + mHealthInfo
+ + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
+ + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
+ + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
+ + ", batteryStatus=" + mBatteryProps.batteryStatus
+ + ", batteryHealth=" + mBatteryProps.batteryHealth
+ + ", batteryPresent=" + mBatteryProps.batteryPresent
+ + ", batteryLevel=" + mBatteryProps.batteryLevel
+ + ", batteryTechnology=" + mBatteryProps.batteryTechnology
+ + ", batteryVoltage=" + mBatteryProps.batteryVoltage
+ + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
+ + ", batteryFullCharge=" + mBatteryProps.batteryFullCharge
+ + ", batteryTemperature=" + mBatteryProps.batteryTemperature
+ ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ ", mPlugType=" + mPlugType);
}
// Let the battery stats keep track of the current level.
try {
- mBatteryStats.setBatteryState(mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth,
- mPlugType, mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryTemperature,
- mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryChargeCounter,
- mHealthInfo.legacy.batteryFullCharge);
+ mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
+ mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
+ mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter,
+ mBatteryProps.batteryFullCharge);
} catch (RemoteException e) {
// Should never happen.
}
@@ -419,16 +383,16 @@ public final class BatteryService extends SystemService {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
- mHealthInfo.legacy.batteryLevel != mLastBatteryLevel ||
+ if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
+ mBatteryProps.batteryHealth != mLastBatteryHealth ||
+ mBatteryProps.batteryPresent != mLastBatteryPresent ||
+ mBatteryProps.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
- mHealthInfo.legacy.batteryVoltage != mLastBatteryVoltage ||
- mHealthInfo.legacy.batteryTemperature != mLastBatteryTemperature ||
- mHealthInfo.legacy.maxChargingCurrent != mLastMaxChargingCurrent ||
- mHealthInfo.legacy.maxChargingVoltage != mLastMaxChargingVoltage ||
- mHealthInfo.legacy.batteryChargeCounter != mLastChargeCounter ||
+ mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
+ mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
+ mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
+ mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
+ mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
@@ -437,33 +401,33 @@ public final class BatteryService extends SystemService {
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
- if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.legacy.batteryLevel) {
+ if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
- mDischargeStartLevel, mHealthInfo.legacy.batteryLevel);
+ mDischargeStartLevel, mBatteryProps.batteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
- mDischargeStartLevel = mHealthInfo.legacy.batteryLevel;
+ mDischargeStartLevel = mBatteryProps.batteryLevel;
}
}
- if (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
+ if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
+ mBatteryProps.batteryHealth != mLastBatteryHealth ||
+ mBatteryProps.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
- mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth, mHealthInfo.legacy.batteryPresent ? 1 : 0,
- mPlugType, mHealthInfo.legacy.batteryTechnology);
+ mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
+ mPlugType, mBatteryProps.batteryTechnology);
}
- if (mHealthInfo.legacy.batteryLevel != mLastBatteryLevel) {
+ if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
- mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryTemperature);
+ mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
@@ -476,16 +440,16 @@ public final class BatteryService extends SystemService {
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
- && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel) {
+ && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
} else {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
- } else if (mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
- } else if (force && mHealthInfo.legacy.batteryLevel >= mLowBatteryWarningLevel) {
+ } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
// absolutely check to see if we are now above the warning level.
mBatteryLevelLow = false;
@@ -532,7 +496,7 @@ public final class BatteryService extends SystemService {
}
});
} else if (mSentLowBatteryBroadcast &&
- mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -558,16 +522,16 @@ public final class BatteryService extends SystemService {
logOutlierLocked(dischargeDuration);
}
- mLastBatteryStatus = mHealthInfo.legacy.batteryStatus;
- mLastBatteryHealth = mHealthInfo.legacy.batteryHealth;
- mLastBatteryPresent = mHealthInfo.legacy.batteryPresent;
- mLastBatteryLevel = mHealthInfo.legacy.batteryLevel;
+ mLastBatteryStatus = mBatteryProps.batteryStatus;
+ mLastBatteryHealth = mBatteryProps.batteryHealth;
+ mLastBatteryPresent = mBatteryProps.batteryPresent;
+ mLastBatteryLevel = mBatteryProps.batteryLevel;
mLastPlugType = mPlugType;
- mLastBatteryVoltage = mHealthInfo.legacy.batteryVoltage;
- mLastBatteryTemperature = mHealthInfo.legacy.batteryTemperature;
- mLastMaxChargingCurrent = mHealthInfo.legacy.maxChargingCurrent;
- mLastMaxChargingVoltage = mHealthInfo.legacy.maxChargingVoltage;
- mLastChargeCounter = mHealthInfo.legacy.batteryChargeCounter;
+ mLastBatteryVoltage = mBatteryProps.batteryVoltage;
+ mLastBatteryTemperature = mBatteryProps.batteryTemperature;
+ mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
+ mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
+ mLastChargeCounter = mBatteryProps.batteryChargeCounter;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -579,26 +543,38 @@ public final class BatteryService extends SystemService {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- int icon = getIconLocked(mHealthInfo.legacy.batteryLevel);
+ int icon = getIconLocked(mBatteryProps.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
- intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.legacy.batteryStatus);
- intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.legacy.batteryHealth);
- intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.legacy.batteryPresent);
- intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.legacy.batteryLevel);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
+ intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
+ intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
+ intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
- intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.legacy.batteryVoltage);
- intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
- intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
+ intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
- intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
+ intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
if (DEBUG) {
- Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
- + ", info:" + mHealthInfo.toString());
+ Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
+ ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
+ ", health:" + mBatteryProps.batteryHealth +
+ ", present:" + mBatteryProps.batteryPresent +
+ ", voltage: " + mBatteryProps.batteryVoltage +
+ ", temperature: " + mBatteryProps.batteryTemperature +
+ ", technology: " + mBatteryProps.batteryTechnology +
+ ", AC powered:" + mBatteryProps.chargerAcOnline +
+ ", USB powered:" + mBatteryProps.chargerUsbOnline +
+ ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
+ ", icon:" + icon + ", invalid charger:" + mInvalidCharger +
+ ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
+ ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage +
+ ", chargeCounter:" + mBatteryProps.batteryChargeCounter);
}
mHandler.post(new Runnable() {
@@ -659,14 +635,14 @@ public final class BatteryService extends SystemService {
long durationThreshold = Long.parseLong(durationThresholdString);
int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
if (duration <= durationThreshold &&
- mDischargeStartLevel - mHealthInfo.legacy.batteryLevel >= dischargeThreshold) {
+ mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {
// If the discharge cycle is bad enough we want to know about it.
logBatteryStatsLocked();
}
if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
" discharge threshold: " + dischargeThreshold);
if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
- (mDischargeStartLevel - mHealthInfo.legacy.batteryLevel));
+ (mDischargeStartLevel - mBatteryProps.batteryLevel));
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
durationThresholdString + " or " + dischargeThresholdString);
@@ -675,14 +651,14 @@ public final class BatteryService extends SystemService {
}
private int getIconLocked(int level) {
- if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
+ if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
- } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+ } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
return com.android.internal.R.drawable.stat_sys_battery;
- } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
- || mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+ } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+ || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
- && mHealthInfo.legacy.batteryLevel >= 100) {
+ && mBatteryProps.batteryLevel >= 100) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else {
return com.android.internal.R.drawable.stat_sys_battery;
@@ -744,11 +720,11 @@ public final class BatteryService extends SystemService {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ mLastBatteryProps.set(mBatteryProps);
}
- mHealthInfo.legacy.chargerAcOnline = false;
- mHealthInfo.legacy.chargerUsbOnline = false;
- mHealthInfo.legacy.chargerWirelessOnline = false;
+ mBatteryProps.chargerAcOnline = false;
+ mBatteryProps.chargerUsbOnline = false;
+ mBatteryProps.chargerWirelessOnline = false;
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
@@ -775,30 +751,30 @@ public final class BatteryService extends SystemService {
}
try {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ mLastBatteryProps.set(mBatteryProps);
}
boolean update = true;
switch (key) {
case "present":
- mHealthInfo.legacy.batteryPresent = Integer.parseInt(value) != 0;
+ mBatteryProps.batteryPresent = Integer.parseInt(value) != 0;
break;
case "ac":
- mHealthInfo.legacy.chargerAcOnline = Integer.parseInt(value) != 0;
+ mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
break;
case "usb":
- mHealthInfo.legacy.chargerUsbOnline = Integer.parseInt(value) != 0;
+ mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
break;
case "wireless":
- mHealthInfo.legacy.chargerWirelessOnline = Integer.parseInt(value) != 0;
+ mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
break;
case "status":
- mHealthInfo.legacy.batteryStatus = Integer.parseInt(value);
+ mBatteryProps.batteryStatus = Integer.parseInt(value);
break;
case "level":
- mHealthInfo.legacy.batteryLevel = Integer.parseInt(value);
+ mBatteryProps.batteryLevel = Integer.parseInt(value);
break;
case "temp":
- mHealthInfo.legacy.batteryTemperature = Integer.parseInt(value);
+ mBatteryProps.batteryTemperature = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
@@ -830,7 +806,7 @@ public final class BatteryService extends SystemService {
try {
if (mUpdatesStopped) {
mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
+ mBatteryProps.set(mLastBatteryProps);
processValuesFromShellLocked(pw, opts);
}
} finally {
@@ -857,20 +833,20 @@ public final class BatteryService extends SystemService {
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
- pw.println(" AC powered: " + mHealthInfo.legacy.chargerAcOnline);
- pw.println(" USB powered: " + mHealthInfo.legacy.chargerUsbOnline);
- pw.println(" Wireless powered: " + mHealthInfo.legacy.chargerWirelessOnline);
- pw.println(" Max charging current: " + mHealthInfo.legacy.maxChargingCurrent);
- pw.println(" Max charging voltage: " + mHealthInfo.legacy.maxChargingVoltage);
- pw.println(" Charge counter: " + mHealthInfo.legacy.batteryChargeCounter);
- pw.println(" status: " + mHealthInfo.legacy.batteryStatus);
- pw.println(" health: " + mHealthInfo.legacy.batteryHealth);
- pw.println(" present: " + mHealthInfo.legacy.batteryPresent);
- pw.println(" level: " + mHealthInfo.legacy.batteryLevel);
+ pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
+ pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
+ pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
+ pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
+ pw.println(" Max charging voltage: " + mBatteryProps.maxChargingVoltage);
+ pw.println(" Charge counter: " + mBatteryProps.batteryChargeCounter);
+ pw.println(" status: " + mBatteryProps.batteryStatus);
+ pw.println(" health: " + mBatteryProps.batteryHealth);
+ pw.println(" present: " + mBatteryProps.batteryPresent);
+ pw.println(" level: " + mBatteryProps.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mHealthInfo.legacy.batteryVoltage);
- pw.println(" temperature: " + mHealthInfo.legacy.batteryTemperature);
- pw.println(" technology: " + mHealthInfo.legacy.batteryTechnology);
+ pw.println(" voltage: " + mBatteryProps.batteryVoltage);
+ pw.println(" temperature: " + mBatteryProps.batteryTemperature);
+ pw.println(" technology: " + mBatteryProps.batteryTechnology);
} else {
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
@@ -884,25 +860,25 @@ public final class BatteryService extends SystemService {
synchronized (mLock) {
proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE;
- if (mHealthInfo.legacy.chargerAcOnline) {
+ if (mBatteryProps.chargerAcOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC;
- } else if (mHealthInfo.legacy.chargerUsbOnline) {
+ } else if (mBatteryProps.chargerUsbOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB;
- } else if (mHealthInfo.legacy.chargerWirelessOnline) {
+ } else if (mBatteryProps.chargerWirelessOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
- proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
- proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.legacy.batteryStatus);
- proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.legacy.batteryHealth);
- proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.legacy.batteryPresent);
- proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.legacy.batteryLevel);
+ proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
+ proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
+ proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
+ proto.write(BatteryServiceDumpProto.STATUS, mBatteryProps.batteryStatus);
+ proto.write(BatteryServiceDumpProto.HEALTH, mBatteryProps.batteryHealth);
+ proto.write(BatteryServiceDumpProto.IS_PRESENT, mBatteryProps.batteryPresent);
+ proto.write(BatteryServiceDumpProto.LEVEL, mBatteryProps.batteryLevel);
proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
- proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.legacy.batteryVoltage);
- proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
- proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
+ proto.write(BatteryServiceDumpProto.VOLTAGE, mBatteryProps.batteryVoltage);
+ proto.write(BatteryServiceDumpProto.TEMPERATURE, mBatteryProps.batteryTemperature);
+ proto.write(BatteryServiceDumpProto.TECHNOLOGY, mBatteryProps.batteryTechnology);
}
proto.flush();
}
@@ -935,8 +911,8 @@ public final class BatteryService extends SystemService {
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
- final int level = mHealthInfo.legacy.batteryLevel;
- final int status = mHealthInfo.legacy.batteryStatus;
+ final int level = mBatteryProps.batteryLevel;
+ final int status = mBatteryProps.batteryStatus;
if (level < mLowBatteryWarningLevel) {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
// Solid red when battery is charging
@@ -1009,7 +985,7 @@ public final class BatteryService extends SystemService {
@Override
public int getBatteryLevel() {
synchronized (mLock) {
- return mHealthInfo.legacy.batteryLevel;
+ return mBatteryProps.batteryLevel;
}
}
@@ -1027,121 +1003,4 @@ public final class BatteryService extends SystemService {
}
}
}
-
- /**
- * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
- * necessary.
- *
- * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
- * the internal service is refreshed.
- * On death of an existing IHealth service, the internal service is NOT cleared to avoid
- * race condition between death notification and new service notification. Hence,
- * a caller must check for transaction errors when calling into the service.
- *
- * @hide Should only be used internally.
- */
- @VisibleForTesting
- static final class HealthServiceWrapper {
- private static final String TAG = "HealthServiceWrapper";
- public static final String INSTANCE_HEALTHD = "backup";
- public static final String INSTANCE_VENDOR = "default";
- // All interesting instances, sorted by priority high -> low.
- private static final List<String> sAllInstances =
- Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
-
- private final IServiceNotification mNotification = new Notification();
- private Callback mCallback;
- private IHealthSupplier mHealthSupplier;
-
- /**
- * init should be called after constructor. For testing purposes, init is not called by
- * constructor.
- */
- HealthServiceWrapper() {
- }
-
- /**
- * Start monitoring registration of new IHealth services. Only instances that are in
- * {@code sAllInstances} and in device / framework manifest are used. This function should
- * only be called once.
- * @throws RemoteException transaction error when talking to IServiceManager
- * @throws NoSuchElementException if one of the following cases:
- * - No service manager;
- * - none of {@code sAllInstances} are in manifests (i.e. not
- * available on this device), or none of these instances are available to current
- * process.
- * @throws NullPointerException when callback is null or supplier is null
- */
- void init(Callback callback,
- IServiceManagerSupplier managerSupplier,
- IHealthSupplier healthSupplier)
- throws RemoteException, NoSuchElementException, NullPointerException {
- if (callback == null || managerSupplier == null || healthSupplier == null)
- throw new NullPointerException();
-
- mCallback = callback;
- mHealthSupplier = healthSupplier;
-
- IServiceManager manager = managerSupplier.get();
- for (String name : sAllInstances) {
- if (manager.getTransport(IHealth.kInterfaceName, name) ==
- IServiceManager.Transport.EMPTY) {
- continue;
- }
-
- manager.registerForNotifications(IHealth.kInterfaceName, name, mNotification);
- Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + name);
- return;
- }
-
- throw new NoSuchElementException(String.format(
- "No IHealth service instance among %s is available. Perhaps no permission?",
- sAllInstances.toString()));
- }
-
- interface Callback {
- /**
- * This function is invoked asynchronously when a new and related IServiceNotification
- * is received.
- * @param service the recently retrieved service from IServiceManager.
- * Can be a dead service before service notification of a new service is delivered.
- * Implementation must handle cases for {@link RemoteException}s when calling
- * into service.
- * @param instance instance name.
- */
- void onRegistration(IHealth service, String instance);
- }
-
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IServiceManagerSupplier {
- IServiceManager get() throws NoSuchElementException, RemoteException;
- }
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IHealthSupplier {
- IHealth get(String instanceName) throws NoSuchElementException, RemoteException;
- }
-
- private class Notification extends IServiceNotification.Stub {
- @Override
- public final void onRegistration(String interfaceName, String instanceName,
- boolean preexisting) {
- if (!IHealth.kInterfaceName.equals(interfaceName)) return;
- if (!sAllInstances.contains(instanceName)) return;
- try {
- IHealth service = mHealthSupplier.get(instanceName);
- Slog.i(TAG, "health: new instance registered " + instanceName);
- mCallback.onRegistration(service, instanceName);
- } catch (NoSuchElementException | RemoteException ex) {
- Slog.e(TAG, "health: Cannot get instance '" + instanceName + "': " +
- ex.getMessage() + ". Perhaps no permission?");
- }
- }
- }
- }
}
diff --git a/com/android/server/IntentResolver.java b/com/android/server/IntentResolver.java
index 119c9df6..40499c96 100644
--- a/com/android/server/IntentResolver.java
+++ b/com/android/server/IntentResolver.java
@@ -38,8 +38,6 @@ import android.util.Printer;
import android.content.Intent;
import android.content.IntentFilter;
-import android.util.proto.ProtoOutputStream;
-
import com.android.internal.util.FastPrintWriter;
/**
@@ -281,31 +279,6 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
return printedSomething;
}
- void writeProtoMap(ProtoOutputStream proto, long fieldId, ArrayMap<String, F[]> map) {
- int N = map.size();
- for (int mapi = 0; mapi < N; mapi++) {
- long token = proto.start(fieldId);
- proto.write(IntentResolverProto.ArrayMapEntry.KEY, map.keyAt(mapi));
- for (F f : map.valueAt(mapi)) {
- if (f != null) {
- proto.write(IntentResolverProto.ArrayMapEntry.VALUES, f.toString());
- }
- }
- proto.end(token);
- }
- }
-
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- writeProtoMap(proto, IntentResolverProto.FULL_MIME_TYPES, mTypeToFilter);
- writeProtoMap(proto, IntentResolverProto.BASE_MIME_TYPES, mBaseTypeToFilter);
- writeProtoMap(proto, IntentResolverProto.WILD_MIME_TYPES, mWildTypeToFilter);
- writeProtoMap(proto, IntentResolverProto.SCHEMES, mSchemeToFilter);
- writeProtoMap(proto, IntentResolverProto.NON_DATA_ACTIONS, mActionToFilter);
- writeProtoMap(proto, IntentResolverProto.MIME_TYPED_ACTIONS, mTypedActionToFilter);
- proto.end(token);
- }
-
public boolean dump(PrintWriter out, String title, String prefix, String packageName,
boolean printFilter, boolean collapseDuplicates) {
String innerPrefix = prefix + " ";
diff --git a/com/android/server/SystemServer.java b/com/android/server/SystemServer.java
index 49dd5285..92cbd3d5 100644
--- a/com/android/server/SystemServer.java
+++ b/com/android/server/SystemServer.java
@@ -125,7 +125,6 @@ import java.util.Timer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
-import static android.os.IServiceManager.DUMP_PRIORITY_CRITICAL;
import static android.view.Display.DEFAULT_DISPLAY;
public final class SystemServer {
@@ -825,8 +824,7 @@ public final class SystemServer {
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
- ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
- DUMP_PRIORITY_CRITICAL);
+ ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
traceEnd();
@@ -1642,7 +1640,11 @@ public final class SystemServer {
traceEnd();
traceBeginAndSlog("MakePackageManagerServiceReady");
- mPackageManagerService.systemReady();
+ try {
+ mPackageManagerService.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Package Manager Service ready", e);
+ }
traceEnd();
traceBeginAndSlog("MakeDisplayManagerServiceReady");
diff --git a/com/android/server/VibratorService.java b/com/android/server/VibratorService.java
index 8b79b9dd..046eb761 100644
--- a/com/android/server/VibratorService.java
+++ b/com/android/server/VibratorService.java
@@ -373,24 +373,12 @@ public class VibratorService extends IVibratorService.Stub
if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
&& newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
}
return;
}
}
- // If the current vibration is repeating and the incoming one is non-repeating, then ignore
- // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
- // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
- // vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- return;
- }
-
Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
// Only link against waveforms since they potentially don't have a finish if
@@ -416,16 +404,6 @@ public class VibratorService extends IVibratorService.Stub
}
}
- private static boolean isRepeatingVibration(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Waveform) {
- final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
- if (waveform.getRepeatIndex() >= 0) {
- return true;
- }
- }
- return false;
- }
-
private void addToPreviousVibrationsLocked(Vibration vib) {
if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
mPreviousVibrations.removeFirst();
diff --git a/com/android/server/accessibility/AccessibilityInputFilter.java b/com/android/server/accessibility/AccessibilityInputFilter.java
index f6fcaae4..c60647fa 100644
--- a/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,9 +19,6 @@ package com.android.server.accessibility;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
-import android.util.DebugUtils;
-import android.util.ExceptionUtils;
-import android.util.Log;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -34,7 +31,6 @@ import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.util.BitUtils;
import com.android.server.LocalServices;
/**
@@ -192,7 +188,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
if (mEventHandler == null) {
- if (DEBUG) Slog.d(TAG, "mEventHandler == null for event " + event);
super.onInputEvent(event, policyFlags);
return;
}
@@ -344,8 +339,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
MotionEvent transformedEvent = MotionEvent.obtain(event);
mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
transformedEvent.recycle();
- } else {
- if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event);
}
}
@@ -373,20 +366,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
@Override
- public EventStreamTransformation getNext() {
- return null;
- }
-
- @Override
public void clearEvents(int inputSource) {
/* do nothing */
}
void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
- if (DEBUG) {
- Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x"
- + Integer.toHexString(enabledFeatures) + ")");
- }
if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
return;
}
@@ -413,8 +397,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
private void enableFeatures() {
- if (DEBUG) Slog.i(TAG, "enableFeatures()");
-
resetStreamState();
if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
@@ -461,7 +443,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
private void addFirstEventHandler(EventStreamTransformation handler) {
if (mEventHandler != null) {
- handler.setNext(mEventHandler);
+ handler.setNext(mEventHandler);
} else {
handler.setNext(this);
}
diff --git a/com/android/server/accessibility/AutoclickController.java b/com/android/server/accessibility/AutoclickController.java
index f5b0eb1e..892e9da4 100644
--- a/com/android/server/accessibility/AutoclickController.java
+++ b/com/android/server/accessibility/AutoclickController.java
@@ -23,12 +23,15 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Slog;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
/**
@@ -52,10 +55,11 @@ import android.view.accessibility.AccessibilityManager;
*
* Each instance is associated to a single user (and it does not handle user switch itself).
*/
-public class AutoclickController extends BaseEventStreamTransformation {
+public class AutoclickController implements EventStreamTransformation {
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+ private EventStreamTransformation mNext;
private final Context mContext;
private final int mUserId;
@@ -84,7 +88,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
mClickScheduler.cancel();
}
- super.onMotionEvent(event, rawEvent, policyFlags);
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
}
@Override
@@ -97,7 +103,21 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
- super.onKeyEvent(event, policyFlags);
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
}
@Override
@@ -106,7 +126,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
mClickScheduler.cancel();
}
- super.clearEvents(inputSource);
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
}
@Override
@@ -396,7 +418,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
* Creates and forwards click event sequence.
*/
private void sendClick() {
- if (mLastMotionEvent == null || getNext() == null) {
+ if (mLastMotionEvent == null || mNext == null) {
return;
}
@@ -426,10 +448,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
MotionEvent upEvent = MotionEvent.obtain(downEvent);
upEvent.setAction(MotionEvent.ACTION_UP);
- AutoclickController.super.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
+ mNext.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
downEvent.recycle();
- AutoclickController.super.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
+ mNext.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
upEvent.recycle();
}
diff --git a/com/android/server/accessibility/BaseEventStreamTransformation.java b/com/android/server/accessibility/BaseEventStreamTransformation.java
deleted file mode 100644
index ce54586c..00000000
--- a/com/android/server/accessibility/BaseEventStreamTransformation.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- ** Copyright 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.accessibility;
-
-abstract class BaseEventStreamTransformation implements EventStreamTransformation {
- private EventStreamTransformation mNext;
-
- @Override
- public void setNext(EventStreamTransformation next) {
- mNext = next;
- }
-
- @Override
- public EventStreamTransformation getNext() {
- return mNext;
- }
-} \ No newline at end of file
diff --git a/com/android/server/accessibility/EventStreamTransformation.java b/com/android/server/accessibility/EventStreamTransformation.java
index 7982996e..fdc40984 100644
--- a/com/android/server/accessibility/EventStreamTransformation.java
+++ b/com/android/server/accessibility/EventStreamTransformation.java
@@ -65,12 +65,7 @@ interface EventStreamTransformation {
* @param rawEvent The raw motion event.
* @param policyFlags Policy flags for the event.
*/
- default void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- EventStreamTransformation next = getNext();
- if (next != null) {
- next.onMotionEvent(event, rawEvent, policyFlags);
- }
- }
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
* Receives a key event.
@@ -78,24 +73,14 @@ interface EventStreamTransformation {
* @param event The key event.
* @param policyFlags Policy flags for the event.
*/
- default void onKeyEvent(KeyEvent event, int policyFlags) {
- EventStreamTransformation next = getNext();
- if (next != null) {
- next.onKeyEvent(event, policyFlags);
- }
- }
+ public void onKeyEvent(KeyEvent event, int policyFlags);
/**
* Receives an accessibility event.
*
* @param event The accessibility event.
*/
- default void onAccessibilityEvent(AccessibilityEvent event) {
- EventStreamTransformation next = getNext();
- if (next != null) {
- next.onAccessibilityEvent(event);
- }
- };
+ public void onAccessibilityEvent(AccessibilityEvent event);
/**
* Sets the next transformation.
@@ -105,26 +90,14 @@ interface EventStreamTransformation {
public void setNext(EventStreamTransformation next);
/**
- * Gets the next transformation.
- *
- * @return The next transformation.
- */
- public EventStreamTransformation getNext();
-
- /**
* Clears internal state associated with events from specific input source.
*
* @param inputSource The input source class for which transformation state should be cleared.
*/
- default void clearEvents(int inputSource) {
- EventStreamTransformation next = getNext();
- if (next != null) {
- next.clearEvents(inputSource);
- }
- }
+ public void clearEvents(int inputSource);
/**
* Destroys this transformation.
*/
- default void onDestroy() {}
+ public void onDestroy();
}
diff --git a/com/android/server/accessibility/KeyboardInterceptor.java b/com/android/server/accessibility/KeyboardInterceptor.java
index 77249452..f00a9540 100644
--- a/com/android/server/accessibility/KeyboardInterceptor.java
+++ b/com/android/server/accessibility/KeyboardInterceptor.java
@@ -22,12 +22,14 @@ import android.os.SystemClock;
import android.util.Pools;
import android.util.Slog;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
+import android.view.accessibility.AccessibilityEvent;
/**
* Intercepts key events and forwards them to accessibility manager service.
*/
-public class KeyboardInterceptor extends BaseEventStreamTransformation implements Handler.Callback {
+public class KeyboardInterceptor implements EventStreamTransformation, Handler.Callback {
private static final int MESSAGE_PROCESS_QUEUED_EVENTS = 1;
private static final String LOG_TAG = "KeyboardInterceptor";
@@ -35,6 +37,7 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement
private final WindowManagerPolicy mPolicy;
private final Handler mHandler;
+ private EventStreamTransformation mNext;
private KeyEventHolder mEventQueueStart;
private KeyEventHolder mEventQueueEnd;
@@ -62,6 +65,13 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement
}
@Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
/*
* Certain keys have system-level behavior that affects accessibility services.
@@ -80,6 +90,29 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement
}
@Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
+ public void clearEvents(int inputSource) {
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+
+ @Override
public boolean handleMessage(Message msg) {
if (msg.what != MESSAGE_PROCESS_QUEUED_EVENTS) {
Slog.e(LOG_TAG, "Unexpected message type");
diff --git a/com/android/server/accessibility/MagnificationController.java b/com/android/server/accessibility/MagnificationController.java
index a10b7a20..98b8e6b7 100644
--- a/com/android/server/accessibility/MagnificationController.java
+++ b/com/android/server/accessibility/MagnificationController.java
@@ -56,7 +56,6 @@ import java.util.Locale;
* constraints.
*/
class MagnificationController implements Handler.Callback {
- private static final boolean DEBUG = false;
private static final String LOG_TAG = "MagnificationController";
public static final float MIN_SCALE = 1.0f;
@@ -510,12 +509,6 @@ class MagnificationController implements Handler.Callback {
private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY,
boolean animate, int id) {
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
- + ", centerY = " + centerY + ", animate = " + animate + ", id = " + id
- + ")");
- }
final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
sendSpecToAnimation(mCurrentMagnificationSpec, animate);
if (isMagnifying() && (id != INVALID_ID)) {
@@ -542,9 +535,7 @@ class MagnificationController implements Handler.Callback {
final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
- onMagnificationChangedLocked();
- }
+ updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
if (id != INVALID_ID) {
mIdOfLastServiceToMagnify = id;
}
@@ -642,11 +633,6 @@ class MagnificationController implements Handler.Callback {
}
private boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
- + ", nonNormOffsetY = " + nonNormOffsetY + ")");
- }
boolean changed = false;
final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
@@ -764,9 +750,6 @@ class MagnificationController implements Handler.Callback {
}
private void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
- }
if (Thread.currentThread().getId() == mMainThreadId) {
mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
} else {
diff --git a/com/android/server/accessibility/MagnificationGestureHandler.java b/com/android/server/accessibility/MagnificationGestureHandler.java
index 9b2b4eb7..d6452f87 100644
--- a/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -42,12 +42,14 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
@@ -100,23 +102,31 @@ import com.android.internal.annotations.VisibleForTesting;
* 7. The magnification scale will be persisted in settings and in the cloud.
*/
@SuppressWarnings("WeakerAccess")
-class MagnificationGestureHandler extends BaseEventStreamTransformation {
- private static final String LOG_TAG = "MagnificationGestureHandler";
+class MagnificationGestureHandler implements EventStreamTransformation {
+ private static final String LOG_TAG = "MagnificationEventHandler";
private static final boolean DEBUG_ALL = false;
private static final boolean DEBUG_STATE_TRANSITIONS = false || DEBUG_ALL;
private static final boolean DEBUG_DETECTING = false || DEBUG_ALL;
- private static final boolean DEBUG_PANNING_SCALING = false || DEBUG_ALL;
+ private static final boolean DEBUG_PANNING = false || DEBUG_ALL;
+
+ /** @see #handleMotionEventStateDelegating */
+ @VisibleForTesting static final int STATE_DELEGATING = 1;
+ /** @see DetectingStateHandler */
+ @VisibleForTesting static final int STATE_DETECTING = 2;
+ /** @see ViewportDraggingStateHandler */
+ @VisibleForTesting static final int STATE_VIEWPORT_DRAGGING = 3;
+ /** @see PanningScalingStateHandler */
+ @VisibleForTesting static final int STATE_PANNING_SCALING = 4;
private static final float MIN_SCALE = 2.0f;
private static final float MAX_SCALE = 5.0f;
@VisibleForTesting final MagnificationController mMagnificationController;
- @VisibleForTesting final DelegatingState mDelegatingState;
- @VisibleForTesting final DetectingState mDetectingState;
- @VisibleForTesting final PanningScalingState mPanningScalingState;
- @VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+ @VisibleForTesting final DetectingStateHandler mDetectingStateHandler;
+ @VisibleForTesting final PanningScalingStateHandler mPanningScalingStateHandler;
+ @VisibleForTesting final ViewportDraggingStateHandler mViewportDraggingStateHandler;
private final ScreenStateReceiver mScreenStateReceiver;
@@ -128,12 +138,21 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
final boolean mDetectTripleTap;
/**
- * Whether {@link DetectingState#mShortcutTriggered shortcut} is enabled
+ * Whether {@link #mShortcutTriggered shortcut} is enabled
*/
final boolean mDetectShortcutTrigger;
- @VisibleForTesting State mCurrentState;
- @VisibleForTesting State mPreviousState;
+ EventStreamTransformation mNext;
+
+ @VisibleForTesting int mCurrentState;
+ @VisibleForTesting int mPreviousState;
+
+ @VisibleForTesting boolean mShortcutTriggered;
+
+ /**
+ * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING}
+ */
+ long mDelegatingStateDownTime;
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
@@ -155,10 +174,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
boolean detectShortcutTrigger) {
mMagnificationController = magnificationController;
- mDelegatingState = new DelegatingState();
- mDetectingState = new DetectingState(context);
- mViewportDraggingState = new ViewportDraggingState();
- mPanningScalingState = new PanningScalingState(context);
+ mDetectingStateHandler = new DetectingStateHandler(context);
+ mViewportDraggingStateHandler = new ViewportDraggingStateHandler();
+ mPanningScalingStateHandler =
+ new PanningScalingStateHandler(context);
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
@@ -170,29 +189,62 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mScreenStateReceiver = null;
}
- transitionTo(mDetectingState);
+ transitionTo(STATE_DETECTING);
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (DEBUG_ALL) Slog.i(LOG_TAG, "onMotionEvent(" + event + ")");
-
if ((!mDetectTripleTap && !mDetectShortcutTrigger)
|| !event.isFromSource(SOURCE_TOUCHSCREEN)) {
dispatchTransformedEvent(event, rawEvent, policyFlags);
return;
}
+ // Local copy to avoid dispatching the same event to more than one state handler
+ // in case mPanningScalingStateHandler changes mCurrentState
+ int currentState = mCurrentState;
+ mPanningScalingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ switch (currentState) {
+ case STATE_DELEGATING: {
+ handleMotionEventStateDelegating(event, rawEvent, policyFlags);
+ }
+ break;
+ case STATE_DETECTING: {
+ mDetectingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ break;
+ case STATE_VIEWPORT_DRAGGING: {
+ mViewportDraggingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ break;
+ case STATE_PANNING_SCALING: {
+ // mPanningScalingStateHandler handles events only
+ // if this is the current state since it uses ScaleGestureDetector
+ // and a GestureDetector which need well formed event stream.
+ }
+ break;
+ default: {
+ throw new IllegalStateException("Unknown state: " + currentState);
+ }
+ }
+ }
- handleEventWith(mCurrentState, event, rawEvent, policyFlags);
+ @Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
}
- private void handleEventWith(State stateHandler,
- MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- // To keep InputEventConsistencyVerifiers within GestureDetectors happy
- mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
- mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
- stateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
}
@Override
@@ -201,16 +253,13 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
clearAndTransitionToStateDetecting();
}
- super.clearEvents(inputSource);
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
}
@Override
public void onDestroy() {
- if (DEBUG_STATE_TRANSITIONS) {
- Slog.i(LOG_TAG, "onDestroy(); delayed = "
- + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue));
- }
-
if (mScreenStateReceiver != null) {
mScreenStateReceiver.unregister();
}
@@ -223,21 +272,59 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
} else {
- mDetectingState.toggleShortcutTriggered();
+ toggleShortcutTriggered();
}
}
}
+ private void toggleShortcutTriggered() {
+ setShortcutTriggered(!mShortcutTriggered);
+ }
+
+ private void setShortcutTriggered(boolean state) {
+ if (mShortcutTriggered == state) {
+ return;
+ }
+
+ mShortcutTriggered = state;
+ mMagnificationController.setForceShowMagnifiableBounds(state);
+ }
+
void clearAndTransitionToStateDetecting() {
- mCurrentState = mDelegatingState;
- mDetectingState.clear();
- mViewportDraggingState.clear();
- mPanningScalingState.clear();
+ setShortcutTriggered(false);
+ mCurrentState = STATE_DETECTING;
+ mDetectingStateHandler.clear();
+ mViewportDraggingStateHandler.clear();
+ mPanningScalingStateHandler.clear();
+ }
+
+ private void handleMotionEventStateDelegating(MotionEvent event,
+ MotionEvent rawEvent, int policyFlags) {
+ if (event.getActionMasked() == ACTION_UP) {
+ transitionTo(STATE_DETECTING);
+ }
+ delegateEvent(event, rawEvent, policyFlags);
+ }
+
+ void delegateEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mDelegatingStateDownTime = event.getDownTime();
+ }
+ if (mNext != null) {
+ // We cache some events to see if the user wants to trigger magnification.
+ // If no magnification is triggered we inject these events with adjusted
+ // time and down time to prevent subsequent transformations being confused
+ // by stale events. After the cached events, which always have a down, are
+ // injected we need to also update the down time of all subsequent non cached
+ // events. All delegated events cached and non-cached are delivered here.
+ event.setDownTime(mDelegatingStateDownTime);
+ dispatchTransformedEvent(event, rawEvent, policyFlags);
+ }
}
private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
+ if (mNext == null) return; // Nowhere to dispatch to
// If the touchscreen event is within the magnified portion of the screen we have
// to change its location to be where the user thinks he is poking the
@@ -264,7 +351,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
event.getFlags());
}
- super.onMotionEvent(event, rawEvent, policyFlags);
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
}
private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
@@ -299,10 +386,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
return mTempPointerProperties;
}
- private void transitionTo(State state) {
+ private void transitionTo(int state) {
if (DEBUG_STATE_TRANSITIONS) {
- Slog.i(LOG_TAG,
- (State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
+ Slog.i(LOG_TAG, (stateToString(mCurrentState) + " -> " + stateToString(state)
+ " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
.replace(getClass().getName(), ""));
}
@@ -310,40 +396,40 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mCurrentState = state;
}
- interface State {
- void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
+ private static String stateToString(int state) {
+ switch (state) {
+ case STATE_DELEGATING: return "STATE_DELEGATING";
+ case STATE_DETECTING: return "STATE_DETECTING";
+ case STATE_VIEWPORT_DRAGGING: return "STATE_VIEWPORT_DRAGGING";
+ case STATE_PANNING_SCALING: return "STATE_PANNING_SCALING";
+ case 0: return "0";
+ default: throw new IllegalArgumentException("Unknown state: " + state);
+ }
+ }
- default void clear() {}
+ private interface MotionEventHandler {
- default String name() {
- return getClass().getSimpleName();
- }
+ void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
- static String nameOf(@Nullable State s) {
- return s != null ? s.name() : "null";
- }
+ void clear();
}
/**
* This class determines if the user is performing a scale or pan gesture.
*
- * Unlike when {@link ViewportDraggingState dragging the viewport}, in panning mode the viewport
- * moves in the same direction as the fingers, and allows to easily and precisely scale the
- * magnification level.
- * This makes it the preferred mode for one-off adjustments, due to its precision and ease of
- * triggering.
+ * @see #STATE_PANNING_SCALING
*/
- final class PanningScalingState extends SimpleOnGestureListener
- implements OnScaleGestureListener, State {
+ final class PanningScalingStateHandler extends SimpleOnGestureListener
+ implements OnScaleGestureListener, MotionEventHandler {
private final ScaleGestureDetector mScaleGestureDetector;
- private final GestureDetector mScrollGestureDetector;
+ private final GestureDetector mGestureDetector;
final float mScalingThreshold;
float mInitialScaleFactor = -1;
boolean mScaling;
- public PanningScalingState(Context context) {
+ public PanningScalingStateHandler(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
@@ -351,27 +437,35 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mScalingThreshold = scaleValue.getFloat();
mScaleGestureDetector = new ScaleGestureDetector(context, this);
mScaleGestureDetector.setQuickScaleEnabled(false);
- mScrollGestureDetector = new GestureDetector(context, this);
+ mGestureDetector = new GestureDetector(context, this);
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- int action = event.getActionMasked();
+ // Dispatches #onScaleBegin, #onScale, #onScaleEnd
+ mScaleGestureDetector.onTouchEvent(event);
+ // Dispatches #onScroll
+ mGestureDetector.onTouchEvent(event);
+ if (mCurrentState != STATE_PANNING_SCALING) {
+ return;
+ }
+
+ int action = event.getActionMasked();
if (action == ACTION_POINTER_UP
&& event.getPointerCount() == 2 // includes the pointer currently being released
- && mPreviousState == mViewportDraggingState) {
+ && mPreviousState == STATE_VIEWPORT_DRAGGING) {
- persistScaleAndTransitionTo(mViewportDraggingState);
+ persistScaleAndTransitionTo(STATE_VIEWPORT_DRAGGING);
} else if (action == ACTION_UP) {
- persistScaleAndTransitionTo(mDetectingState);
+ persistScaleAndTransitionTo(STATE_DETECTING);
}
}
- public void persistScaleAndTransitionTo(State state) {
+ public void persistScaleAndTransitionTo(int state) {
mMagnificationController.persistScale();
clear();
transitionTo(state);
@@ -380,16 +474,16 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
@Override
public boolean onScroll(MotionEvent first, MotionEvent second,
float distanceX, float distanceY) {
- if (mCurrentState != mPanningScalingState) {
+ if (mCurrentState != STATE_PANNING_SCALING) {
return true;
}
- if (DEBUG_PANNING_SCALING) {
+ if (DEBUG_PANNING) {
Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
+ " scrollY: " + distanceY);
}
mMagnificationController.offsetMagnifiedRegion(distanceX, distanceY,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- return /* event consumed: */ true;
+ return true;
}
@Override
@@ -400,8 +494,12 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
return false;
}
final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
- mScaling = abs(deltaScale) > mScalingThreshold;
- return mScaling;
+ if (abs(deltaScale) > mScalingThreshold) {
+ mScaling = true;
+ return true;
+ } else {
+ return false;
+ }
}
final float initialScale = mMagnificationController.getScale();
@@ -425,15 +523,14 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
final float pivotX = detector.getFocusX();
final float pivotY = detector.getFocusY();
- if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
mMagnificationController.setScale(scale, pivotX, pivotY, false,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- return /* handled: */ true;
+ return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
- return /* continue recognizing: */ (mCurrentState == mPanningScalingState);
+ return (mCurrentState == STATE_PANNING_SCALING);
}
@Override
@@ -449,7 +546,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
@Override
public String toString() {
- return "PanningScalingState{" +
+ return "MagnifiedContentInteractionStateHandler{" +
"mInitialScaleFactor=" + mInitialScaleFactor +
", mScaling=" + mScaling +
'}';
@@ -461,11 +558,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
* determined that the user is performing a single-finger drag of the
* magnification viewport.
*
- * Unlike when {@link PanningScalingState panning}, the viewport moves in the opposite direction
- * of the finger, and any part of the screen is reachable without lifting the finger.
- * This makes it the preferable mode for tasks like reading text spanning full screen width.
+ * @see #STATE_VIEWPORT_DRAGGING
*/
- final class ViewportDraggingState implements State {
+ final class ViewportDraggingStateHandler implements MotionEventHandler {
/** Whether to disable zoom after dragging ends */
boolean mZoomedInBeforeDrag;
@@ -477,7 +572,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
switch (action) {
case ACTION_POINTER_DOWN: {
clear();
- transitionTo(mPanningScalingState);
+ transitionTo(STATE_PANNING_SCALING);
}
break;
case ACTION_MOVE: {
@@ -499,7 +594,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
case ACTION_UP: {
if (!mZoomedInBeforeDrag) zoomOff();
clear();
- transitionTo(mDetectingState);
+ transitionTo(STATE_DETECTING);
}
break;
@@ -518,51 +613,25 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
@Override
public String toString() {
- return "ViewportDraggingState{" +
+ return "ViewportDraggingStateHandler{" +
"mZoomedInBeforeDrag=" + mZoomedInBeforeDrag +
", mLastMoveOutsideMagnifiedRegion=" + mLastMoveOutsideMagnifiedRegion +
'}';
}
}
- final class DelegatingState implements State {
- /**
- * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link DelegatingState}
- */
- public long mLastDelegatedDownEventTime;
-
- @Override
- public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (event.getActionMasked() == ACTION_UP) {
- transitionTo(mDetectingState);
- }
-
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mLastDelegatedDownEventTime = event.getDownTime();
- }
- if (getNext() != null) {
- // We cache some events to see if the user wants to trigger magnification.
- // If no magnification is triggered we inject these events with adjusted
- // time and down time to prevent subsequent transformations being confused
- // by stale events. After the cached events, which always have a down, are
- // injected we need to also update the down time of all subsequent non cached
- // events. All delegated events cached and non-cached are delivered here.
- event.setDownTime(mLastDelegatedDownEventTime);
- dispatchTransformedEvent(event, rawEvent, policyFlags);
- }
- }
- }
-
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
+ *
+ * @see #STATE_DETECTING
*/
- final class DetectingState implements State, Handler.Callback {
+ final class DetectingStateHandler implements MotionEventHandler, Handler.Callback {
private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
- final int mLongTapMinDelay;
+ final int mLongTapMinDelay = ViewConfiguration.getJumpTapTimeout();
final int mSwipeMinDistance;
final int mMultiTapMaxDelay;
final int mMultiTapMaxDistance;
@@ -573,12 +642,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
private MotionEvent mLastUp;
private MotionEvent mPreLastUp;
- @VisibleForTesting boolean mShortcutTriggered;
-
Handler mHandler = new Handler(this);
- public DetectingState(Context context) {
- mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
+ public DetectingStateHandler(Context context) {
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
+ context.getResources().getInteger(
com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
@@ -595,7 +661,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
}
break;
case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ true);
}
break;
default: {
@@ -616,12 +682,12 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
} else if (isMultiTapTriggered(2 /* taps */)) {
// 3tap and hold
- afterLongTapTimeoutTransitionToDraggingState(event);
+ delayedTransitionToDraggingState(event);
} else if (mDetectTripleTap
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
@@ -629,21 +695,21 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
|| mMagnificationController.isMagnifying()) {
- afterMultiTapTimeoutTransitionToDelegatingState();
+ delayedTransitionToDelegatingState();
} else {
// Delegate pending events without delay
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
case ACTION_POINTER_DOWN: {
if (mMagnificationController.isMagnifying()) {
- transitionTo(mPanningScalingState);
+ transitionTo(STATE_PANNING_SCALING);
clear();
} else {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
@@ -656,7 +722,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
&& !isMultiTapTriggered(2 /* taps */)) {
// Swipe detected - delegate skipping timeout
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ true);
}
}
break;
@@ -667,7 +733,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
} else if (isMultiTapTriggered(3 /* taps */)) {
@@ -676,11 +742,12 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
} else if (
// Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
isFingerDown()
- //TODO long tap should never happen here
- && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
- || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
+ //TODO long tap should never happen here
+ && (timeBetween(mLastDown, /* mLastUp */ event) >= mLongTapMinDelay)
+ || distance(mLastDown, /* mLastUp */ event)
+ >= mSwipeMinDistance) {
- transitionToDelegatingStateAndClear();
+ transitionToDelegatingState(/* andClear */ true);
}
}
@@ -728,15 +795,15 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
}
- /** -> {@link DelegatingState} */
- public void afterMultiTapTimeoutTransitionToDelegatingState() {
+ /** -> {@link #STATE_DELEGATING} */
+ public void delayedTransitionToDelegatingState() {
mHandler.sendEmptyMessageDelayed(
MESSAGE_TRANSITION_TO_DELEGATING_STATE,
mMultiTapMaxDelay);
}
- /** -> {@link ViewportDraggingState} */
- public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) {
+ /** -> {@link #STATE_VIEWPORT_DRAGGING} */
+ public void delayedTransitionToDraggingState(MotionEvent event) {
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
ViewConfiguration.getLongPressTimeout());
@@ -779,7 +846,11 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
- handleEventWith(mDelegatingState, info.event, info.rawEvent, info.policyFlags);
+ // Because MagnifiedInteractionStateHandler requires well-formed event stream
+ mPanningScalingStateHandler.onMotionEvent(
+ info.event, info.rawEvent, info.policyFlags);
+
+ delegateEvent(info.event, info.rawEvent, info.policyFlags);
info.recycle();
}
@@ -797,10 +868,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mLastUp = null;
}
- void transitionToDelegatingStateAndClear() {
- transitionTo(mDelegatingState);
+ void transitionToDelegatingState(boolean andClear) {
+ transitionTo(STATE_DELEGATING);
sendDelayedMotionEvents();
- clear();
+ if (andClear) clear();
}
private void onTripleTap(MotionEvent up) {
@@ -824,40 +895,24 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
- mViewportDraggingState.mZoomedInBeforeDrag =
+ mViewportDraggingStateHandler.mZoomedInBeforeDrag =
mMagnificationController.isMagnifying();
zoomOn(down.getX(), down.getY());
- transitionTo(mViewportDraggingState);
+ transitionTo(STATE_VIEWPORT_DRAGGING);
}
@Override
public String toString() {
- return "DetectingState{" +
+ return "DetectingStateHandler{" +
"tapCount()=" + tapCount() +
- ", mShortcutTriggered=" + mShortcutTriggered +
", mDelayedEventQueue=" + MotionEventInfo.toString(mDelayedEventQueue) +
'}';
}
-
- void toggleShortcutTriggered() {
- setShortcutTriggered(!mShortcutTriggered);
- }
-
- void setShortcutTriggered(boolean state) {
- if (mShortcutTriggered == state) {
- return;
- }
-
- mShortcutTriggered = state;
- mMagnificationController.setForceShowMagnifiableBounds(state);
- }
}
private void zoomOn(float centerX, float centerY) {
- if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")");
-
final float scale = MathUtils.constrain(
mMagnificationController.getPersistedScale(),
MIN_SCALE, MAX_SCALE);
@@ -868,8 +923,6 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
}
private void zoomOff() {
- if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
-
mMagnificationController.reset(/* animate */ true);
}
@@ -882,15 +935,16 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
@Override
public String toString() {
- return "MagnificationGesture{" +
- "mDetectingState=" + mDetectingState +
- ", mDelegatingState=" + mDelegatingState +
- ", mMagnifiedInteractionState=" + mPanningScalingState +
- ", mViewportDraggingState=" + mViewportDraggingState +
+ return "MagnificationGestureHandler{" +
+ "mDetectingStateHandler=" + mDetectingStateHandler +
+ ", mMagnifiedInteractionStateHandler=" + mPanningScalingStateHandler +
+ ", mViewportDraggingStateHandler=" + mViewportDraggingStateHandler +
", mDetectTripleTap=" + mDetectTripleTap +
", mDetectShortcutTrigger=" + mDetectShortcutTrigger +
- ", mCurrentState=" + State.nameOf(mCurrentState) +
- ", mPreviousState=" + State.nameOf(mPreviousState) +
+ ", mCurrentState=" + stateToString(mCurrentState) +
+ ", mPreviousState=" + stateToString(mPreviousState) +
+ ", mShortcutTriggered=" + mShortcutTriggered +
+ ", mDelegatingStateDownTime=" + mDelegatingStateDownTime +
", mMagnificationController=" + mMagnificationController +
'}';
}
@@ -997,7 +1051,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
@Override
public void onReceive(Context context, Intent intent) {
- mGestureHandler.mDetectingState.setShortcutTriggered(false);
+ mGestureHandler.setShortcutTriggered(false);
}
}
}
diff --git a/com/android/server/accessibility/MotionEventInjector.java b/com/android/server/accessibility/MotionEventInjector.java
index b6b78129..48041adb 100644
--- a/com/android/server/accessibility/MotionEventInjector.java
+++ b/com/android/server/accessibility/MotionEventInjector.java
@@ -30,13 +30,13 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.InputDevice;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
-
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -45,7 +45,7 @@ import java.util.List;
* <p>
* All methods except {@code injectEvents} must be called only from the main thread.
*/
-public class MotionEventInjector extends BaseEventStreamTransformation implements Handler.Callback {
+public class MotionEventInjector implements EventStreamTransformation, Handler.Callback {
private static final String LOG_TAG = "MotionEventInjector";
private static final int MESSAGE_SEND_MOTION_EVENT = 1;
private static final int MESSAGE_INJECT_EVENTS = 2;
@@ -68,6 +68,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
private final Handler mHandler;
private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+ private EventStreamTransformation mNext;
private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
private IntArray mSequencesInProgress = new IntArray(5);
private boolean mIsDestroyed = false;
@@ -116,6 +117,25 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
}
@Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
public void clearEvents(int inputSource) {
/*
* Reset state for motion events passing through so we won't send a cancel event for
@@ -167,7 +187,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
return;
}
- if (getNext() == null) {
+ if (mNext == null) {
notifyService(serviceInterface, sequence, false);
return;
}
@@ -242,24 +262,17 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
int continuedPointerId = mStrokeIdToPointerId
.get(touchPoint.mContinuedStrokeId, -1);
if (continuedPointerId == -1) {
- Slog.w(LOG_TAG, "Can't continue gesture due to unknown continued stroke id in "
- + touchPoint);
return false;
}
mStrokeIdToPointerId.put(touchPoint.mStrokeId, continuedPointerId);
int lastPointIndex = findPointByStrokeId(
mLastTouchPoints, mNumLastTouchPoints, touchPoint.mContinuedStrokeId);
if (lastPointIndex < 0) {
- Slog.w(LOG_TAG, "Can't continue gesture due continued gesture id of "
- + touchPoint + " not matching any previous strokes in "
- + Arrays.asList(mLastTouchPoints));
return false;
}
if (mLastTouchPoints[lastPointIndex].mIsEndOfPath
|| (mLastTouchPoints[lastPointIndex].mX != touchPoint.mX)
|| (mLastTouchPoints[lastPointIndex].mY != touchPoint.mY)) {
- Slog.w(LOG_TAG, "Can't continue gesture due to points mismatch between "
- + mLastTouchPoints[lastPointIndex] + " and " + touchPoint);
return false;
}
// Update the last touch point to match the continuation, so the gestures will
@@ -279,8 +292,8 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
private void sendMotionEventToNext(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (getNext() != null) {
- super.onMotionEvent(event, rawEvent, policyFlags);
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOpenGesturesInProgress.put(event.getSource(), true);
}
@@ -292,7 +305,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
}
private void cancelAnyGestureInProgress(int source) {
- if ((getNext() != null) && mOpenGesturesInProgress.get(source, false)) {
+ if ((mNext != null) && mOpenGesturesInProgress.get(source, false)) {
long now = SystemClock.uptimeMillis();
MotionEvent cancelEvent =
obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1);
diff --git a/com/android/server/accessibility/TouchExplorer.java b/com/android/server/accessibility/TouchExplorer.java
index a32686df..e380f2c6 100644
--- a/com/android/server/accessibility/TouchExplorer.java
+++ b/com/android/server/accessibility/TouchExplorer.java
@@ -55,8 +55,7 @@ import java.util.List;
*
* @hide
*/
-class TouchExplorer extends BaseEventStreamTransformation
- implements AccessibilityGestureDetector.Listener {
+class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener {
private static final boolean DEBUG = false;
@@ -132,6 +131,9 @@ class TouchExplorer extends BaseEventStreamTransformation
// the two dragging pointers as opposed to use the location of the primary one.
private final int mScaledMinPointerDistanceToUseMiddleLocation;
+ // The handler to which to delegate events.
+ private EventStreamTransformation mNext;
+
// Helper class to track received pointers.
private final ReceivedPointerTracker mReceivedPointerTracker;
@@ -196,7 +198,9 @@ class TouchExplorer extends BaseEventStreamTransformation
if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
clear();
}
- super.clearEvents(inputSource);
+ if (mNext != null) {
+ mNext.clearEvents(inputSource);
+ }
}
@Override
@@ -254,9 +258,16 @@ class TouchExplorer extends BaseEventStreamTransformation
}
@Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- super.onMotionEvent(event, rawEvent, policyFlags);
+ if (mNext != null) {
+ mNext.onMotionEvent(event, rawEvent, policyFlags);
+ }
return;
}
@@ -300,6 +311,13 @@ class TouchExplorer extends BaseEventStreamTransformation
}
@Override
+ public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mNext != null) {
+ mNext.onKeyEvent(event, policyFlags);
+ }
+ }
+
+ @Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
@@ -335,7 +353,9 @@ class TouchExplorer extends BaseEventStreamTransformation
mLastTouchedWindowId = event.getWindowId();
} break;
}
- super.onAccessibilityEvent(event);
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
}
@Override
@@ -949,10 +969,12 @@ class TouchExplorer extends BaseEventStreamTransformation
// 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);
+ if (mNext != null) {
+ // 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.
+ mNext.onMotionEvent(event, null, policyFlags);
+ }
mInjectedPointerTracker.onMotionEvent(event);
diff --git a/com/android/server/am/ActivityDisplay.java b/com/android/server/am/ActivityDisplay.java
index 6ed05552..8bcbfbef 100644
--- a/com/android/server/am/ActivityDisplay.java
+++ b/com/android/server/am/ActivityDisplay.java
@@ -16,7 +16,9 @@
package com.android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -46,14 +48,13 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.ConfigurationContainer;
-import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s.
*/
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends ConfigurationContainer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
@@ -67,7 +68,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
- private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+ final ArrayList<ActivityStack> mStacks = new ArrayList<>();
/** Array of all UIDs that are present on the display. */
private IntArray mDisplayAccessUIDs = new IntArray();
@@ -79,13 +80,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
private boolean mSleeping;
- // Cached reference to some special stacks we tend to get a lot so we don't need to loop
- // through the list to find them.
- private ActivityStack mHomeStack = null;
- private ActivityStack mRecentsStack = null;
- private ActivityStack mPinnedStack = null;
- private ActivityStack mSplitScreenPrimaryStack = null;
-
ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
mSupervisor = supervisor;
mDisplayId = displayId;
@@ -104,7 +98,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
+ " to displayId=" + mDisplayId + " position=" + position);
- addStackReferenceIfNeeded(stack);
positionChildAt(stack, position);
mSupervisor.mService.updateSleepIfNeededLocked();
}
@@ -113,7 +106,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
- removeStackReferenceIfNeeded(stack);
mSupervisor.mService.updateSleepIfNeededLocked();
}
@@ -158,16 +150,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
* @see ConfigurationContainer#isCompatible(int, int)
*/
<T extends ActivityStack> T getStack(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_HOME) {
- return (T) mHomeStack;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
- return (T) mRecentsStack;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- return (T) mPinnedStack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return (T) mSplitScreenPrimaryStack;
- }
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
// TODO: Should undefined windowing and activity type be compatible with standard type?
@@ -231,14 +213,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
// TODO: Should be okay to have stacks with with undefined windowing mode long term, but
// have to set them to something for now due to logic that depending on them.
- windowingMode = getWindowingMode(); // Put in current display's windowing mode
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Else fullscreen for now...
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- }
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
}
- final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
+ final boolean inSplitScreenMode = hasSplitScreenStack();
if (!inSplitScreenMode
&& windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
// Switch to fullscreen windowing mode if we are not in split-screen mode and we are
@@ -250,7 +228,24 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
- final int stackId = mSupervisor.getNextStackId();
+ int stackId = INVALID_STACK_ID;
+ if (mDisplayId == DEFAULT_DISPLAY && (activityType == ACTIVITY_TYPE_STANDARD
+ || activityType == ACTIVITY_TYPE_UNDEFINED)) {
+ // TODO: Will be removed once we are no longer using static stack ids.
+ stackId = getStackIdForWindowingMode(windowingMode);
+ if (stackId == INVALID_STACK_ID) {
+ // Whatever...put in fullscreen stack for now.
+ stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ }
+ final T stack = getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+
+ if (stackId == INVALID_STACK_ID) {
+ stackId = mSupervisor.getNextStackId();
+ }
final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
@@ -296,7 +291,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
if (stack.getWindowingMode() != windowingMode) {
continue;
}
- mSupervisor.removeStack(stack);
+ mSupervisor.removeStackLocked(stack.mStackId);
}
}
}
@@ -311,63 +306,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
if (stack.getActivityType() == activityType) {
- mSupervisor.removeStack(stack);
+ mSupervisor.removeStackLocked(stack.mStackId);
}
}
}
}
- void onStackWindowingModeChanged(ActivityStack stack) {
- removeStackReferenceIfNeeded(stack);
- addStackReferenceIfNeeded(stack);
- }
-
- private void addStackReferenceIfNeeded(ActivityStack stack) {
- final int activityType = stack.getActivityType();
- final int windowingMode = stack.getWindowingMode();
-
- if (activityType == ACTIVITY_TYPE_HOME) {
- if (mHomeStack != null && mHomeStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
- + mHomeStack + " already exist on display=" + this + " stack=" + stack);
- }
- mHomeStack = stack;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
- if (mRecentsStack != null && mRecentsStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
- + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
- }
- mRecentsStack = stack;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- if (mPinnedStack != null && mPinnedStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
- + mPinnedStack + " already exist on display=" + this
- + " stack=" + stack);
- }
- mPinnedStack = stack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded:"
- + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
- + " already exist on display=" + this + " stack=" + stack);
- }
- mSplitScreenPrimaryStack = stack;
- }
- }
-
- private void removeStackReferenceIfNeeded(ActivityStack stack) {
- if (stack == mHomeStack) {
- mHomeStack = null;
- } else if (stack == mRecentsStack) {
- mRecentsStack = null;
- } else if (stack == mPinnedStack) {
- mPinnedStack = null;
- } else if (stack == mSplitScreenPrimaryStack) {
- mSplitScreenPrimaryStack = null;
- }
- }
-
/** Returns the top visible stack activity type that isn't in the exclude windowing mode. */
int getTopVisibleStackActivityType(int excludeWindowingMode) {
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -382,42 +326,20 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
return ACTIVITY_TYPE_UNDEFINED;
}
- /**
- * Get the topmost stack on the display. It may be different from focused stack, because
- * focus may be on another display.
- */
- ActivityStack getTopStack() {
- return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
+ ActivityStack getSplitScreenStack() {
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
}
- boolean isTopStack(ActivityStack stack) {
- return stack == getTopStack();
- }
-
- int getIndexOf(ActivityStack stack) {
- return mStacks.indexOf(stack);
- }
-
- void onLockTaskPackagesUpdated() {
- for (int i = mStacks.size() - 1; i >= 0; --i) {
- mStacks.get(i).onLockTaskPackagesUpdated();
- }
- }
-
- ActivityStack getSplitScreenPrimaryStack() {
- return mSplitScreenPrimaryStack;
- }
-
- boolean hasSplitScreenPrimaryStack() {
- return mSplitScreenPrimaryStack != null;
+ boolean hasSplitScreenStack() {
+ return getSplitScreenStack() != null;
}
PinnedActivityStack getPinnedStack() {
- return (PinnedActivityStack) mPinnedStack;
+ return getStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
}
boolean hasPinnedStack() {
- return mPinnedStack != null;
+ return getPinnedStack() != null;
}
@Override
@@ -431,7 +353,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
@Override
- protected ActivityStack getChildAt(int index) {
+ protected ConfigurationContainer getChildAt(int index) {
return mStacks.get(index);
}
@@ -479,10 +401,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
mSleeping = asleep;
}
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "displayId=" + mDisplayId + " mStacks=" + mStacks);
- }
-
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER);
diff --git a/com/android/server/am/ActivityManagerDebugConfig.java b/com/android/server/am/ActivityManagerDebugConfig.java
index ceb2ad62..3a9bf125 100644
--- a/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/com/android/server/am/ActivityManagerDebugConfig.java
@@ -59,6 +59,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
+ static final boolean DEBUG_LOCKSCREEN = DEBUG_ALL || false;
static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
@@ -73,10 +74,10 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
static final boolean DEBUG_PSS = DEBUG_ALL || false;
static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
- static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
@@ -84,6 +85,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
static final boolean DEBUG_TASKS = DEBUG_ALL || false;
+ static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
@@ -103,6 +105,7 @@ class ActivityManagerDebugConfig {
static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : "";
static final String POSTFIX_IDLE = (APPEND_CATEGORY_NAME) ? "_Idle" : "";
static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : "";
+ static final String POSTFIX_LOCKSCREEN = (APPEND_CATEGORY_NAME) ? "_LockScreen" : "";
static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : "";
static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
static final String POSTFIX_MU = "_MU";
@@ -119,6 +122,7 @@ class ActivityManagerDebugConfig {
static final String POSTFIX_RELEASE = (APPEND_CATEGORY_NAME) ? "_Release" : "";
static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : "";
static final String POSTFIX_SAVED_STATE = (APPEND_CATEGORY_NAME) ? "_SavedState" : "";
+ static final String POSTFIX_SCREENSHOTS = (APPEND_CATEGORY_NAME) ? "_Screenshots" : "";
static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
static final String POSTFIX_SERVICE_EXECUTING =
(APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
@@ -126,11 +130,13 @@ class ActivityManagerDebugConfig {
static final String POSTFIX_STATES = (APPEND_CATEGORY_NAME) ? "_States" : "";
static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : "";
static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
+ static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
? "_UidObservers" : "";
static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
+ static final String POSTFIX_VISIBLE_BEHIND = (APPEND_CATEGORY_NAME) ? "_VisibleBehind" : "";
}
diff --git a/com/android/server/am/ActivityManagerService.java b/com/android/server/am/ActivityManagerService.java
index f17c9ac3..e6fe6204 100644
--- a/com/android/server/am/ActivityManagerService.java
+++ b/com/android/server/am/ActivityManagerService.java
@@ -27,10 +27,18 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.ActivityManager.StackId.isStaticStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -49,9 +57,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.os.Build.VERSION_CODES.N;
-import static android.os.IServiceManager.DUMP_PRIORITY_CRITICAL;
-import static android.os.IServiceManager.DUMP_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_PRIORITY_NORMAL;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.FIRST_ISOLATED_UID;
@@ -132,6 +137,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
@@ -176,6 +182,7 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.am.proto.ActivityManagerServiceProto.ACTIVITIES;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
@@ -192,6 +199,7 @@ import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
@@ -208,6 +216,7 @@ import android.app.ContentProviderHolder;
import android.app.Dialog;
import android.app.IActivityController;
import android.app.IActivityManager;
+import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
@@ -395,9 +404,6 @@ import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.proto.ActivityManagerServiceProto;
-import com.android.server.am.proto.BroadcastProto;
-import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
@@ -733,6 +739,9 @@ public class ActivityManagerService extends IActivityManager.Stub
doDump(fd, pw, new String[] {"associations"});
}
doDump(fd, pw, new String[] {"processes"});
+ doDump(fd, pw, new String[] {"-v", "all"});
+ doDump(fd, pw, new String[] {"service", "all"});
+ doDump(fd, pw, new String[] {"provider", "all"});
}
@Override
@@ -744,8 +753,6 @@ public class ActivityManagerService extends IActivityManager.Stub
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
&& !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
- && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
- mUserController.getCurrentUserId())
&& !(UserManager.isDeviceInDemoMode(mContext)
&& mUserController.getCurrentUser().isDemo());
}
@@ -1710,6 +1717,7 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+ static final int TOP_APP_KILLED_BY_LMK_MSG = 73;
static final int NOTIFY_VR_KEYGUARD_MSG = 74;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1730,6 +1738,9 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
private boolean mUserIsMonkey;
+ /** Flag whether the device has a Recents UI */
+ boolean mHasRecents;
+
/** The dimensions of the thumbnails in the Recents UI. */
int mThumbnailWidth;
int mThumbnailHeight;
@@ -1935,6 +1946,17 @@ public class ActivityManagerService extends IActivityManager.Stub
dispatchProcessDied(pid, uid);
break;
}
+ case TOP_APP_KILLED_BY_LMK_MSG: {
+ final String appName = (String) msg.obj;
+ final AlertDialog d = new BaseErrorDialog(mUiContext);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.setTitle(mUiContext.getText(R.string.top_app_killed_title));
+ d.setMessage(mUiContext.getString(R.string.top_app_killed_message, appName));
+ d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.close),
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+ d.show();
+ break;
+ }
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
@@ -2089,8 +2111,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String text = mContext.getString(R.string.heavy_weight_notification,
context.getApplicationInfo().loadLabel(context.getPackageManager()));
Notification notification =
- new Notification.Builder(context,
- SystemNotificationChannels.HEAVY_WEIGHT_APP)
+ new Notification.Builder(context, SystemNotificationChannels.DEVELOPER)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
@@ -2124,7 +2145,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
try {
inm.cancelNotificationWithTag("android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
@@ -2482,16 +2503,13 @@ public class ActivityManagerService extends IActivityManager.Stub
public void setSystemProcess() {
try {
- ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
- DUMP_PRIORITY_CRITICAL | DUMP_PRIORITY_NORMAL);
+ ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
- ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
- DUMP_PRIORITY_HIGH | DUMP_PRIORITY_NORMAL);
+ ServiceManager.addService("meminfo", new MemBinder(this));
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
- ServiceManager.addService("cpuinfo", new CpuBinder(this),
- /* allowIsolated= */ false, DUMP_PRIORITY_CRITICAL);
+ ServiceManager.addService("cpuinfo", new CpuBinder(this));
}
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
@@ -2522,6 +2540,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
mWindowManager = wm;
mStackSupervisor.setWindowManager(wm);
+ mActivityStarter.setWindowManager(wm);
mLockTaskController.setWindowManager(wm);
}
}
@@ -2763,9 +2782,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mTaskChangeNotificationController =
new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
- mActivityStarter = new ActivityStarter(this);
+ mActivityStarter = new ActivityStarter(this, mStackSupervisor);
mRecentTasks = new RecentTasks(this, mStackSupervisor);
- mStackSupervisor.setRecentTasks(mRecentTasks);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
mProcessCpuThread = new Thread("CpuTracker") {
@@ -3223,12 +3241,11 @@ public class ActivityManagerService extends IActivityManager.Stub
// stack implementation changes in the future, keep in mind that the use of the fullscreen
// stack is a means to move the activity to the main display and a moveActivityToDisplay()
// option would be a better choice here.
- if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
+ if (r.requestedVrComponent != null && r.getStackId() >= FIRST_DYNAMIC_STACK_ID) {
Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ " to main stack for VR");
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
- moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+ setTaskWindowingMode(r.getTask().taskId,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
}
mHandler.sendMessage(
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
@@ -5078,12 +5095,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
synchronized(this) {
- final ProcessRecord proc = mHeavyWeightProcess;
- if (proc == null) {
+ if (mHeavyWeightProcess == null) {
return;
}
- ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
+ ArrayList<ActivityRecord> activities = new ArrayList<>(mHeavyWeightProcess.activities);
for (int i = 0; i < activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
@@ -5093,7 +5109,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- proc.userId, 0));
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
}
@@ -5412,6 +5428,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean doLowMem = app.instr == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
+ maybeNotifyTopAppKilled(app);
Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: "
+ ProcessList.makeOomAdjString(app.setAdj)
+ ProcessList.makeProcStateString(app.setProcState));
@@ -5445,6 +5462,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /** Show system error dialog when a top app is killed by LMK */
+ void maybeNotifyTopAppKilled(ProcessRecord app) {
+ if (!shouldNotifyTopAppKilled(app)) {
+ return;
+ }
+
+ Message msg = mHandler.obtainMessage(TOP_APP_KILLED_BY_LMK_MSG);
+ msg.obj = mContext.getPackageManager().getApplicationLabel(app.info);
+ mUiHandler.sendMessage(msg);
+ }
+
+ /** Only show notification when the top app is killed on low ram devices */
+ private boolean shouldNotifyTopAppKilled(ProcessRecord app) {
+ return app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ ActivityManager.isLowRamDeviceStatic();
+ }
+
/**
* If a stack trace dump file is configured, dump process stack traces.
* @param clearTraces causes the dump file to be erased prior to the new
@@ -5929,7 +5963,16 @@ public class ActivityManagerService extends IActivityManager.Stub
if (appInfo != null) {
forceStopPackageLocked(packageName, appInfo.uid, "clear data");
- mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId);
+ // Remove all tasks match the cleared application package and user
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ final TaskRecord tr = mRecentTasks.get(i);
+ final String taskPackageName =
+ tr.getBaseIntent().getComponent().getPackageName();
+ if (tr.userId != resolvedUserId) continue;
+ if (!taskPackageName.equals(packageName)) continue;
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ REMOVE_FROM_RECENTS);
+ }
}
}
@@ -6510,7 +6553,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// Clean-up disabled tasks
- mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+ cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
@@ -8041,8 +8084,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
- if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
- || r.getStack().isInStackLocked(r) == null) {
+ if (r == null || r.getStack() == null || !r.getStack().isPinnedStack() ||
+ r.getStack().isInStackLocked(r) == null) {
return false;
}
@@ -8136,7 +8179,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(params);
- if (r.inPinnedWindowingMode()) {
+ if (r.getStack().getStackId() == PINNED_STACK_ID) {
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
@@ -9757,12 +9800,35 @@ public class ActivityManagerService extends IActivityManager.Stub
public List<IBinder> getAppTasks(String callingPackage) {
int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
- try {
- synchronized(this) {
- return mRecentTasks.getAppTasksList(callingUid, callingPackage);
+
+ synchronized(this) {
+ ArrayList<IBinder> list = new ArrayList<IBinder>();
+ try {
+ if (DEBUG_ALL) Slog.v(TAG, "getAppTasks");
+
+ final int N = mRecentTasks.size();
+ for (int i = 0; i < N; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ // Skip tasks that do not match the caller. We don't need to verify
+ // callingPackage, because we are also limiting to callingUid and know
+ // that will limit to the correct security sandbox.
+ if (tr.effectiveUid != callingUid) {
+ continue;
+ }
+ Intent intent = tr.getBaseIntent();
+ if (intent == null ||
+ !callingPackage.equals(intent.getComponent().getPackageName())) {
+ continue;
+ }
+ ActivityManager.RecentTaskInfo taskInfo =
+ createRecentTaskInfoFromTaskRecord(tr);
+ AppTaskImpl taskImpl = new AppTaskImpl(taskInfo.persistentId, callingUid);
+ list.add(taskImpl.asBinder());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
+ return list;
}
}
@@ -9785,6 +9851,58 @@ public class ActivityManagerService extends IActivityManager.Stub
return list;
}
+ /**
+ * Creates a new RecentTaskInfo from a TaskRecord.
+ */
+ private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
+ // Update the task description to reflect any changes in the task stack
+ tr.updateTaskDescription();
+
+ // Compose the recent task info
+ ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
+ rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
+ rti.persistentId = tr.taskId;
+ rti.baseIntent = new Intent(tr.getBaseIntent());
+ rti.origActivity = tr.origActivity;
+ rti.realActivity = tr.realActivity;
+ rti.description = tr.lastDescription;
+ rti.stackId = tr.getStackId();
+ rti.userId = tr.userId;
+ rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
+ rti.firstActiveTime = tr.firstActiveTime;
+ rti.lastActiveTime = tr.lastActiveTime;
+ rti.affiliatedTaskId = tr.mAffiliatedTaskId;
+ rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
+ rti.numActivities = 0;
+ if (tr.mBounds != null) {
+ rti.bounds = new Rect(tr.mBounds);
+ }
+ rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
+ rti.resizeMode = tr.mResizeMode;
+ rti.configuration.setTo(tr.getConfiguration());
+
+ ActivityRecord base = null;
+ ActivityRecord top = null;
+ ActivityRecord tmp;
+
+ for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
+ tmp = tr.mActivities.get(i);
+ if (tmp.finishing) {
+ continue;
+ }
+ base = tmp;
+ if (top == null || (top.state == ActivityState.INITIALIZING)) {
+ top = base;
+ }
+ rti.numActivities++;
+ }
+
+ rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
+ rti.topActivity = (top != null) ? top.intent.getComponent() : null;
+
+ return rti;
+ }
+
private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
@@ -9818,15 +9936,118 @@ public class ActivityManagerService extends IActivityManager.Stub
final int callingUid = Binder.getCallingUid();
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
false, ALLOW_FULL_ONLY, "getRecentTasks", null);
- final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
- callingUid);
- final boolean detailed = checkCallingPermission(
- android.Manifest.permission.GET_DETAILED_TASKS)
- == PackageManager.PERMISSION_GRANTED;
+ final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0;
+ final boolean withExcluded = (flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0;
synchronized (this) {
- return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
+ final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
+ final boolean detailed = checkCallingPermission(
+ android.Manifest.permission.GET_DETAILED_TASKS)
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
+ Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
+ return ParceledListSlice.emptyList();
+ }
+ mRecentTasks.loadUserRecentsLocked(userId);
+
+ final int recentsCount = mRecentTasks.size();
+ ArrayList<ActivityManager.RecentTaskInfo> res =
+ new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
+
+ final Set<Integer> includedUsers;
+ if (includeProfiles) {
+ includedUsers = mUserController.getProfileIds(userId);
+ } else {
+ includedUsers = new HashSet<>();
+ }
+ includedUsers.add(Integer.valueOf(userId));
+
+ for (int i = 0; i < recentsCount && maxNum > 0; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ // Only add calling user or related users recent tasks
+ if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
+ continue;
+ }
+
+ if (tr.realActivitySuspended) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
+ continue;
+ }
+
+ // Return the entry if desired by the caller. We always return
+ // the first entry, because callers always expect this to be the
+ // foreground app. We may filter others if the caller has
+ // not supplied RECENT_WITH_EXCLUDED and there is some reason
+ // we should exclude the entry.
+
+ if (i == 0
+ || withExcluded
+ || (tr.intent == null)
+ || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ == 0)) {
+ if (!allowed) {
+ // If the caller doesn't have the GET_TASKS permission, then only
+ // allow them to see a small subset of tasks -- their own and home.
+ if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
+ continue;
+ }
+ }
+ final ActivityStack stack = tr.getStack();
+ if ((flags & ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS) != 0) {
+ if (stack != null && stack.isHomeOrRecentsStack()) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, home or recents stack task: " + tr);
+ continue;
+ }
+ }
+ if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
+ if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, top task in docked stack: " + tr);
+ continue;
+ }
+ }
+ if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
+ if (stack != null && stack.isPinnedStack()) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, pinned stack task: " + tr);
+ continue;
+ }
+ }
+ if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
+ // Don't include auto remove tasks that are finished or finishing.
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, auto-remove without activity: " + tr);
+ continue;
+ }
+ if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
+ && !tr.isAvailable) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, unavail real act: " + tr);
+ continue;
+ }
+
+ if (!tr.mUserSetupComplete) {
+ // Don't include task launched while user is not done setting-up.
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+ "Skipping, user setup not complete: " + tr);
+ continue;
+ }
+
+ ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
+ if (!detailed) {
+ rti.baseIntent.replaceExtras((Bundle)null);
+ }
+
+ res.add(rti);
+ maxNum--;
+ }
+ }
+ return new ParceledListSlice<>(res);
}
}
@@ -9898,10 +10119,23 @@ public class ActivityManagerService extends IActivityManager.Stub
TaskRecord task = new TaskRecord(this,
mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
ainfo, intent, description);
- if (!mRecentTasks.addToBottom(task)) {
+
+ int trimIdx = mRecentTasks.trimForTaskLocked(task, false);
+ if (trimIdx >= 0) {
+ // If this would have caused a trim, then we'll abort because that
+ // means it would be added at the end of the list but then just removed.
return INVALID_TASK_ID;
}
- r.getStack().addTask(task, !ON_TOP, "addAppTask");
+
+ final int N = mRecentTasks.size();
+ if (N >= (ActivityManager.getMaxRecentTasksStatic()-1)) {
+ final TaskRecord tr = mRecentTasks.remove(N - 1);
+ tr.removedFromRecents();
+ }
+
+ task.inRecents = true;
+ mRecentTasks.add(task);
+ r.getStack().addTask(task, false, "addAppTask");
// TODO: Send the thumbnail to WM to store it.
@@ -10117,6 +10351,38 @@ public class ActivityManagerService extends IActivityManager.Stub
mWindowManager.executeAppTransition();
}
+ private void removeTasksByPackageNameLocked(String packageName, int userId) {
+ // Remove all tasks with activities in the specified package from the list of recent tasks
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (tr.userId != userId) continue;
+
+ ComponentName cn = tr.intent.getComponent();
+ if (cn != null && cn.getPackageName().equals(packageName)) {
+ // If the package name matches, remove the task.
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+ }
+ }
+ }
+
+ private void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
+ int userId) {
+
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (userId != UserHandle.USER_ALL && tr.userId != userId) {
+ continue;
+ }
+
+ ComponentName cn = tr.intent.getComponent();
+ final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
+ && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
+ if (sameComponent) {
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+ }
+ }
+ }
+
@Override
public void removeStack(int stackId) {
enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -10124,14 +10390,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
final ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack == null) {
- return;
- }
- if (!stack.isActivityTypeStandardOrUndefined()) {
+ if (stack != null && !stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException(
"Removing non-standard stack is not allowed.");
}
- mStackSupervisor.removeStack(stack);
+ mStackSupervisor.removeStackLocked(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10346,7 +10609,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
final ActivityStack stack = r.getStack();
- if (stack == null || !stack.inFreeformWindowingMode()) {
+ if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
}
@@ -10414,20 +10677,27 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
+ if (stackId == DOCKED_STACK_ID) {
+ mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ null /* initialBounds */);
+ }
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
- throw new IllegalStateException(
- "moveTaskToStack: No stack for stackId=" + stackId);
+ if (!isStaticStack(stackId)) {
+ throw new IllegalStateException(
+ "moveTaskToStack: No stack for stackId=" + stackId);
+ }
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final int windowingMode =
+ getWindowingModeForStackId(stackId, display.hasSplitScreenStack());
+ stack = display.getOrCreateStack(windowingMode,
+ task.getStack().getActivityType(), toTop);
}
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ taskId + " to stack " + stackId);
}
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- mWindowManager.setDockedStackCreateState(
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- }
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -10497,9 +10767,9 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
final ActivityStack stack =
- mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
if (toTop) {
- mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
+ mStackSupervisor.resizeStackLocked(stack.mStackId, null /* destBounds */,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
true /* preserveWindows */, true /* allowResizeInDockedMode */,
!DEFER_RESUME);
@@ -10592,12 +10862,7 @@ public class ActivityManagerService extends IActivityManager.Stub
stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
animationDuration, false /* fromFullscreen */);
} else {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack == null) {
- Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
- return;
- }
- mStackSupervisor.resizeStackLocked(stack, destBounds, null /* tempTaskBounds */,
+ mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
allowResizeInDockedMode, !DEFER_RESUME);
}
@@ -12673,10 +12938,6 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
+ bugreportType);
}
- // Always log caller, even if it does not have permission to dump.
- String type = extraOptions == null ? "bugreport" : extraOptions;
- Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
-
enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
if (extraOptions != null) {
SystemProperties.set("dumpstate.options", extraOptions);
@@ -13884,6 +14145,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Load resources only after the current configuration has been set.
final Resources res = mContext.getResources();
+ mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
mThumbnailWidth = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
@@ -14842,31 +15104,10 @@ public class ActivityManagerService extends IActivityManager.Stub
long origId = Binder.clearCallingIdentity();
if (useProto) {
+ //TODO: Options when dumping proto
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- String cmd = opti < args.length ? args[opti] : "";
- opti++;
-
- if ("activities".equals(cmd) || "a".equals(cmd)) {
- // output proto is ActivityStackSupervisorProto
- synchronized (this) {
- writeActivitiesToProtoLocked(proto);
- }
- } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
- // output proto is BroadcastProto
- synchronized (this) {
- writeBroadcastsToProtoLocked(proto);
- }
- } else {
- // default option, dump everything, output is ActivityManagerServiceProto
- synchronized (this) {
- long activityToken = proto.start(ActivityManagerServiceProto.ACTIVITIES);
- writeActivitiesToProtoLocked(proto);
- proto.end(activityToken);
-
- long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
- writeBroadcastsToProtoLocked(proto);
- proto.end(broadcastToken);
- }
+ synchronized (this) {
+ writeActivitiesToProtoLocked(proto);
}
proto.flush();
Binder.restoreCallingIdentity(origId);
@@ -14892,9 +15133,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
} else if ("recents".equals(cmd) || "r".equals(cmd)) {
synchronized (this) {
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
- }
+ dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
String[] newArgs;
@@ -15115,9 +15354,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, dumpAll, dumpPackage);
- }
+ dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -15187,9 +15424,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, dumpAll, dumpPackage);
- }
+ dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -15223,8 +15458,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
- // The output proto of "activity --proto activities" is ActivityStackSupervisorProto
- mStackSupervisor.writeToProto(proto);
+ mStackSupervisor.writeToProto(proto, ACTIVITIES);
}
private void dumpLastANRLocked(PrintWriter pw) {
@@ -15276,6 +15510,42 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ void dumpRecentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
+
+ boolean printedAnything = false;
+
+ if (mRecentTasks != null && mRecentTasks.size() > 0) {
+ boolean printedHeader = false;
+
+ final int N = mRecentTasks.size();
+ for (int i=0; i<N; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (dumpPackage != null) {
+ if (tr.realActivity == null ||
+ !dumpPackage.equals(tr.realActivity.getPackageName())) {
+ continue;
+ }
+ }
+ if (!printedHeader) {
+ pw.println(" Recent tasks:");
+ printedHeader = true;
+ printedAnything = true;
+ }
+ pw.print(" * Recent #"); pw.print(i); pw.print(": ");
+ pw.println(tr);
+ if (dumpAll) {
+ mRecentTasks.get(i).dump(pw, " ");
+ }
+ }
+ }
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
+ }
+
void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
@@ -16102,40 +16372,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
- if (mRegisteredReceivers.size() > 0) {
- Iterator it = mRegisteredReceivers.values().iterator();
- while (it.hasNext()) {
- ReceiverList r = (ReceiverList)it.next();
- r.writeToProto(proto, BroadcastProto.RECEIVER_LIST);
- }
- }
- mReceiverResolver.writeToProto(proto, BroadcastProto.RECEIVER_RESOLVER);
- for (BroadcastQueue q : mBroadcastQueues) {
- q.writeToProto(proto, BroadcastProto.BROADCAST_QUEUE);
- }
- for (int user=0; user<mStickyBroadcasts.size(); user++) {
- long token = proto.start(BroadcastProto.STICKY_BROADCASTS);
- proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
- for (Map.Entry<String, ArrayList<Intent>> ent
- : mStickyBroadcasts.valueAt(user).entrySet()) {
- long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
- proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
- for (Intent intent : ent.getValue()) {
- intent.writeToProto(proto, StickyBroadcastProto.StickyAction.INTENTS,
- false, true, true, false);
- }
- proto.end(actionToken);
- }
- proto.end(token);
- }
-
- long handlerToken = proto.start(BroadcastProto.HANDLER);
- proto.write(BroadcastProto.MainHandler.HANDLER, mHandler.toString());
- mHandler.getLooper().writeToProto(proto, BroadcastProto.MainHandler.LOOPER);
- proto.end(handlerToken);
- }
-
void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -19124,7 +19360,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Remove all permissions granted from/to this package
removeUriPermissionsForPackageLocked(ssp, userId, true);
- mRecentTasks.removeTasksByPackageName(ssp, userId);
+ removeTasksByPackageNameLocked(ssp, userId);
mServices.forceStopPackageLocked(ssp, userId);
@@ -20461,10 +20697,9 @@ public class ActivityManagerService extends IActivityManager.Stub
/** Helper method that requests bounds from WM and applies them to stack. */
private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
final Rect newStackBounds = new Rect();
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
- stack.getBoundsForNewConfiguration(newStackBounds);
+ mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds);
mStackSupervisor.resizeStackLocked(
- stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+ stackId, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
}
@@ -24132,6 +24367,125 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * An implementation of IAppTask, that allows an app to manage its own tasks via
+ * {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that
+ * only the process that calls getAppTasks() can call the AppTask methods.
+ */
+ class AppTaskImpl extends IAppTask.Stub {
+ private int mTaskId;
+ private int mCallingUid;
+
+ public AppTaskImpl(int taskId, int callingUid) {
+ mTaskId = taskId;
+ mCallingUid = callingUid;
+ }
+
+ private void checkCaller() {
+ if (mCallingUid != Binder.getCallingUid()) {
+ throw new SecurityException("Caller " + mCallingUid
+ + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
+ }
+ }
+
+ @Override
+ public void finishAndRemoveTask() {
+ checkCaller();
+
+ synchronized (ActivityManagerService.this) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ // We remove the task from recents to preserve backwards
+ if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+ REMOVE_FROM_RECENTS)) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public ActivityManager.RecentTaskInfo getTaskInfo() {
+ checkCaller();
+
+ synchronized (ActivityManagerService.this) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ return createRecentTaskInfoFromTaskRecord(tr);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public void moveToFront() {
+ checkCaller();
+ // Will bring task to front if it already has a root activity.
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public int startActivity(IBinder whoThread, String callingPackage,
+ Intent intent, String resolvedType, Bundle bOptions) {
+ checkCaller();
+
+ int callingUser = UserHandle.getCallingUserId();
+ TaskRecord tr;
+ IApplicationThread appThread;
+ synchronized (ActivityManagerService.this) {
+ tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ appThread = IApplicationThread.Stub.asInterface(whoThread);
+ if (appThread == null) {
+ throw new IllegalArgumentException("Bad app thread " + appThread);
+ }
+ }
+ return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent,
+ resolvedType, null, null, null, null, 0, 0, null, null,
+ null, bOptions, false, callingUser, tr, "AppTaskImpl");
+ }
+
+ @Override
+ public void setExcludeFromRecents(boolean exclude) {
+ checkCaller();
+
+ synchronized (ActivityManagerService.this) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ Intent intent = tr.getBaseIntent();
+ if (exclude) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ } else {
+ intent.setFlags(intent.getFlags()
+ & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ }
+
+ /**
* Kill processes for the user with id userId and that depend on the package named packageName
*/
@Override
diff --git a/com/android/server/am/ActivityManagerShellCommand.java b/com/android/server/am/ActivityManagerShellCommand.java
index f03d2d53..4c934232 100644
--- a/com/android/server/am/ActivityManagerShellCommand.java
+++ b/com/android/server/am/ActivityManagerShellCommand.java
@@ -73,8 +73,10 @@ import java.util.List;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -84,6 +86,15 @@ final class ActivityManagerShellCommand extends ShellCommand {
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
+ // Is the object moving in a positive direction?
+ private static final boolean MOVING_FORWARD = true;
+ // Is the object moving in the horizontal plan?
+ private static final boolean MOVING_HORIZONTALLY = true;
+ // Is the object current point great then its target point?
+ private static final boolean GREATER_THAN_TARGET = true;
+ // Amount we reduce the stack size by when testing a task re-size.
+ private static final int STACK_BOUNDS_INSET = 10;
+
// IPC interface to activity manager -- don't need to do additional security checks.
final IActivityManager mInterface;
@@ -1933,6 +1944,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runStackInfo(pw);
case "move-top-activity-to-pinned-stack":
return runMoveTopActivityToPinnedStack(pw);
+ case "size-docked-stack-test":
+ return runStackSizeDockedStackTest(pw);
case "remove":
return runStackRemove(pw);
default:
@@ -2130,6 +2143,89 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runStackSizeDockedStackTest(PrintWriter pw) throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ final int stepSize = Integer.parseInt(getNextArgRequired());
+ final String side = getNextArgRequired();
+ final String delayStr = getNextArg();
+ final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
+
+ ActivityManager.StackInfo info = mInterface.getStackInfo(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ if (info == null) {
+ err.println("Docked stack doesn't exist");
+ return -1;
+ }
+ if (info.bounds == null) {
+ err.println("Docked stack doesn't have a bounds");
+ return -1;
+ }
+ Rect bounds = info.bounds;
+
+ final boolean horizontalGrowth = "l".equals(side) || "r".equals(side);
+ final int changeSize = (horizontalGrowth ? bounds.width() : bounds.height()) / 2;
+ int currentPoint;
+ switch (side) {
+ case "l":
+ currentPoint = bounds.left;
+ break;
+ case "r":
+ currentPoint = bounds.right;
+ break;
+ case "t":
+ currentPoint = bounds.top;
+ break;
+ case "b":
+ currentPoint = bounds.bottom;
+ break;
+ default:
+ err.println("Unknown growth side: " + side);
+ return -1;
+ }
+
+ final int startPoint = currentPoint;
+ final int minPoint = currentPoint - changeSize;
+ final int maxPoint = currentPoint + changeSize;
+
+ int maxChange;
+ pw.println("Shrinking docked stack side=" + side);
+ pw.flush();
+ while (currentPoint > minPoint) {
+ maxChange = Math.min(stepSize, currentPoint - minPoint);
+ currentPoint -= maxChange;
+ setBoundsSide(bounds, side, currentPoint);
+ int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+ if (res < 0) {
+ return res;
+ }
+ }
+
+ pw.println("Growing docked stack side=" + side);
+ pw.flush();
+ while (currentPoint < maxPoint) {
+ maxChange = Math.min(stepSize, maxPoint - currentPoint);
+ currentPoint += maxChange;
+ setBoundsSide(bounds, side, currentPoint);
+ int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+ if (res < 0) {
+ return res;
+ }
+ }
+
+ pw.println("Back to Original size side=" + side);
+ pw.flush();
+ while (currentPoint > startPoint) {
+ maxChange = Math.min(stepSize, currentPoint - startPoint);
+ currentPoint -= maxChange;
+ setBoundsSide(bounds, side, currentPoint);
+ int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+ if (res < 0) {
+ return res;
+ }
+ }
+ return 0;
+ }
+
void setBoundsSide(Rect bounds, String side, int value) {
switch (side) {
case "l":
@@ -2591,6 +2687,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
pw.println(" and supplying temporary different task bounds indicated by");
pw.println(" <TASK_LEFT,TOP,RIGHT,BOTTOM>");
+ pw.println(" size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]");
+ pw.println(" Test command for sizing docked stack by");
+ pw.println(" <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom");
+ pw.println(" applying the optional [DELAY_MS] between each step.");
pw.println(" move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
pw.println(" Moves the top activity from");
pw.println(" <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the");
diff --git a/com/android/server/am/ActivityMetricsLogger.java b/com/android/server/am/ActivityMetricsLogger.java
index 93c0f772..fdcb8c69 100644
--- a/com/android/server/am/ActivityMetricsLogger.java
+++ b/com/android/server/am/ActivityMetricsLogger.java
@@ -5,7 +5,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -128,7 +127,7 @@ class ActivityMetricsLogger {
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
break;
- case WINDOWING_MODE_FREEFORM:
+ case WINDOW_STATE_FREEFORM:
mWindowState = WINDOW_STATE_FREEFORM;
break;
default:
diff --git a/com/android/server/am/ActivityRecord.java b/com/android/server/am/ActivityRecord.java
index 2c72a4db..7b0b942a 100644
--- a/com/android/server/am/ActivityRecord.java
+++ b/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,9 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
@@ -58,10 +60,6 @@ import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
@@ -286,7 +284,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
int configChangeFlags; // which config values have changed
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
- int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
@@ -423,13 +420,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
if (iconFilename != null || taskDescription.getLabel() != null ||
taskDescription.getPrimaryColor() != 0) {
pw.print(prefix); pw.print("taskDescription:");
+ pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename());
pw.print(" label=\""); pw.print(taskDescription.getLabel());
pw.print("\"");
- pw.print(" icon="); pw.print(taskDescription.getInMemoryIcon() != null
- ? taskDescription.getInMemoryIcon().getByteCount() + " bytes"
- : "null");
- pw.print(" iconResource="); pw.print(taskDescription.getIconResource());
- pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename());
pw.print(" primaryColor=");
pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
pw.print(prefix + " backgroundColor=");
@@ -439,6 +432,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
pw.print(prefix + " navigationBarColor=");
pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
}
+ if (iconFilename == null && taskDescription.getIcon() != null) {
+ pw.print(prefix); pw.println("taskDescription contains Bitmap");
+ }
}
if (results != null) {
pw.print(prefix); pw.print("results="); pw.println(results);
@@ -665,7 +661,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return;
}
- final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
+ final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
+ (targetStackBounds != null);
if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
@@ -687,6 +684,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
}
+ boolean isFreeform() {
+ return task != null && task.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
@Override
protected int getChildCount() {
// {@link ActivityRecord} is a leaf node and has no children.
@@ -830,6 +831,23 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
hasBeenLaunched = false;
mStackSupervisor = supervisor;
+ mRotationAnimationHint = aInfo.rotationAnimation;
+
+ if (options != null) {
+ pendingOptions = options;
+ mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
+
+ final int rotationAnimation = pendingOptions.getRotationAnimationHint();
+ // Only override manifest supplied option if set.
+ if (rotationAnimation >= 0) {
+ mRotationAnimationHint = rotationAnimation;
+ }
+ PendingIntent usageReport = pendingOptions.getUsageTimeReport();
+ if (usageReport != null) {
+ appTimeTracker = new AppTimeTracker(usageReport);
+ }
+ }
+
// This starts out true, since the initial state of an activity is that we have everything,
// and we shouldn't never consider it lacking in state to be removed if it dies.
haveState = true;
@@ -896,32 +914,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
-
- mRotationAnimationHint = aInfo.rotationAnimation;
- lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
- if (appInfo.isPrivilegedApp() && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
- || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
- }
-
- if (options != null) {
- pendingOptions = options;
- mLaunchTaskBehind = options.getLaunchTaskBehind();
-
- final int rotationAnimation = pendingOptions.getRotationAnimationHint();
- // Only override manifest supplied option if set.
- if (rotationAnimation >= 0) {
- mRotationAnimationHint = rotationAnimation;
- }
- final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
- if (usageReport != null) {
- appTimeTracker = new AppTimeTracker(usageReport);
- }
- final boolean useLockTask = pendingOptions.getLockTaskMode();
- if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
- lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
- }
- }
}
AppWindowContainerController getWindowContainerController() {
@@ -956,7 +948,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// update the initial multi-window modes so that the callbacks are scheduled correctly when
// the user leaves that mode.
mLastReportedMultiWindowMode = !task.mFullscreen;
- mLastReportedPictureInPictureMode = inPinnedWindowingMode();
+ mLastReportedPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID);
}
void removeWindowContainer() {
@@ -1559,7 +1551,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// On devices that support leanback only (Android TV), Recents activity can only be
// visible if the home stack is the focused stack or we are in split-screen mode.
final ActivityDisplay display = getDisplay();
- boolean hasSplitScreenStack = display != null && display.hasSplitScreenPrimaryStack();
+ boolean hasSplitScreenStack = display != null && display.hasSplitScreenStack();
isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack());
}
@@ -2747,8 +2739,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
void setShowWhenLocked(boolean showWhenLocked) {
mShowWhenLocked = showWhenLocked;
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
- false /* preserveWindows */);
}
/**
diff --git a/com/android/server/am/ActivityStack.java b/com/android/server/am/ActivityStack.java
index f0811dda..1940ca2b 100644
--- a/com/android/server/am/ActivityStack.java
+++ b/com/android/server/am/ActivityStack.java
@@ -16,25 +16,27 @@
package com.android.server.am;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityDisplay.POSITION_TOP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -98,6 +100,7 @@ import static java.lang.Integer.MAX_VALUE;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -107,7 +110,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -190,6 +192,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// finished destroying itself.
private static final int DESTROY_TIMEOUT = 10 * 1000;
+ // How long until we reset a task when the user returns to it. Currently
+ // disabled.
+ private static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
+
// Set to false to disable the preview that is shown while a new activity
// is being started.
private static final boolean SHOW_APP_STARTING_PREVIEW = true;
@@ -346,11 +352,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
- private final Point mTmpSize = new Point();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
+ private final LaunchingTaskPositioner mTaskPositioner;
+
private boolean mTopActivityOccludesKeyguard;
private ActivityRecord mTopDismissingKeyguardActivity;
@@ -453,6 +460,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mWindowManager = mService.mWindowManager;
mStackId = stackId;
mCurrentUser = mService.mUserController.getCurrentUserId();
+ mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
+ ? new LaunchingTaskPositioner() : null;
mTmpRect2.setEmpty();
setWindowingMode(windowingMode);
setActivityType(activityType);
@@ -470,16 +479,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return mWindowContainerController;
}
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- final int prevWindowingMode = getWindowingMode();
- super.onConfigurationChanged(newParentConfig);
- final ActivityDisplay display = getDisplay();
- if (display != null && prevWindowingMode != getWindowingMode()) {
- display.onStackWindowingModeChanged(this);
- }
- }
-
/** Adds the stack to specified display and calls WindowManager to do the same. */
void reparent(ActivityDisplay activityDisplay, boolean onTop) {
removeFromDisplay();
@@ -503,11 +502,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mDisplayId = activityDisplay.mDisplayId;
mBounds = bounds != null ? new Rect(bounds) : null;
mFullscreen = mBounds == null;
-
+ if (mTaskPositioner != null) {
+ mTaskPositioner.setDisplay(activityDisplay.mDisplay);
+ mTaskPositioner.configure(mBounds);
+ }
onParentChanged();
activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
- if (inSplitScreenPrimaryWindowingMode()) {
+ if (mStackId == DOCKED_STACK_ID) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
mStackSupervisor.resizeDockedStackLocked(
@@ -531,6 +533,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
display.removeChild(this);
}
mDisplayId = INVALID_DISPLAY;
+ if (mTaskPositioner != null) {
+ mTaskPositioner.reset();
+ }
}
/** Removes the stack completely. Also calls WindowManager to do the same on its side. */
@@ -634,6 +639,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
+ if (mTaskPositioner != null) {
+ mTaskPositioner.configure(bounds);
+ }
}
ActivityRecord topRunningActivityLocked() {
@@ -810,6 +818,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return isActivityTypeHome() || isActivityTypeRecents();
}
+ final boolean isDockedStack() {
+ return mStackId == DOCKED_STACK_ID;
+ }
+
+ final boolean isPinnedStack() {
+ return mStackId == PINNED_STACK_ID;
+ }
+
final boolean isOnHomeDisplay() {
return mDisplayId == DEFAULT_DISPLAY;
}
@@ -1489,9 +1505,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* needed. A stack is considered translucent if it don't contain a visible or
* starting (about to be visible) activity that is fullscreen (opaque).
* @param starting The currently starting activity or null if there is none.
- * @param stackBehind The stack directly behind this one.
+ * @param stackBehindId The id of the stack directly behind this one.
*/
- private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+ private boolean isStackTranslucent(ActivityRecord starting, int stackBehindId) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1516,6 +1532,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
+ final ActivityStack stackBehind = mStackSupervisor.getStack(stackBehindId);
final boolean stackBehindHomeOrRecent = stackBehind != null
&& stackBehind.isHomeOrRecentsStack();
if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
@@ -1536,10 +1553,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
&& !mForceHidden;
}
- boolean isTopStackOnDisplay() {
- return getDisplay().isTopStack(this);
- }
-
/**
* Returns true if the stack should be visible.
*
@@ -1550,15 +1563,23 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
- final ActivityDisplay display = getDisplay();
- if (isTopStackOnDisplay() || mStackSupervisor.isFocusedStack(this)) {
+ if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) {
return true;
}
- final int stackIndex = display.getIndexOf(this);
+ final ActivityDisplay display = getDisplay();
+ final ArrayList<ActivityStack> displayStacks = display.mStacks;
+ final int stackIndex = displayStacks.indexOf(this);
+
+ if (stackIndex == displayStacks.size() - 1) {
+ Slog.wtf(TAG,
+ "Stack=" + this + " isn't front stack but is at the top of the stack list");
+ return false;
+ }
// Check position and visibility of this stack relative to the front stack on its display.
- final ActivityStack topStack = getDisplay().getTopStack();
+ final ActivityStack topStack = getTopStackOnDisplay();
+ final int topStackId = topStack.mStackId;
final int windowingMode = getWindowingMode();
final int activityType = getActivityType();
@@ -1566,7 +1587,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isActivityTypeAssistant()) {
- return topStack.isStackTranslucent(starting, this);
+ return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
}
return true;
}
@@ -1575,31 +1596,34 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// A case would be if recents stack exists but has no tasks and is below the docked stack
// and home stack is below recents
if (activityType == ACTIVITY_TYPE_HOME) {
- final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
- int dockedStackIndex = display.getIndexOf(splitScreenStack);
+ final ActivityStack splitScreenStack = display.getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ int dockedStackIndex = displayStacks.indexOf(splitScreenStack);
if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
return false;
}
}
// Find the first stack behind front stack that actually got something visible.
- int stackBehindTopIndex = display.getIndexOf(topStack) - 1;
+ int stackBehindTopIndex = displayStacks.indexOf(topStack) - 1;
while (stackBehindTopIndex >= 0 &&
- display.getChildAt(stackBehindTopIndex).topRunningActivityLocked() == null) {
+ displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
- ? display.getChildAt(stackBehindTopIndex) : null;
+ ? displayStacks.get(stackBehindTopIndex) : null;
+ int stackBehindTopId = INVALID_STACK_ID;
int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
if (stackBehindTop != null) {
+ stackBehindTopId = stackBehindTop.mStackId;
stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
stackBehindTopActivityType = stackBehindTop.getActivityType();
}
final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
- if (this == stackBehindTop) {
+ if (stackIndex == stackBehindTopIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
return true;
} else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
@@ -1608,13 +1632,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
return true;
} else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
- return stackBehindTop.isStackTranslucent(starting, this);
+ return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
+ starting, mStackId);
}
}
}
if (topStack.isBackdropToTranslucentActivity()
- && topStack.isStackTranslucent(starting, stackBehindTop)) {
+ && topStack.isStackTranslucent(starting, stackBehindTopId)) {
// Stacks behind the fullscreen or assistant stack with a translucent activity are
// always visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
@@ -1632,15 +1657,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- if (isOnHomeDisplay()) {
- // Visibility of any stack on default display should have been determined by the
- // conditions above.
+ if (StackId.isStaticStack(mStackId)
+ || isHomeOrRecentsStack() || isActivityTypeAssistant()) {
+ // Visibility of any static stack should have been determined by the conditions above.
return false;
}
- final int stackCount = display.getChildCount();
- for (int i = stackIndex + 1; i < stackCount; i++) {
- final ActivityStack stack = display.getChildAt(i);
+ for (int i = stackIndex + 1; i < displayStacks.size(); i++) {
+ final ActivityStack stack = displayStacks.get(i);
if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
continue;
@@ -1651,7 +1675,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
- if (!stack.isStackTranslucent(starting, null /* stackBehind */)) {
+ if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
return false;
}
}
@@ -1777,8 +1801,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
makeInvisible(r);
}
}
- final int windowingMode = getWindowingMode();
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
@@ -1793,8 +1816,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// show activities in the next application stack behind them vs. another
// task in the home stack like recents.
behindFullscreenActivity = true;
- } else if (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ } else if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
+ " returning to non-application type=" + task.getTaskToReturnTo());
// Once we reach a fullscreen stack task that has a running activity and should
@@ -1830,32 +1852,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
/**
- * Returns true if this stack should be resized to match the bounds specified by
- * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
- */
- boolean resizeStackWithLaunchBounds() {
- return inPinnedWindowingMode();
- }
-
- /**
- * Returns true if we try to maintain focus in the current stack when the top activity finishes.
- */
- private boolean keepFocusInStackIfPossible() {
- final int windowingMode = getWindowingMode();
- return windowingMode == WINDOWING_MODE_FREEFORM
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_PINNED;
- }
-
- /**
- * Returns true if the top task in the task is allowed to return home when finished and
- * there are other tasks in the stack.
- */
- boolean allowTopTaskToReturnHome() {
- return !inPinnedWindowingMode();
- }
-
- /**
* @return the top most visible activity that wants to dismiss Keyguard
*/
ActivityRecord getTopDismissingKeyguardActivity() {
@@ -1871,7 +1867,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
*/
boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
- final boolean isInPinnedStack = r.inPinnedWindowingMode();
+ final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
@@ -2169,7 +2165,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mResumedActivity = r;
r.state = ActivityState.RESUMED;
mService.setResumedActivityUncheckLocked(r, reason);
- mStackSupervisor.mRecentTasks.add(r.getTask());
+ mStackSupervisor.addRecentActivity(r);
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
@@ -2576,8 +2572,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null
- && lastStack.isTopStackOnDisplay()) {
+ } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
+ mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
next.showStartingWindow(null /* prev */, false /* newTask */,
false /* taskSwitch */);
}
@@ -2621,7 +2617,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
ActivityOptions options, String reason) {
- if (adjustFocusToNextFocusableStackLocked(reason)) {
+ if ((!mFullscreen || !isOnHomeDisplay()) && adjustFocusToNextFocusableStackLocked(reason)) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
@@ -2750,8 +2746,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// make underlying task focused when this one will be finished.
int returnToType = isLastTaskOverHome
? task.getTaskToReturnTo() : ACTIVITY_TYPE_STANDARD;
- if (fromHomeOrRecents && allowTopTaskToReturnHome()) {
- returnToType = topTask == null ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
+ if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
+ returnToType = topTask == null
+ ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
}
task.setTaskToReturnTo(returnToType);
}
@@ -2904,7 +2901,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Ensure the caller has requested not to trigger auto-enter PiP
return false;
}
- if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
+ if (pipCandidate == null || pipCandidate.getStackId() == PINNED_STACK_ID) {
// Ensure that we do not trigger entering PiP an activity on the pinned stack
return false;
}
@@ -3189,8 +3186,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
ActivityRecord newActivity) {
- final boolean forceReset =
+ boolean forceReset =
(newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+ if (ACTIVITY_INACTIVE_RESET_TIME > 0
+ && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
+ forceReset = true;
+ }
+ }
+
final TaskRecord task = taskTop.getTask();
/** False until we evaluate the TaskRecord associated with taskTop. Switches to true
@@ -3287,7 +3291,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final String myReason = reason + " adjustFocus";
if (next != r) {
- if (next != null && keepFocusInStackIfPossible() && isFocusable()) {
+ if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) {
// For freeform, docked, and pinned stacks we always keep the focus within the
// stack as long as there is a running activity.
return;
@@ -3753,7 +3757,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (mode == FINISH_IMMEDIATELY
|| (prevState == ActivityState.PAUSED
- && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
+ && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
|| finishingActivityInNonFocusedStack
|| prevState == STOPPING
|| prevState == STOPPED
@@ -4432,7 +4436,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
AppTimeTracker timeTracker, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
- final ActivityStack topStack = getDisplay().getTopStack();
+ final ActivityStack topStack = getTopStackOnDisplay();
final ActivityRecord topActivity = topStack != null ? topStack.topActivity() : null;
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
@@ -4460,9 +4464,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Don't refocus if invisible to current user
final ActivityRecord top = tr.getTopActivity();
if (top == null || !top.okToShowLocked()) {
- if (top != null) {
- mStackSupervisor.mRecentTasks.add(top.getTask());
- }
+ mStackSupervisor.addRecentActivity(top);
ActivityOptions.abort(options);
return;
}
@@ -4522,7 +4524,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
- if (isTopStackOnDisplay() && mService.mController != null) {
+ if (mStackSupervisor.isFrontStackOnDisplay(this) && mService.mController != null) {
ActivityRecord next = topRunningActivityLocked(null, taskId);
if (next == null) {
next = topRunningActivityLocked(null, 0);
@@ -4569,8 +4571,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
}
- if (inPinnedWindowingMode()) {
- mStackSupervisor.removeStack(this);
+ if (mStackId == PINNED_STACK_ID) {
+ mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
return true;
}
@@ -4602,6 +4604,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return true;
}
+ /**
+ * Get the topmost stack on the current display. It may be different from focused stack, because
+ * focus may be on another display.
+ */
+ private ActivityStack getTopStackOnDisplay() {
+ final ArrayList<ActivityStack> stacks = getDisplay().mStacks;
+ return stacks.isEmpty() ? null : stacks.get(stacks.size() - 1);
+ }
+
static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
final Uri data = r.intent.getData();
final String strData = data != null ? data.toSafeString() : null;
@@ -4676,7 +4687,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- if (inFreeformWindowingMode()) {
+ if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
@@ -4878,7 +4889,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (focusedStack && topTask) {
// Give the latest time to ensure foreground task can be sorted
// at the first, because lastActiveTime of creating task is 0.
- ci.lastActiveTime = SystemClock.elapsedRealtime();
+ ci.lastActiveTime = System.currentTimeMillis();
topTask = false;
}
@@ -5058,7 +5069,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (task.autoRemoveFromRecents() || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
- mStackSupervisor.mRecentTasks.remove(task);
+ mStackSupervisor.removeTaskFromRecents(task);
}
task.removeWindowContainer();
@@ -5086,7 +5097,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
task.setStack(null);
// Notify if a task from the pinned stack is being removed (or moved depending on the mode)
- if (inPinnedWindowingMode()) {
+ if (mStackId == PINNED_STACK_ID) {
mService.mTaskChangeNotificationController.notifyActivityUnpinned();
}
}
@@ -5109,12 +5120,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
- if (!task.inFreeformWindowingMode()) {
+ if (mTaskPositioner == null) {
return false;
}
- mStackSupervisor.getLaunchingTaskPositioner()
- .updateDefaultBounds(task, mTaskHistory, windowLayout);
-
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory, windowLayout);
return true;
}
@@ -5239,12 +5248,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
@Override
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
- + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()) + ", "
- + mTaskHistory.size() + " tasks}";
+ + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
}
- void onLockTaskPackagesUpdated() {
+ void onLockTaskPackagesUpdatedLocked() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
mTaskHistory.get(taskNdx).setLockTaskAuth();
}
diff --git a/com/android/server/am/ActivityStackSupervisor.java b/com/android/server/am/ActivityStackSupervisor.java
index 5c91e3cc..da2827a6 100644
--- a/com/android/server/am/ActivityStackSupervisor.java
+++ b/com/android/server/am/ActivityStackSupervisor.java
@@ -21,7 +21,11 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -31,13 +35,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -46,7 +47,6 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
-
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
@@ -86,13 +86,12 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS;
import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
-
import static java.lang.Integer.MAX_VALUE;
import android.Manifest;
@@ -103,6 +102,7 @@ import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityOptions;
@@ -176,8 +176,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
- RecentTasks.Callbacks {
+public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
@@ -287,7 +286,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityManagerService mService;
- RecentTasks mRecentTasks;
+ private RecentTasks mRecentTasks;
final ActivityStackSupervisorHandler mHandler;
@@ -295,10 +294,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- LaunchingTaskPositioner mTaskPositioner = new LaunchingTaskPositioner();
-
/** Counter for next free stack ID to use for dynamic activity stacks. */
- private int mNextFreeStackId = 0;
+ private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
/**
* Maps the task identifier that activities are currently being started in to the userId of the
@@ -579,7 +576,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void setRecentTasks(RecentTasks recentTasks) {
mRecentTasks = recentTasks;
- mRecentTasks.registerCallback(this);
}
/**
@@ -631,6 +627,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return stack != null && stack == mFocusedStack;
}
+ /** The top most stack on its display. */
+ boolean isFrontStackOnDisplay(ActivityStack stack) {
+ return isFrontOfStackList(stack, stack.getDisplay().mStacks);
+ }
+
+ private boolean isFrontOfStackList(ActivityStack stack, List<ActivityStack> stackList) {
+ return stack == stackList.get((stackList.size() - 1));
+ }
+
/** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
if (!focusCandidate.isFocusable()) {
@@ -726,9 +731,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ ActivityStack stack = stacks.get(stackNdx);
final TaskRecord task = stack.taskForIdLocked(id);
if (task != null) {
return task;
@@ -744,7 +749,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Otherwise, check the recent tasks and return if we find it there and we are not restoring
// the task from recents
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
- final TaskRecord task = mRecentTasks.getTask(id);
+ final TaskRecord task = mRecentTasks.taskForIdLocked(id);
if (task == null) {
if (DEBUG_RECENTS) {
@@ -771,10 +776,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ActivityRecord isInAnyStackLocked(IBinder token) {
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.isInStackLocked(token);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord r = stacks.get(stackNdx).isInStackLocked(token);
if (r != null) {
return r;
}
@@ -812,21 +816,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void lockAllProfileTasks(@UserIdInt int userId) {
mWindowManager.deferSurfaceLayout();
try {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final List<TaskRecord> tasks = stack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
- final TaskRecord task = tasks.get(taskNdx);
-
- // Check the task for a top activity belonging to userId, or returning a
- // result to an activity belonging to userId. Example case: a document
- // picker for personal files, opened by a work app, should still get locked.
- if (taskTopActivityIsUser(task, userId)) {
- mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
- task.taskId, userId);
- }
+ final List<ActivityStack> stacks = getStacks();
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; stackNdx--) {
+ final List<TaskRecord> tasks = stacks.get(stackNdx).getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+ final TaskRecord task = tasks.get(taskNdx);
+
+ // Check the task for a top activity belonging to userId, or returning a result
+ // to an activity belonging to userId. Example case: a document picker for
+ // personal files, opened by a work app, should still get locked.
+ if (taskTopActivityIsUser(task, userId)) {
+ mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
+ task.taskId, userId);
}
}
}
@@ -857,7 +858,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
- while (mRecentTasks.containsTaskId(candidateTaskId, userId)
+ while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
|| anyTaskForIdLocked(
candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
@@ -892,9 +893,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack)) {
continue;
}
@@ -927,9 +928,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean allResumedActivitiesIdle() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
@@ -948,9 +949,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean allResumedActivitiesComplete() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (isFocusedStack(stack)) {
final ActivityRecord r = stack.mResumedActivity;
if (r != null && r.state != RESUMED) {
@@ -967,12 +968,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return true;
}
- private boolean allResumedActivitiesVisible() {
+ boolean allResumedActivitiesVisible() {
boolean foundResumed = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.mResumedActivity;
if (r != null) {
if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
@@ -996,9 +997,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
@@ -1013,9 +1014,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean allPausedActivitiesComplete() {
boolean pausing = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
if (r != null && r.state != PAUSED && r.state != STOPPED && r.state != STOPPING) {
if (DEBUG_STATES) {
@@ -1033,10 +1034,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void cancelInitializingActivities() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.cancelInitializingActivities();
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stacks.get(stackNdx).cancelInitializingActivities();
}
}
}
@@ -1134,10 +1134,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
final int displayId = mTmpOrderedDisplayIds.get(i);
- final ActivityDisplay display = mActivityDisplays.get(displayId);
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
- if (stack != focusedStack && stack.isTopStackOnDisplay() && stack.isFocusable()) {
+ final List<ActivityStack> stacks = mActivityDisplays.get(displayId).mStacks;
+ if (stacks == null) {
+ continue;
+ }
+ for (int j = stacks.size() - 1; j >= 0; --j) {
+ final ActivityStack stack = stacks.get(j);
+ if (stack != focusedStack && isFrontStackOnDisplay(stack) && stack.isFocusable()) {
r = stack.topRunningActivityLocked();
if (r != null) {
return r;
@@ -1153,9 +1156,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>();
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
runningTaskLists.add(stackTaskList);
stack.getTasksLocked(stackTaskList, callingUid, allowed);
@@ -1604,16 +1607,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
- final boolean lockTaskMode = options.getLockTaskMode();
- if (lockTaskMode && !mService.mLockTaskController.isPackageWhitelisted(
- UserHandle.getUserId(callingUid), aInfo.packageName)) {
- final String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ") with lockTaskMode=true";
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
}
return true;
@@ -1935,10 +1928,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean handleAppDiedLocked(ProcessRecord app) {
boolean hasVisibleActivities = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- hasVisibleActivities |= stack.handleAppDiedLocked(app);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ hasVisibleActivities |= stacks.get(stackNdx).handleAppDiedLocked(app);
}
}
return hasVisibleActivities;
@@ -1946,10 +1938,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void closeSystemDialogsLocked() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.closeSystemDialogsLocked();
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stacks.get(stackNdx).closeSystemDialogsLocked();
}
}
}
@@ -1975,9 +1966,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean doit, boolean evenPersistent, int userId) {
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
packageName, filterByClasses, doit, evenPersistent, userId)) {
didSomething = true;
@@ -1997,9 +1988,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// hosted by the process that is actually still the foreground.
ProcessRecord fgApp = null;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (isFocusedStack(stack)) {
if (stack.mResumedActivity != null) {
fgApp = stack.mResumedActivity.app;
@@ -2049,10 +2040,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.updateActivityApplicationInfoLocked(aInfo);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stacks.get(stackNdx).updateActivityApplicationInfoLocked(aInfo);
}
}
}
@@ -2061,10 +2051,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
TaskRecord finishedTask = null;
ActivityStack focusedStack = getFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- final int numStacks = display.getChildCount();
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityStack stack = stacks.get(stackNdx);
TaskRecord t = stack.finishTopRunningActivityLocked(app, reason);
if (stack == focusedStack || finishedTask == null) {
finishedTask = t;
@@ -2076,10 +2066,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void finishVoiceTask(IVoiceInteractionSession session) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- final int numStacks = display.getChildCount();
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityStack stack = stacks.get(stackNdx);
stack.finishVoiceTask(session);
}
}
@@ -2115,8 +2105,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
- if (stack.resizeStackWithLaunchBounds()) {
- resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+ if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
+ resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
@@ -2135,7 +2125,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
"findTaskToMoveToFront: moved to front of stack=" + currentStack);
handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY,
- currentStack, forceNonResizeable);
+ currentStack.mStackId, forceNonResizeable);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
@@ -2149,10 +2139,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|| mService.mSupportsFreeformWindowManagement;
}
- LaunchingTaskPositioner getLaunchingTaskPositioner() {
- return mTaskPositioner;
- }
-
protected <T extends ActivityStack> T getStack(int stackId) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
@@ -2330,8 +2316,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display != null) {
- for (int i = display.getChildCount() - 1; i >= 0; --i) {
- stack = (T) display.getChildAt(i);
+ for (int i = display.mStacks.size() - 1; i >= 0; --i) {
+ stack = (T) display.mStacks.get(i);
if (stack.isCompatible(windowingMode, activityType)) {
return stack;
}
@@ -2399,8 +2385,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
// Return the topmost valid stack on the display.
- for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = activityDisplay.getChildAt(i);
+ for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = activityDisplay.mStacks.get(i);
if (isValidLaunchStack(stack, displayId, r)) {
return stack;
}
@@ -2431,13 +2417,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
}
- if (!stack.isOnHomeDisplay()) {
+ if (StackId.isDynamicStack(stack.mStackId)) {
return r.canBeLaunchedOnDisplay(displayId);
}
Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
return false;
}
+ ArrayList<ActivityStack> getStacks() {
+ ArrayList<ActivityStack> allStacks = new ArrayList<>();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ allStacks.addAll(mActivityDisplays.valueAt(displayNdx).mStacks);
+ }
+ return allStacks;
+ }
+
+ ArrayList<ActivityStack> getStacksOnDefaultDisplay() {
+ return mActivityDisplays.valueAt(DEFAULT_DISPLAY).mStacks;
+ }
+
/**
* Get next focusable stack in the system. This will search across displays and stacks
* in last-focused order for a focusable and visible stack, different from the target stack.
@@ -2452,9 +2450,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
final int displayId = mTmpOrderedDisplayIds.get(i);
// If a display is registered in WM, it must also be available in AM.
- final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
+ @SuppressWarnings("ConstantConditions")
+ final List<ActivityStack> stacks = getActivityDisplayOrCreateLocked(displayId).mStacks;
+ for (int j = stacks.size() - 1; j >= 0; --j) {
+ final ActivityStack stack = stacks.get(j);
if (stack != currentFocus && stack.isFocusable()
&& stack.shouldBeVisible(null)) {
return stack;
@@ -2512,17 +2511,20 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return null;
}
- void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
- Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
- boolean deferResume) {
-
- if (stack.inSplitScreenPrimaryWindowingMode()) {
+ void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
+ boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
+ if (stackId == DOCKED_STACK_ID) {
resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
preserveWindows, deferResume);
return;
}
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+ return;
+ }
- final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+ final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack();
if (!allowResizeInDockedMode
&& !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
// If the docked stack exists, don't resize non-floating stacks independently of the
@@ -2530,7 +2532,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
if (stack.supportsSplitScreenWindowingMode()) {
@@ -2582,7 +2584,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
/**
* TODO: This should just change the windowing mode and resize vs. actually moving task around.
- * Can do that once we are no longer using static stack ids.
+ * Can do that once we are no longer using static stack ids. Specially when
+ * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
*/
private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
int toDisplayId, boolean onTop) {
@@ -2597,12 +2600,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
- for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = toDisplay.getChildAt(i);
+ final ArrayList<ActivityStack> displayStacks = toDisplay.mStacks;
+ for (int i = displayStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = displayStacks.get(i);
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- resizeStackLocked(otherStack, null, null, null, PRESERVE_WINDOWS,
+ resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
@@ -2693,7 +2697,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
- final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
@@ -2722,10 +2727,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
- final ActivityDisplay display = getDefaultDisplay();
+ final ArrayList<ActivityStack> stacks = getStacksOnDefaultDisplay();
final Rect otherTaskRect = new Rect();
- for (int i = display.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack current = display.getChildAt(i);
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = stacks.get(i);
if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
@@ -2739,7 +2744,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
- resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+ resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2757,7 +2762,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
// TODO(multi-display): Pinned stack display should be passed in.
- final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+ final PinnedActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -2795,7 +2801,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- private void removeStackInSurfaceTransaction(ActivityStack stack) {
+ private void removeStackInSurfaceTransaction(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
/**
@@ -2825,12 +2836,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
/**
- * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
+ * Removes the stack associated with the given {@param stackId}. If the {@param stackId} is the
* pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
* instead moved back onto the fullscreen stack.
*/
- void removeStack(ActivityStack stack) {
- mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
+ void removeStackLocked(int stackId) {
+ mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stackId));
}
/**
@@ -2881,9 +2892,23 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return false;
}
+ void addRecentActivity(ActivityRecord r) {
+ if (r == null) {
+ return;
+ }
+ final TaskRecord task = r.getTask();
+ mRecentTasks.addLocked(task);
+ task.touchActiveTime();
+ }
+
+ void removeTaskFromRecents(TaskRecord task) {
+ mRecentTasks.remove(task);
+ task.removedFromRecents();
+ }
+
void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
if (removeFromRecents) {
- mRecentTasks.remove(tr);
+ removeTaskFromRecents(tr);
}
ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
@@ -2954,7 +2979,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
int getNextStackId() {
while (true) {
- if (getStack(mNextFreeStackId) == null) {
+ if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
+ && getStack(mNextFreeStackId) == null) {
break;
}
mNextFreeStackId++;
@@ -2963,8 +2989,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
/**
- * Called to restore the state of the task into the stack that it's supposed to go into.
- *
+ * Restores a recent task to a stack
* @param task The recent task to be restored.
* @param aOptions The activity options to use for restoration.
* @return true if the task has been restored successfully.
@@ -2995,22 +3020,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return true;
}
- @Override
- public void onRecentTaskAdded(TaskRecord task) {
- task.touchActiveTime();
- }
-
- @Override
- public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
- if (wasTrimmed) {
- // Task was trimmed from the recent tasks list -- remove the active task record as well
- // since the user won't really be able to go back to it
- removeTaskByIdLocked(task.taskId, false /* killProcess */,
- false /* removeFromRecents */);
- }
- task.removedFromRecents();
- }
-
/**
* Move stack with all its existing content to specified display.
* @param stackId Id of stack to move.
@@ -3151,7 +3160,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
- resizeStackLocked(stack, task.mBounds, null /* tempTaskBounds */,
+ resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
@@ -3248,9 +3257,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ActivityRecord affinityMatch = null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (!r.hasCompatibleActivityType(stack)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
+ stack);
@@ -3283,13 +3292,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
- boolean compareIntentFilters) {
+ boolean compareIntentFilters) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord ar = stack.findActivityLocked(
- intent, info, compareIntentFilters);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord ar = stacks.get(stackNdx)
+ .findActivityLocked(intent, info, compareIntentFilters);
if (ar != null) {
return ar;
}
@@ -3383,8 +3391,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
// Set the sleeping state of the stacks on the display.
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = display.mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
if (displayShouldSleep) {
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
@@ -3446,13 +3455,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
boolean allSleep = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
if (allowDelay) {
- allSleep &= stack.goToSleepIfPossible(shuttingDown);
+ allSleep &= stacks.get(stackNdx).goToSleepIfPossible(shuttingDown);
} else {
- stack.goToSleep();
+ stacks.get(stackNdx).goToSleep();
}
}
}
@@ -3477,10 +3485,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void handleAppCrashLocked(ProcessRecord app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.handleAppCrashLocked(app);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ int stackNdx = stacks.size() - 1;
+ while (stackNdx >= 0) {
+ stacks.get(stackNdx).handleAppCrashLocked(app);
+ stackNdx--;
}
}
}
@@ -3491,7 +3500,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityStack stack = task.getStack();
r.mLaunchTaskBehind = false;
- mRecentTasks.add(task);
+ mRecentTasks.addLocked(task);
mService.mTaskChangeNotificationController.notifyTaskStackChanged();
r.setVisibility(false);
@@ -3513,9 +3522,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
try {
// 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.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int topStackNdx = stacks.size() - 1;
+ for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
}
}
@@ -3526,9 +3536,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int topStackNdx = stacks.size() - 1;
+ for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
stack.addStartingWindowsForVisibleActivities(taskSwitch);
}
}
@@ -3544,20 +3555,20 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
mTaskLayersChanged = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
int baseLayer = 0;
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- baseLayer += stack.rankTaskLayers(baseLayer);
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ baseLayer += stacks.get(stackNdx).rankTaskLayers(baseLayer);
}
}
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int topStackNdx = stacks.size() - 1;
+ for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
stack.clearOtherAppTimeTrackers(except);
}
}
@@ -3565,9 +3576,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final int numStacks = stacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
stack.scheduleDestroyActivities(app, reason);
}
}
@@ -3619,11 +3631,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// let's iterate through the tasks and release the oldest one.
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- final int stackCount = display.getChildCount();
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
// Step through all stacks starting from behind, to hit the oldest things first.
- for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = 0; stackNdx < stacks.size(); stackNdx++) {
+ final ActivityStack stack = stacks.get(stackNdx);
// Try to release activities in this stack; if we manage to, we are done.
if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
return;
@@ -3635,14 +3646,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean switchUserLocked(int userId, UserState uss) {
final int focusStackId = mFocusedStack.getStackId();
// We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenStack();
if (dockedStack != null) {
moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
// appropriate.
- removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ removeStackLocked(PINNED_STACK_ID);
mUserStackInFront.put(mCurrentUser, focusStackId);
final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
@@ -3650,9 +3661,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mStartingUsers.add(uss);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
stack.switchUserLocked(userId);
TaskRecord task = stack.topTask();
if (task != null) {
@@ -3750,9 +3761,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void validateTopActivitiesLocked() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.state;
if (isFocusedStack(stack)) {
@@ -3787,7 +3798,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
final ActivityDisplay display = mActivityDisplays.valueAt(i);
- display.dump(pw, prefix);
+ pw.println(prefix + "displayId=" + display.mDisplayId + " mStacks=" + display.mStacks);
}
if (!mWaitingForActivityVisible.isEmpty()) {
pw.print(prefix); pw.println("mWaitingForActivityVisible=");
@@ -3800,7 +3811,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mService.mLockTaskController.dump(pw, prefix);
}
- public void writeToProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
@@ -3816,6 +3828,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
} else {
proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
}
+ proto.end(token);
}
/**
@@ -3844,9 +3857,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ ActivityStack stack = stacks.get(stackNdx);
if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
activities.addAll(stack.getDumpActivitiesLocked(name));
}
@@ -3879,13 +3892,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
pw.println(" (activities from top to bottom):");
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
pw.println();
- pw.println(" Stack #" + stack.mStackId
- + ": type=" + activityTypeToString(stack.getActivityType())
- + " mode=" + windowingModeToString(stack.getWindowingMode()));
+ pw.println(" Stack #" + stack.mStackId + ":");
pw.println(" mFullscreen=" + stack.mFullscreen);
pw.println(" isSleeping=" + stack.shouldSleepActivities());
pw.println(" mBounds=" + stack.mBounds);
@@ -4129,29 +4140,30 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
synchronized (mService) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
- if (activityDisplay == null) {
- return;
- }
- final boolean destroyContentOnRemoval
- = activityDisplay.shouldDestroyContentOnRemove();
- while (activityDisplay.getChildCount() > 0) {
- final ActivityStack stack = activityDisplay.getChildAt(0);
- if (destroyContentOnRemoval) {
- moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY, false /* onTop */);
- stack.finishAllActivitiesLocked(true /* immediately */);
- } else {
- // Moving all tasks to fullscreen stack, because it's guaranteed to be
- // a valid launch stack for all activities. This way the task history from
- // external display will be preserved on primary after move.
- moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+ ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ if (activityDisplay != null) {
+ final boolean destroyContentOnRemoval
+ = activityDisplay.shouldDestroyContentOnRemove();
+ final ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
+ while (!stacks.isEmpty()) {
+ final ActivityStack stack = stacks.get(0);
+ if (destroyContentOnRemoval) {
+ moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY,
+ false /* onTop */);
+ stack.finishAllActivitiesLocked(true /* immediately */);
+ } else {
+ // Moving all tasks to fullscreen stack, because it's guaranteed to be
+ // a valid launch stack for all activities. This way the task history from
+ // external display will be preserved on primary after move.
+ moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+ }
}
- }
- releaseSleepTokens(activityDisplay);
+ releaseSleepTokens(activityDisplay);
- mActivityDisplays.remove(displayId);
- mWindowManager.onDisplayRemoved(displayId);
+ mActivityDisplays.remove(displayId);
+ mWindowManager.onDisplayRemoved(displayId);
+ }
}
}
@@ -4223,7 +4235,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
info.userId = stack.mCurrentUser;
info.visible = stack.shouldBeVisible(null);
// A stack might be not attached to a display.
- info.position = display != null ? display.getIndexOf(stack) : 0;
+ info.position = display != null ? display.mStacks.indexOf(stack) : 0;
info.configuration.setTo(stack.getConfiguration());
ArrayList<TaskRecord> tasks = stack.getAllTasks();
@@ -4269,25 +4281,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ArrayList<StackInfo> getAllStackInfosLocked() {
ArrayList<StackInfo> list = new ArrayList<>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- list.add(getStackInfo(stack));
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
+ list.add(getStackInfo(stacks.get(ndx)));
}
}
return list;
}
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
- int preferredDisplayId, ActivityStack actualStack) {
+ int preferredDisplayId, int actualStackId) {
handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
- actualStack, false /* forceNonResizable */);
+ actualStackId, false /* forceNonResizable */);
}
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
- int preferredDisplayId, ActivityStack actualStack, boolean forceNonResizable) {
+ int preferredDisplayId, int actualStackId, boolean forceNonResizable) {
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
+ final ActivityStack actualStack = getStack(actualStackId);
final boolean inSplitScreenMode = actualStack != null
&& actualStack.inSplitScreenWindowingMode();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
@@ -4303,8 +4315,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// The task landed on an inappropriate display somehow, move it to the default
// display.
// TODO(multi-display): Find proper stack for the task on the default display.
- mService.setTaskWindowingMode(task.taskId,
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
+ mService.moveTaskToStack(task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+ true /* toTop */);
launchOnSecondaryDisplayFailed = true;
} else {
// The task might have landed on a display different from requested.
@@ -4334,9 +4346,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
- final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenStack();
if (dockedStack != null) {
- moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+ moveTasksToFullscreenStackLocked(dockedStack,
+ actualStackId == dockedStack.getStackId());
}
} else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
@@ -4389,7 +4402,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
final ActivityStack stack = task.getStack();
if (prevStack == null || prevStack == stack
- || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
+ || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
return;
}
@@ -4548,13 +4561,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (display == null) {
return null;
}
- for (int i = display.getChildCount() - 1; i >= 0; i--) {
- if (display.getChildAt(i) == stack && i > 0) {
- return display.getChildAt(i - 1);
+ final ArrayList<ActivityStack> stacks = display.mStacks;
+ for (int i = stacks.size() - 1; i >= 0; i--) {
+ if (stacks.get(i) == stack && i > 0) {
+ return stacks.get(i - 1);
}
}
throw new IllegalStateException("Failed to find a stack behind stack=" + stack
- + " in=" + display);
+ + " in=" + stacks);
}
/**
@@ -4667,8 +4681,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
final ActivityDisplay display = mActivityDisplays.valueAt(i);
// Traverse all stacks on a display.
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
+ for (int j = display.mStacks.size() - 1; j >= 0; j--) {
+ final ActivityStack stack = display.mStacks.get(j);
// Get top activity from a visible stack and add it to the list.
if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.topActivity();
diff --git a/com/android/server/am/ActivityStarter.java b/com/android/server/am/ActivityStarter.java
index 6f74d851..d444c663 100644
--- a/com/android/server/am/ActivityStarter.java
+++ b/com/android/server/am/ActivityStarter.java
@@ -26,9 +26,15 @@ import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.isDynamicStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -115,6 +121,7 @@ import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
import java.text.DateFormat;
@@ -133,11 +140,11 @@ class ActivityStarter {
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
- private static final int INVALID_LAUNCH_MODE = -1;
private final ActivityManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private final ActivityStartInterceptor mInterceptor;
+ private WindowManagerService mWindowManager;
final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
@@ -147,7 +154,9 @@ class ActivityStarter {
private int mCallingUid;
private ActivityOptions mOptions;
- private int mLaunchMode;
+ private boolean mLaunchSingleTop;
+ private boolean mLaunchSingleInstance;
+ private boolean mLaunchSingleTask;
private boolean mLaunchTaskBehind;
private int mLaunchFlags;
@@ -157,7 +166,6 @@ class ActivityStarter {
private boolean mDoResume;
private int mStartFlags;
private ActivityRecord mSourceRecord;
-
// The display to launch the activity onto, barring any strong reason to do otherwise.
private int mPreferredDisplayId;
@@ -187,6 +195,8 @@ class ActivityStarter {
private IVoiceInteractionSession mVoiceSession;
private IVoiceInteractor mVoiceInteractor;
+ private boolean mUsingVr2dDisplay;
+
// Last home activity record we attempted to start
private final ActivityRecord[] mLastHomeActivityStartRecord = new ActivityRecord[1];
// The result of the last home activity we attempted to start.
@@ -206,9 +216,11 @@ class ActivityStarter {
mCallingUid = -1;
mOptions = null;
+ mLaunchSingleTop = false;
+ mLaunchSingleInstance = false;
+ mLaunchSingleTask = false;
mLaunchTaskBehind = false;
mLaunchFlags = 0;
- mLaunchMode = INVALID_LAUNCH_MODE;
mLaunchBounds = null;
@@ -236,13 +248,16 @@ class ActivityStarter {
mVoiceSession = null;
mVoiceInteractor = null;
+ mUsingVr2dDisplay = false;
+
mIntentDelivered = false;
}
- ActivityStarter(ActivityManagerService service) {
+ ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
mService = service;
- mSupervisor = mService.mStackSupervisor;
+ mSupervisor = supervisor;
mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
+ mUsingVr2dDisplay = false;
}
int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
@@ -595,41 +610,38 @@ class ActivityStarter {
mSupervisor.reportTaskToFrontNoLaunch(mStartActivity);
}
- ActivityStack startedActivityStack = null;
+ int startedActivityStackId = INVALID_STACK_ID;
final ActivityStack currentStack = r.getStack();
if (currentStack != null) {
- startedActivityStack = currentStack;
+ startedActivityStackId = currentStack.mStackId;
} else if (mTargetStack != null) {
- startedActivityStack = targetStack;
+ startedActivityStackId = targetStack.mStackId;
}
- if (startedActivityStack == null) {
- return;
- }
-
- if (startedActivityStack.inSplitScreenPrimaryWindowingMode()) {
- final ActivityStack homeStack = mSupervisor.mHomeStack;
+ if (startedActivityStackId == DOCKED_STACK_ID) {
+ final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
if (homeStackVisible) {
// We launch an activity while being in home stack, which means either launcher or
// recents into docked stack. We don't want the launched activity to be alone in a
// docked stack, so we want to immediately launch recents too.
if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
- mService.mWindowManager.showRecentApps(true /* fromHome */);
+ mWindowManager.showRecentApps(true /* fromHome */);
}
return;
}
boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null);
- if (startedActivityStack.inPinnedWindowingMode()
- && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP
- || clearedTask)) {
+ if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
+ || result == START_DELIVERED_TO_TOP || clearedTask)) {
// The activity was already running in the pinned stack so it wasn't started, but either
// brought to the front or the new intent was delivered to it since it was already in
// front. Notify anyone interested in this piece of information.
mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
clearedTask);
+ return;
}
}
@@ -1050,7 +1062,7 @@ class ActivityStarter {
// operations.
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
- || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+ || mLaunchSingleInstance || mLaunchSingleTask) {
final TaskRecord task = reusedActivity.getTask();
// In this situation we want to remove all activities from the task up to the one
@@ -1133,7 +1145,7 @@ class ActivityStarter {
&& top.userId == mStartActivity.userId
&& top.app != null && top.app.thread != null
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
+ || mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
@@ -1152,7 +1164,7 @@ class ActivityStarter {
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
- preferredLaunchDisplayId, topStack);
+ preferredLaunchDisplayId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
}
@@ -1215,7 +1227,7 @@ class ActivityStarter {
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
- mService.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
} else {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
@@ -1228,13 +1240,13 @@ class ActivityStarter {
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
- } else if (mStartActivity != null) {
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
+ } else {
+ mSupervisor.addRecentActivity(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
- preferredLaunchDisplayId, mTargetStack);
+ preferredLaunchDisplayId, mTargetStack.mStackId);
return START_SUCCESS;
}
@@ -1256,13 +1268,13 @@ class ActivityStarter {
mLaunchBounds = getOverrideBounds(r, options, inTask);
- mLaunchMode = r.launchMode;
-
+ mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
+ mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
+ mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
mLaunchFlags = adjustLaunchFlagsToDocumentMode(
- r, LAUNCH_SINGLE_INSTANCE == mLaunchMode,
- LAUNCH_SINGLE_TASK == mLaunchMode, mIntent.getFlags());
+ r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
mLaunchTaskBehind = r.mLaunchTaskBehind
- && !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
+ && !mLaunchSingleTask && !mLaunchSingleInstance
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
sendNewTaskResultRequestIfNeeded();
@@ -1372,7 +1384,7 @@ class ActivityStarter {
// If this task is empty, then we are adding the first activity -- it
// determines the root, and must be launching as a NEW_TASK.
- if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+ if (mLaunchSingleInstance || mLaunchSingleTask) {
if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
ActivityOptions.abort(mOptions);
throw new IllegalArgumentException("Trying to launch singleInstance/Task "
@@ -1415,7 +1427,7 @@ class ActivityStarter {
// in any task/stack, however it could launch other activities like ResolverActivity,
// and we want those to stay in the original task.
if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
- && mSourceRecord.inFreeformWindowingMode()) {
+ && mSourceRecord.isFreeform()) {
mAddingToTask = true;
}
}
@@ -1434,7 +1446,7 @@ class ActivityStarter {
// instance... this new activity it is starting must go on its
// own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
- } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+ } else if (mLaunchSingleInstance || mLaunchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
@@ -1485,7 +1497,7 @@ class ActivityStarter {
// launch this as a new task behind the current one.
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
+ || mLaunchSingleInstance || mLaunchSingleTask;
// If bring to front is requested, and no result is requested and we have not been given
// an explicit task to launch in to, and we can find a task that was started with this
// same component, then instead of launching bring that one to the front.
@@ -1495,7 +1507,7 @@ class ActivityStarter {
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
- if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
+ if (mLaunchSingleInstance) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
@@ -1504,7 +1516,7 @@ class ActivityStarter {
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
- !(LAUNCH_SINGLE_TASK == mLaunchMode));
+ !mLaunchSingleTask);
} else {
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
@@ -1533,6 +1545,7 @@ class ActivityStarter {
if (DEBUG_STACK) {
Slog.d(TAG, "getSourceDisplayId :" + displayId);
}
+ mUsingVr2dDisplay = true;
return displayId;
}
@@ -1654,7 +1667,7 @@ class ActivityStarter {
}
mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
- WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+ WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack.mStackId);
// If the caller has requested that the target task be reset, then do so.
if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
@@ -1706,7 +1719,7 @@ class ActivityStarter {
// mTaskToReturnTo values and we don't want to overwrite them accidentally.
mMovedOtherTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+ || mLaunchSingleInstance || mLaunchSingleTask) {
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
mLaunchFlags);
if (top == null) {
@@ -1735,8 +1748,7 @@ class ActivityStarter {
// so we take that as a request to bring the task to the foreground. If the top
// activity in the task is the root activity, deliver this new intent to it if it
// desires.
- if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || LAUNCH_SINGLE_TOP == mLaunchMode)
+ if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
@@ -1940,7 +1952,7 @@ class ActivityStarter {
if (top != null && top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId) {
if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
+ || mLaunchSingleTop || mLaunchSingleTask) {
mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "inTaskToFront");
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1989,9 +2001,9 @@ class ActivityStarter {
return;
}
- final ActivityStack stack = task.getStack();
- if (stack != null && stack.resizeStackWithLaunchBounds()) {
- mService.resizeStack(stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+ final int stackId = task.getStackId();
+ if (StackId.resizeStackWithLaunchBounds(stackId)) {
+ mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
} else {
task.updateOverrideConfiguration(bounds);
}
@@ -2103,10 +2115,11 @@ class ActivityStarter {
}
if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- stack = display.getChildAt(stackNdx);
- if (!stack.isOnHomeDisplay()) {
+ final ArrayList<ActivityStack> homeDisplayStacks =
+ mSupervisor.getStacksOnDefaultDisplay();
+ for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stack = homeDisplayStacks.get(stackNdx);
+ if (isDynamicStack(stack.mStackId)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting focused stack=" + stack);
return stack;
@@ -2125,6 +2138,7 @@ class ActivityStarter {
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
final ActivityStack focusedStack = mSupervisor.mFocusedStack;
final boolean canUseFocusedStack;
+ final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
} else {
@@ -2147,7 +2161,7 @@ class ActivityStarter {
default:
// Dynamic stacks behave similarly to the fullscreen stack and can contain any
// resizeable task.
- canUseFocusedStack = !focusedStack.isOnHomeDisplay()
+ canUseFocusedStack = isDynamicStack(focusedStackId)
&& r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
}
}
@@ -2163,8 +2177,7 @@ class ActivityStarter {
return mReuseTask.getStack();
}
- final int vrDisplayId = mPreferredDisplayId == mService.mVr2dDisplayId
- ? mPreferredDisplayId : INVALID_DISPLAY;
+ final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY;
final ActivityStack launchStack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
vrDisplayId);
@@ -2192,7 +2205,7 @@ class ActivityStarter {
return mSupervisor.mFocusedStack;
}
- if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
+ if (parentStack != null && parentStack.isDockedStack()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
@@ -2202,8 +2215,8 @@ class ActivityStarter {
// If the parent is not in the docked stack, we check if there is docked window
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
- final ActivityStack dockedStack =
- mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
@@ -2223,8 +2236,8 @@ class ActivityStarter {
return newBounds;
}
- private boolean isLaunchModeOneOf(int mode1, int mode2) {
- return mode1 == mLaunchMode || mode2 == mLaunchMode;
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
}
static boolean isDocumentLaunchesIntoExisting(int flags) {
@@ -2305,11 +2318,11 @@ class ActivityStarter {
}
pw.print(prefix);
pw.print("mLaunchSingleTop=");
- pw.print(LAUNCH_SINGLE_TOP == mLaunchMode);
+ pw.print(mLaunchSingleTop);
pw.print(" mLaunchSingleInstance=");
- pw.print(LAUNCH_SINGLE_INSTANCE == mLaunchMode);
+ pw.print(mLaunchSingleInstance);
pw.print(" mLaunchSingleTask=");
- pw.println(LAUNCH_SINGLE_TASK == mLaunchMode);
+ pw.println(mLaunchSingleTask);
pw.print(prefix);
pw.print("mLaunchFlags=0x");
pw.print(Integer.toHexString(mLaunchFlags));
diff --git a/com/android/server/am/AppTaskImpl.java b/com/android/server/am/AppTaskImpl.java
deleted file mode 100644
index a4e2e70e..00000000
--- a/com/android/server/am/AppTaskImpl.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-
-import android.app.ActivityManager;
-import android.app.IAppTask;
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.UserHandle;
-
-/**
- * An implementation of IAppTask, that allows an app to manage its own tasks via
- * {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that
- * only the process that calls getAppTasks() can call the AppTask methods.
- */
-class AppTaskImpl extends IAppTask.Stub {
- private ActivityManagerService mService;
-
- private int mTaskId;
- private int mCallingUid;
-
- public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) {
- mService = service;
- mTaskId = taskId;
- mCallingUid = callingUid;
- }
-
- private void checkCaller() {
- if (mCallingUid != Binder.getCallingUid()) {
- throw new SecurityException("Caller " + mCallingUid
- + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
- }
- }
-
- @Override
- public void finishAndRemoveTask() {
- checkCaller();
-
- synchronized (mService) {
- long origId = Binder.clearCallingIdentity();
- try {
- // We remove the task from recents to preserve backwards
- if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
- REMOVE_FROM_RECENTS)) {
- throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public ActivityManager.RecentTaskInfo getTaskInfo() {
- checkCaller();
-
- synchronized (mService) {
- long origId = Binder.clearCallingIdentity();
- try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
- if (tr == null) {
- throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
- }
- return RecentTasks.createRecentTaskInfo(tr);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public void moveToFront() {
- checkCaller();
- // Will bring task to front if it already has a root activity.
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- mService.mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public int startActivity(IBinder whoThread, String callingPackage,
- Intent intent, String resolvedType, Bundle bOptions) {
- checkCaller();
-
- int callingUser = UserHandle.getCallingUserId();
- TaskRecord tr;
- IApplicationThread appThread;
- synchronized (mService) {
- tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
- if (tr == null) {
- throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
- }
- appThread = IApplicationThread.Stub.asInterface(whoThread);
- if (appThread == null) {
- throw new IllegalArgumentException("Bad app thread " + appThread);
- }
- }
- return mService.mActivityStarter.startActivityMayWait(appThread, -1, callingPackage,
- intent, resolvedType, null, null, null, null, 0, 0, null, null,
- null, bOptions, false, callingUser, tr, "AppTaskImpl");
- }
-
- @Override
- public void setExcludeFromRecents(boolean exclude) {
- checkCaller();
-
- synchronized (mService) {
- long origId = Binder.clearCallingIdentity();
- try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
- if (tr == null) {
- throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
- }
- Intent intent = tr.getBaseIntent();
- if (exclude) {
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- } else {
- intent.setFlags(intent.getFlags()
- & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-} \ No newline at end of file
diff --git a/com/android/server/am/BatteryStatsService.java b/com/android/server/am/BatteryStatsService.java
index 68ed9aec..7c9cd00e 100644
--- a/com/android/server/am/BatteryStatsService.java
+++ b/com/android/server/am/BatteryStatsService.java
@@ -297,12 +297,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
+
+ // TODO: remove this once we figure out properly where and how
+ // PROCESS_EVENT = 1112
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
+ // KEY_UID: 2
+ StatsLog.writeArray(1112, 1, 1, 1002, name, 2, uid);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
+
+ // TODO: remove this once we figure out properly where and how
+ // PROCESS_EVENT = 1112
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
+ // KEY_UID: 2
+ StatsLog.writeArray(1112, 1, 2, 1002, name, 2, uid);
}
}
@@ -320,9 +334,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
- StatsLog.write(StatsLog.PROCESS_STATE_CHANGED, uid, state);
-
mStats.noteUidProcessStateLocked(uid, state);
}
}
@@ -537,10 +548,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
-
mStats.noteScreenStateLocked(state);
+ // TODO: remove this once we figure out properly where and how
+ // SCREEN_EVENT = 2
+ // KEY_STATE: 1
+ // State value: state. We can change this to our own def later.
+ StatsLog.writeArray(2, 1, state);
}
if (DBG) Slog.d(TAG, "end noteScreenState");
}
diff --git a/com/android/server/am/BroadcastFilter.java b/com/android/server/am/BroadcastFilter.java
index 7ff227f5..f96b06fa 100644
--- a/com/android/server/am/BroadcastFilter.java
+++ b/com/android/server/am/BroadcastFilter.java
@@ -19,9 +19,6 @@ package com.android.server.am;
import android.content.IntentFilter;
import android.util.PrintWriterPrinter;
import android.util.Printer;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.server.am.proto.BroadcastFilterProto;
import java.io.PrintWriter;
@@ -47,38 +44,27 @@ final class BroadcastFilter extends IntentFilter {
instantApp = _instantApp;
visibleToInstantApp = _visibleToInstantApp;
}
-
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- super.writeToProto(proto, BroadcastFilterProto.INTENT_FILTER);
- if (requiredPermission != null) {
- proto.write(BroadcastFilterProto.REQUIRED_PERMISSION, requiredPermission);
- }
- proto.write(BroadcastFilterProto.HEX_HASH, Integer.toHexString(System.identityHashCode(this)));
- proto.write(BroadcastFilterProto.OWNING_USER_ID, owningUserId);
- proto.end(token);
- }
-
+
public void dump(PrintWriter pw, String prefix) {
dumpInReceiverList(pw, new PrintWriterPrinter(pw), prefix);
receiverList.dumpLocal(pw, prefix);
}
-
+
public void dumpBrief(PrintWriter pw, String prefix) {
dumpBroadcastFilterState(pw, prefix);
}
-
+
public void dumpInReceiverList(PrintWriter pw, Printer pr, String prefix) {
super.dump(pr, prefix);
dumpBroadcastFilterState(pw, prefix);
}
-
+
void dumpBroadcastFilterState(PrintWriter pw, String prefix) {
if (requiredPermission != null) {
pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
}
}
-
+
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("BroadcastFilter{");
diff --git a/com/android/server/am/BroadcastQueue.java b/com/android/server/am/BroadcastQueue.java
index c62cc38b..d8354549 100644
--- a/com/android/server/am/BroadcastQueue.java
+++ b/com/android/server/am/BroadcastQueue.java
@@ -51,12 +51,9 @@ import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
import static com.android.server.am.ActivityManagerDebugConfig.*;
-import com.android.server.am.proto.BroadcastQueueProto;
-
/**
* BROADCASTS
*
@@ -1588,55 +1585,6 @@ public final class BroadcastQueue {
&& (mPendingBroadcast == null);
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
- int N;
- N = mParallelBroadcasts.size();
- for (int i = N - 1; i >= 0; i--) {
- mParallelBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
- }
- N = mOrderedBroadcasts.size();
- for (int i = N - 1; i >= 0; i--) {
- mOrderedBroadcasts.get(i).writeToProto(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
- }
- if (mPendingBroadcast != null) {
- mPendingBroadcast.writeToProto(proto, BroadcastQueueProto.PENDING_BROADCAST);
- }
-
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r != null) {
- r.writeToProto(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
- }
- } while (ringIndex != lastIndex);
-
- lastIndex = ringIndex = mSummaryHistoryNext;
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
- intent.writeToProto(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
- false, true, true, false);
- proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
- mSummaryHistoryEnqueueTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
- mSummaryHistoryDispatchTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
- mSummaryHistoryFinishTime[ringIndex]);
- proto.end(summaryToken);
- } while (ringIndex != lastIndex);
- proto.end(token);
- }
-
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
diff --git a/com/android/server/am/BroadcastRecord.java b/com/android/server/am/BroadcastRecord.java
index 5b3b2a8e..6bc0744f 100644
--- a/com/android/server/am/BroadcastRecord.java
+++ b/com/android/server/am/BroadcastRecord.java
@@ -30,9 +30,6 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.server.am.proto.BroadcastRecordProto;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -334,17 +331,9 @@ final class BroadcastRecord extends Binder {
return didSomething;
}
- @Override
public String toString() {
return "BroadcastRecord{"
+ Integer.toHexString(System.identityHashCode(this))
+ " u" + userId + " " + intent.getAction() + "}";
}
-
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(BroadcastRecordProto.USER_ID, userId);
- proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction());
- proto.end(token);
- }
}
diff --git a/com/android/server/am/KeyguardController.java b/com/android/server/am/KeyguardController.java
index c3fed171..5c48f90d 100644
--- a/com/android/server/am/KeyguardController.java
+++ b/com/android/server/am/KeyguardController.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -46,6 +47,7 @@ import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -236,9 +238,9 @@ class KeyguardController {
final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
mOccluded = false;
mDismissingKeyguardActivity = null;
- final ActivityDisplay display = mStackSupervisor.getDefaultDisplay();
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ArrayList<ActivityStack> stacks = mStackSupervisor.getStacksOnDefaultDisplay();
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
// Only the focused stack top activity may control occluded state
if (mStackSupervisor.isFocusedStack(stack)) {
@@ -340,7 +342,7 @@ class KeyguardController {
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
if (stack == null) {
return;
}
diff --git a/com/android/server/am/LaunchingTaskPositioner.java b/com/android/server/am/LaunchingTaskPositioner.java
index 0dc73e98..d6523418 100644
--- a/com/android/server/am/LaunchingTaskPositioner.java
+++ b/com/android/server/am/LaunchingTaskPositioner.java
@@ -24,8 +24,8 @@ import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
+import android.view.Display;
import android.view.Gravity;
-import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -64,11 +64,43 @@ class LaunchingTaskPositioner {
private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2;
private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3;
+ private boolean mDefaultStartBoundsConfigurationSet = false;
private final Rect mAvailableRect = new Rect();
private final Rect mTmpProposal = new Rect();
private final Rect mTmpOriginal = new Rect();
- private final Point mDisplaySize = new Point();
+ private int mDefaultFreeformStartX;
+ private int mDefaultFreeformStartY;
+ private int mDefaultFreeformWidth;
+ private int mDefaultFreeformHeight;
+ private int mDefaultFreeformStepHorizontal;
+ private int mDefaultFreeformStepVertical;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
+
+ void setDisplay(Display display) {
+ Point size = new Point();
+ display.getSize(size);
+ mDisplayWidth = size.x;
+ mDisplayHeight = size.y;
+ }
+
+ void configure(Rect stackBounds) {
+ if (stackBounds == null) {
+ mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ } else {
+ mAvailableRect.set(stackBounds);
+ }
+ int width = mAvailableRect.width();
+ int height = mAvailableRect.height();
+ mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR;
+ mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR;
+ mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR;
+ mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR;
+ mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP);
+ mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP);
+ mDefaultStartBoundsConfigurationSet = true;
+ }
/**
* Tries to set task's bound in a way that it won't collide with any other task. By colliding
@@ -82,154 +114,104 @@ class LaunchingTaskPositioner {
*/
void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
@Nullable ActivityInfo.WindowLayout windowLayout) {
- updateAvailableRect(task, mAvailableRect);
-
+ if (!mDefaultStartBoundsConfigurationSet) {
+ return;
+ }
if (windowLayout == null) {
- positionCenter(task, tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
- getFreeformHeight(mAvailableRect));
+ positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight);
return;
}
- int width = getFinalWidth(windowLayout, mAvailableRect);
- int height = getFinalHeight(windowLayout, mAvailableRect);
+ int width = getFinalWidth(windowLayout);
+ int height = getFinalHeight(windowLayout);
int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (verticalGravity == Gravity.TOP) {
if (horizontalGravity == Gravity.RIGHT) {
- positionTopRight(task, tasks, mAvailableRect, width, height);
+ positionTopRight(task, tasks, width, height);
} else {
- positionTopLeft(task, tasks, mAvailableRect, width, height);
+ positionTopLeft(task, tasks, width, height);
}
} else if (verticalGravity == Gravity.BOTTOM) {
if (horizontalGravity == Gravity.RIGHT) {
- positionBottomRight(task, tasks, mAvailableRect, width, height);
+ positionBottomRight(task, tasks, width, height);
} else {
- positionBottomLeft(task, tasks, mAvailableRect, width, height);
+ positionBottomLeft(task, tasks, width, height);
}
} else {
// Some fancy gravity setting that we don't support yet. We just put the activity in the
// center.
Slog.w(TAG, "Received unsupported gravity: " + windowLayout.gravity
+ ", positioning in the center instead.");
- positionCenter(task, tasks, mAvailableRect, width, height);
+ positionCenter(task, tasks, width, height);
}
}
- private void updateAvailableRect(TaskRecord task, Rect availableRect) {
- final Rect stackBounds = task.getStack().mBounds;
-
- if (stackBounds != null) {
- availableRect.set(stackBounds);
- } else {
- task.getStack().getDisplay().mDisplay.getSize(mDisplaySize);
- availableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
- }
- }
-
- @VisibleForTesting
- static int getFreeformStartLeft(Rect bounds) {
- return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR;
- }
-
- @VisibleForTesting
- static int getFreeformStartTop(Rect bounds) {
- return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR;
- }
-
- @VisibleForTesting
- static int getFreeformWidth(Rect bounds) {
- return bounds.width() / WINDOW_SIZE_DENOMINATOR;
- }
-
- @VisibleForTesting
- static int getFreeformHeight(Rect bounds) {
- return bounds.height() / WINDOW_SIZE_DENOMINATOR;
- }
-
- @VisibleForTesting
- static int getHorizontalStep(Rect bounds) {
- return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP);
- }
-
- @VisibleForTesting
- static int getVerticalStep(Rect bounds) {
- return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP);
- }
-
-
-
- private int getFinalWidth(ActivityInfo.WindowLayout windowLayout, Rect availableRect) {
- int width = getFreeformWidth(availableRect);
+ private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) {
+ int width = mDefaultFreeformWidth;
if (windowLayout.width > 0) {
width = windowLayout.width;
}
if (windowLayout.widthFraction > 0) {
- width = (int) (availableRect.width() * windowLayout.widthFraction);
+ width = (int) (mAvailableRect.width() * windowLayout.widthFraction);
}
return width;
}
- private int getFinalHeight(ActivityInfo.WindowLayout windowLayout, Rect availableRect) {
- int height = getFreeformHeight(availableRect);
+ private int getFinalHeight(ActivityInfo.WindowLayout windowLayout) {
+ int height = mDefaultFreeformHeight;
if (windowLayout.height > 0) {
height = windowLayout.height;
}
if (windowLayout.heightFraction > 0) {
- height = (int) (availableRect.height() * windowLayout.heightFraction);
+ height = (int) (mAvailableRect.height() * windowLayout.heightFraction);
}
return height;
}
- private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
- mTmpProposal.set(availableRect.left, availableRect.bottom - height,
- availableRect.left + width, availableRect.bottom);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_RIGHT);
+ private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.left, mAvailableRect.bottom - height,
+ mAvailableRect.left + width, mAvailableRect.bottom);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
}
- private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
- mTmpProposal.set(availableRect.right - width, availableRect.bottom - height,
- availableRect.right, availableRect.bottom);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_LEFT);
+ private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.bottom - height,
+ mAvailableRect.right, mAvailableRect.bottom);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
}
- private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
- mTmpProposal.set(availableRect.left, availableRect.top,
- availableRect.left + width, availableRect.top + height);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_RIGHT);
+ private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.left, mAvailableRect.top,
+ mAvailableRect.left + width, mAvailableRect.top + height);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT);
}
- private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
- mTmpProposal.set(availableRect.right - width, availableRect.top,
- availableRect.right, availableRect.top + height);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_LEFT);
+ private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mAvailableRect.right - width, mAvailableRect.top,
+ mAvailableRect.right, mAvailableRect.top + height);
+ position(task, tasks, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT);
}
- private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
- final int defaultFreeformLeft = getFreeformStartLeft(availableRect);
- final int defaultFreeformTop = getFreeformStartTop(availableRect);
- mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop,
- defaultFreeformLeft + width, defaultFreeformTop + height);
- position(task, tasks, availableRect, mTmpProposal, ALLOW_RESTART,
- SHIFT_POLICY_DIAGONAL_DOWN);
+ private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks, int width,
+ int height) {
+ mTmpProposal.set(mDefaultFreeformStartX, mDefaultFreeformStartY,
+ mDefaultFreeformStartX + width, mDefaultFreeformStartY + height);
+ position(task, tasks, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN);
}
- private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect availableRect,
- Rect proposal, boolean allowRestart, int shiftPolicy) {
+ private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect proposal,
+ boolean allowRestart, int shiftPolicy) {
mTmpOriginal.set(proposal);
boolean restarted = false;
while (boundsConflict(proposal, tasks)) {
// Unfortunately there is already a task at that spot, so we need to look for some
// other place.
- shiftStartingPoint(proposal, availableRect, shiftPolicy);
- if (shiftedTooFar(proposal, availableRect, shiftPolicy)) {
+ shiftStartingPoint(proposal, shiftPolicy);
+ if (shiftedToFar(proposal, shiftPolicy)) {
// We don't want the task to go outside of the stack, because it won't look
// nice. Depending on the starting point we either restart, or immediately give up.
if (!allowRestart) {
@@ -238,13 +220,13 @@ class LaunchingTaskPositioner {
}
// We must have started not from the top. Let's restart from there because there
// might be some space there.
- proposal.set(availableRect.left, availableRect.top,
- availableRect.left + proposal.width(),
- availableRect.top + proposal.height());
+ proposal.set(mAvailableRect.left, mAvailableRect.top,
+ mAvailableRect.left + proposal.width(),
+ mAvailableRect.top + proposal.height());
restarted = true;
}
- if (restarted && (proposal.left > getFreeformStartLeft(availableRect)
- || proposal.top > getFreeformStartTop(availableRect))) {
+ if (restarted && (proposal.left > mDefaultFreeformStartX
+ || proposal.top > mDefaultFreeformStartY)) {
// If we restarted and crossed the initial position, let's not struggle anymore.
// The user already must have ton of tasks visible, we can just smack the new
// one in the center.
@@ -255,30 +237,27 @@ class LaunchingTaskPositioner {
task.updateOverrideConfiguration(proposal);
}
- private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) {
+ private boolean shiftedToFar(Rect start, int shiftPolicy) {
switch (shiftPolicy) {
case SHIFT_POLICY_HORIZONTAL_LEFT:
- return start.left < availableRect.left;
+ return start.left < mAvailableRect.left;
case SHIFT_POLICY_HORIZONTAL_RIGHT:
- return start.right > availableRect.right;
+ return start.right > mAvailableRect.right;
default: // SHIFT_POLICY_DIAGONAL_DOWN
- return start.right > availableRect.right || start.bottom > availableRect.bottom;
+ return start.right > mAvailableRect.right || start.bottom > mAvailableRect.bottom;
}
}
- private void shiftStartingPoint(Rect posposal, Rect availableRect, int shiftPolicy) {
- final int defaultFreeformStepHorizontal = getHorizontalStep(availableRect);
- final int defaultFreeformStepVertical = getVerticalStep(availableRect);
-
+ private void shiftStartingPoint(Rect posposal, int shiftPolicy) {
switch (shiftPolicy) {
case SHIFT_POLICY_HORIZONTAL_LEFT:
- posposal.offset(-defaultFreeformStepHorizontal, 0);
+ posposal.offset(-mDefaultFreeformStepHorizontal, 0);
break;
case SHIFT_POLICY_HORIZONTAL_RIGHT:
- posposal.offset(defaultFreeformStepHorizontal, 0);
+ posposal.offset(mDefaultFreeformStepHorizontal, 0);
break;
default: // SHIFT_POLICY_DIAGONAL_DOWN:
- posposal.offset(defaultFreeformStepHorizontal, defaultFreeformStepVertical);
+ posposal.offset(mDefaultFreeformStepHorizontal, mDefaultFreeformStepVertical);
break;
}
}
@@ -317,4 +296,8 @@ class LaunchingTaskPositioner {
return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE
&& Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE;
}
+
+ void reset() {
+ mDefaultStartBoundsConfigurationSet = false;
+ }
}
diff --git a/com/android/server/am/LockTaskController.java b/com/android/server/am/LockTaskController.java
index 940f9051..72b5de88 100644
--- a/com/android/server/am/LockTaskController.java
+++ b/com/android/server/am/LockTaskController.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.StatusBarManager.DISABLE_BACK;
import static android.app.StatusBarManager.DISABLE_HOME;
import static android.app.StatusBarManager.DISABLE_MASK;
@@ -58,6 +59,7 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
@@ -431,7 +433,7 @@ public class LockTaskController {
mWindowManager.executeAppTransition();
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
- DEFAULT_DISPLAY, task.getStack(), true /* forceNonResizable */);
+ DEFAULT_DISPLAY, task.getStackId(), true /* forceNonResizable */);
}
}
@@ -492,7 +494,11 @@ public class LockTaskController {
}
for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+ ArrayList<ActivityStack> stacks = mSupervisor.getChildAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
+ stack.onLockTaskPackagesUpdatedLocked();
+ }
}
final ActivityRecord r = mSupervisor.topRunningActivityLocked();
diff --git a/com/android/server/am/ProcessRecord.java b/com/android/server/am/ProcessRecord.java
index e8477231..0e318d9c 100644
--- a/com/android/server/am/ProcessRecord.java
+++ b/com/android/server/am/ProcessRecord.java
@@ -27,7 +27,6 @@ import android.util.Slog;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.proto.ProcessRecordProto;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -45,7 +44,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -623,22 +621,6 @@ final class ProcessRecord {
}
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(ProcessRecordProto.PID, pid);
- proto.write(ProcessRecordProto.PROCESS_NAME, processName);
- if (info.uid < Process.FIRST_APPLICATION_UID) {
- proto.write(ProcessRecordProto.UID, uid);
- } else {
- proto.write(ProcessRecordProto.USER_ID, userId);
- proto.write(ProcessRecordProto.APP_ID, UserHandle.getAppId(info.uid));
- if (uid != info.uid) {
- proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
- }
- }
- proto.end(token);
- }
-
public String toShortString() {
if (shortStringName != null) {
return shortStringName;
diff --git a/com/android/server/am/ReceiverList.java b/com/android/server/am/ReceiverList.java
index a9890631..6ade7361 100644
--- a/com/android/server/am/ReceiverList.java
+++ b/com/android/server/am/ReceiverList.java
@@ -21,8 +21,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.PrintWriterPrinter;
import android.util.Printer;
-import android.util.proto.ProtoOutputStream;
-import com.android.server.am.proto.ReceiverListProto;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -43,7 +41,7 @@ final class ReceiverList extends ArrayList<BroadcastFilter>
boolean linkedToDeath = false;
String stringName;
-
+
ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
owner = _owner;
@@ -61,31 +59,12 @@ final class ReceiverList extends ArrayList<BroadcastFilter>
public int hashCode() {
return System.identityHashCode(this);
}
-
+
public void binderDied() {
linkedToDeath = false;
owner.unregisterReceiver(receiver);
}
-
- void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- app.writeToProto(proto, ReceiverListProto.APP);
- proto.write(ReceiverListProto.PID, pid);
- proto.write(ReceiverListProto.UID, uid);
- proto.write(ReceiverListProto.USER, userId);
- if (curBroadcast != null) {
- curBroadcast.writeToProto(proto, ReceiverListProto.CURRENT);
- }
- proto.write(ReceiverListProto.LINKED_TO_DEATH, linkedToDeath);
- final int N = size();
- for (int i=0; i<N; i++) {
- BroadcastFilter bf = get(i);
- bf.writeToProto(proto, ReceiverListProto.FILTERS);
- }
- proto.write(ReceiverListProto.HEX_HASH, Integer.toHexString(System.identityHashCode(this)));
- proto.end(token);
- }
-
+
void dumpLocal(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("app="); pw.print(app != null ? app.toShortString() : null);
pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid);
@@ -95,7 +74,7 @@ final class ReceiverList extends ArrayList<BroadcastFilter>
pw.print(" linkedToDeath="); pw.println(linkedToDeath);
}
}
-
+
void dump(PrintWriter pw, String prefix) {
Printer pr = new PrintWriterPrinter(pw);
dumpLocal(pw, prefix);
@@ -110,7 +89,7 @@ final class ReceiverList extends ArrayList<BroadcastFilter>
bf.dumpInReceiverList(pw, pr, p2);
}
}
-
+
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/com/android/server/am/RecentTasks.java b/com/android/server/am/RecentTasks.java
index 78274bdc..365c5b1d 100644
--- a/com/android/server/am/RecentTasks.java
+++ b/com/android/server/am/RecentTasks.java
@@ -16,25 +16,15 @@
package com.android.server.am;
-import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import com.google.android.collect.Sets;
@@ -47,87 +37,43 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.Bundle;
import android.os.Environment;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.MutableBoolean;
-import android.util.MutableInt;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.am.ActivityStack.ActivityState;
-
import java.io.File;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
/**
- * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
- * least recent.
+ * Class for managing the recent tasks list.
*/
-class RecentTasks {
+class RecentTasks extends ArrayList<TaskRecord> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final boolean TRIMMED = true;
+ // Maximum number recent bitmaps to keep in memory.
+ private static final int MAX_RECENT_BITMAPS = 3;
private static final int DEFAULT_INITIAL_CAPACITY = 5;
// Whether or not to move all affiliated tasks to the front when one of the tasks is launched
private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
- // Comparator to sort by taskId
- private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
- (lhs, rhs) -> rhs.taskId - lhs.taskId;
-
- // Placeholder variables to keep track of activities/apps that are no longer avialble while
- // iterating through the recents list
- private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
- private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
-
- /**
- * Callbacks made when manipulating the list.
- */
- interface Callbacks {
- /**
- * Called when a task is added to the recent tasks list.
- */
- void onRecentTaskAdded(TaskRecord task);
-
- /**
- * Called when a task is removed from the recent tasks list.
- */
- void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
- }
-
/**
* Save recent tasks information across reboots.
*/
private final TaskPersister mTaskPersister;
private final ActivityManagerService mService;
- private final UserController mUserController;
-
- /**
- * Mapping of user id -> whether recent tasks have been loaded for that user.
- */
private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
DEFAULT_INITIAL_CAPACITY);
@@ -135,106 +81,21 @@ class RecentTasks {
* Stores for each user task ids that are taken by tasks residing in persistent storage. These
* tasks may or may not currently be in memory.
*/
- private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
+ final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
DEFAULT_INITIAL_CAPACITY);
- // List of all active recent tasks
- private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
- private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
-
- // These values are generally loaded from resources, but can be set dynamically in the tests
- private boolean mHasVisibleRecentTasks;
- private int mGlobalMaxNumTasks;
- private int mMinNumVisibleTasks;
- private int mMaxNumVisibleTasks;
- private long mActiveTasksSessionDurationMs;
-
// Mainly to avoid object recreation on multiple calls.
- private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
+ private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>();
private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
- private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
+ private final ActivityInfo mTmpActivityInfo = new ActivityInfo();
+ private final ApplicationInfo mTmpAppInfo = new ApplicationInfo();
- @VisibleForTesting
- RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
- UserController userController) {
+ RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
+ File systemDir = Environment.getDataSystemDirectory();
mService = service;
- mUserController = userController;
- mTaskPersister = taskPersister;
- mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
- mHasVisibleRecentTasks = true;
- }
-
- RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) {
- final File systemDir = Environment.getDataSystemDirectory();
- final Resources res = service.mContext.getResources();
- mService = service;
- mUserController = service.mUserController;
- mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
- mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
- mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
- loadParametersFromResources(service.mContext.getResources());
- }
-
- @VisibleForTesting
- void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
- long activeSessionDurationMs) {
- mMinNumVisibleTasks = minNumVisibleTasks;
- mMaxNumVisibleTasks = maxNumVisibleTasks;
- mActiveTasksSessionDurationMs = activeSessionDurationMs;
- }
-
- @VisibleForTesting
- void setGlobalMaxNumTasks(int globalMaxNumTasks) {
- mGlobalMaxNumTasks = globalMaxNumTasks;
- }
-
- /**
- * Loads the parameters from the system resources.
- */
- @VisibleForTesting
- void loadParametersFromResources(Resources res) {
- if (ActivityManager.isLowRamDeviceStatic()) {
- mMinNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
- mMaxNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
- } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
- mMinNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
- mMaxNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
- } else {
- mMinNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_minNumVisibleRecentTasks);
- mMaxNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
- }
- final int sessionDurationHrs = res.getInteger(
- com.android.internal.R.integer.config_activeTaskDurationHours);
- mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
- ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
- : -1;
- }
-
- void registerCallback(Callbacks callback) {
- mCallbacks.add(callback);
- }
-
- void unregisterCallback(Callbacks callback) {
- mCallbacks.remove(callback);
- }
-
- private void notifyTaskAdded(TaskRecord task) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRecentTaskAdded(task);
- }
- }
-
- private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
- }
+ mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
+ mStackSupervisor.setRecentTasks(this);
}
/**
@@ -245,7 +106,6 @@ class RecentTasks {
*/
void loadUserRecentsLocked(int userId) {
if (mUsersWithRecentsLoaded.get(userId)) {
- // User already loaded, return early
return;
}
@@ -254,14 +114,14 @@ class RecentTasks {
// Check if any tasks are added before recents is loaded
final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
- for (final TaskRecord task : mTasks) {
+ for (final TaskRecord task : this) {
if (task.userId == userId && shouldPersistTaskLocked(task)) {
preaddedTasks.put(task.taskId, true);
}
}
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
- mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
+ addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
cleanupLocked(userId);
mUsersWithRecentsLoaded.put(userId, true);
@@ -280,25 +140,11 @@ class RecentTasks {
}
}
- /**
- * @return whether the {@param taskId} is currently in use for the given user.
- */
- boolean containsTaskId(int taskId, int userId) {
+ boolean taskIdTakenForUserLocked(int taskId, int userId) {
loadPersistedTaskIdsForUserLocked(userId);
return mPersistedTaskIds.get(userId).get(taskId);
}
- /**
- * @return all the task ids for the user with the given {@param userId}.
- */
- SparseBooleanArray getTaskIdsForUser(int userId) {
- loadPersistedTaskIdsForUserLocked(userId);
- return mPersistedTaskIds.get(userId);
- }
-
- /**
- * Kicks off the task persister to write any pending tasks to disk.
- */
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
final ActivityStack stack = task != null ? task.getStack() : null;
if (stack != null && stack.isHomeOrRecentsStack()) {
@@ -318,8 +164,8 @@ class RecentTasks {
mPersistedTaskIds.valueAt(i).clear();
}
}
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- final TaskRecord task = mTasks.get(i);
+ for (int i = size() - 1; i >= 0; i--) {
+ final TaskRecord task = get(i);
if (shouldPersistTaskLocked(task)) {
// Set of persisted taskIds for task.userId should not be null here
// TODO Investigate why it can happen. For now initialize with an empty set
@@ -334,12 +180,12 @@ class RecentTasks {
}
private static boolean shouldPersistTaskLocked(TaskRecord task) {
- final ActivityStack stack = task.getStack();
+ final ActivityStack<?> stack = task.getStack();
return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
}
void onSystemReadyLocked() {
- mTasks.clear();
+ clear();
mTaskPersister.startPersisting();
}
@@ -379,6 +225,14 @@ class RecentTasks {
return usersWithRecentsLoaded;
}
+ private void unloadUserRecentsLocked(int userId) {
+ if (mUsersWithRecentsLoaded.get(userId)) {
+ Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
+ mUsersWithRecentsLoaded.delete(userId);
+ removeTasksForUserLocked(userId);
+ }
+ }
+
/**
* Removes recent tasks and any other state kept in memory for the passed in user. Does not
* touch the information present on persistent storage.
@@ -386,36 +240,44 @@ class RecentTasks {
* @param userId the id of the user
*/
void unloadUserDataFromMemoryLocked(int userId) {
- if (mUsersWithRecentsLoaded.get(userId)) {
- Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
- mUsersWithRecentsLoaded.delete(userId);
- removeTasksForUserLocked(userId);
- }
+ unloadUserRecentsLocked(userId);
mPersistedTaskIds.delete(userId);
mTaskPersister.unloadUserDataFromMemory(userId);
}
+ TaskRecord taskForIdLocked(int id) {
+ final int recentsCount = size();
+ for (int i = 0; i < recentsCount; i++) {
+ TaskRecord tr = get(i);
+ if (tr.taskId == id) {
+ return tr;
+ }
+ }
+ return null;
+ }
+
/** Remove recent tasks for a user. */
- private void removeTasksForUserLocked(int userId) {
+ void removeTasksForUserLocked(int userId) {
if(userId <= 0) {
Slog.i(TAG, "Can't remove recent task on user " + userId);
return;
}
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- TaskRecord tr = mTasks.get(i);
+ for (int i = size() - 1; i >= 0; --i) {
+ TaskRecord tr = get(i);
if (tr.userId == userId) {
if(DEBUG_TASKS) Slog.i(TAG_TASKS,
"remove RecentTask " + tr + " when finishing user" + userId);
- remove(mTasks.get(i));
+ remove(i);
+ tr.removedFromRecents();
}
}
}
void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
final Set<String> packageNames = Sets.newHashSet(packages);
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskRecord tr = mTasks.get(i);
+ for (int i = size() - 1; i >= 0; --i) {
+ final TaskRecord tr = get(i);
if (tr.realActivity != null
&& packageNames.contains(tr.realActivity.getPackageName())
&& tr.userId == userId
@@ -424,36 +286,7 @@ class RecentTasks {
notifyTaskPersisterLocked(tr, false);
}
}
- }
-
- void removeTasksByPackageName(String packageName, int userId) {
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskRecord tr = mTasks.get(i);
- final String taskPackageName =
- tr.getBaseIntent().getComponent().getPackageName();
- if (tr.userId != userId) return;
- if (!taskPackageName.equals(packageName)) return;
-
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
- }
- }
-
- void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
- int userId) {
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskRecord tr = mTasks.get(i);
- if (userId != UserHandle.USER_ALL && tr.userId != userId) {
- continue;
- }
- ComponentName cn = tr.intent.getComponent();
- final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
- && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
- if (sameComponent) {
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
- REMOVE_FROM_RECENTS);
- }
- }
}
/**
@@ -462,28 +295,24 @@ class RecentTasks {
* of affiliations.
*/
void cleanupLocked(int userId) {
- int recentsCount = mTasks.size();
+ int recentsCount = size();
if (recentsCount == 0) {
// Happens when called from the packagemanager broadcast before boot,
// or just any empty list.
return;
}
- // Clear the temp lists
- mTmpAvailActCache.clear();
- mTmpAvailAppCache.clear();
-
final IPackageManager pm = AppGlobals.getPackageManager();
for (int i = recentsCount - 1; i >= 0; i--) {
- final TaskRecord task = mTasks.get(i);
+ final TaskRecord task = get(i);
if (userId != UserHandle.USER_ALL && task.userId != userId) {
// Only look at tasks for the user ID of interest.
continue;
}
if (task.autoRemoveRecents && task.getTopActivity() == null) {
// This situation is broken, and we should just get rid of it now.
- mTasks.remove(i);
- notifyTaskRemoved(task, !TRIMMED);
+ remove(i);
+ task.removedFromRecents();
Slog.w(TAG, "Removing auto-remove without activity: " + task);
continue;
}
@@ -502,11 +331,11 @@ class RecentTasks {
continue;
}
if (ai == null) {
- ai = NO_ACTIVITY_INFO_TOKEN;
+ ai = mTmpActivityInfo;
}
mTmpAvailActCache.put(task.realActivity, ai);
}
- if (ai == NO_ACTIVITY_INFO_TOKEN) {
+ if (ai == mTmpActivityInfo) {
// This could be either because the activity no longer exists, or the
// app is temporarily gone. For the former we want to remove the recents
// entry; for the latter we want to mark it as unavailable.
@@ -521,15 +350,15 @@ class RecentTasks {
continue;
}
if (app == null) {
- app = NO_APPLICATION_INFO_TOKEN;
+ app = mTmpAppInfo;
}
mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
}
- if (app == NO_APPLICATION_INFO_TOKEN
+ if (app == mTmpAppInfo
|| (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
// Doesn't exist any more! Good-bye.
- mTasks.remove(i);
- notifyTaskRemoved(task, !TRIMMED);
+ remove(i);
+ task.removedFromRecents();
Slog.w(TAG, "Removing no longer valid recent: " + task);
continue;
} else {
@@ -561,197 +390,121 @@ class RecentTasks {
// Verify the affiliate chain for each task.
int i = 0;
- recentsCount = mTasks.size();
+ recentsCount = size();
while (i < recentsCount) {
i = processNextAffiliateChainLocked(i);
}
// recent tasks are now in sorted, affiliated order.
}
- /**
- * @return whether the given {@param task} can be added to the list without causing another
- * task to be trimmed as a result of that add.
- */
- private boolean canAddTaskWithoutTrim(TaskRecord task) {
- return findTrimIndexForAddTask(task) == -1;
- }
-
- /**
- * Returns the list of {@link ActivityManager.AppTask}s.
- */
- ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
- final ArrayList<IBinder> list = new ArrayList<>();
- final int size = mTasks.size();
- for (int i = 0; i < size; i++) {
- final TaskRecord tr = mTasks.get(i);
- // Skip tasks that do not match the caller. We don't need to verify
- // callingPackage, because we are also limiting to callingUid and know
- // that will limit to the correct security sandbox.
- if (tr.effectiveUid != callingUid) {
- continue;
- }
- Intent intent = tr.getBaseIntent();
- if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
- continue;
- }
- ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr);
- AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid);
- list.add(taskImpl.asBinder());
- }
- return list;
- }
-
- /**
- * @return the list of recent tasks for presentation.
- */
- ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
- boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
- final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
-
- if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
- Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
- return ParceledListSlice.emptyList();
+ private final boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
+ int recentsCount = size();
+ TaskRecord top = task;
+ int topIndex = taskIndex;
+ while (top.mNextAffiliate != null && topIndex > 0) {
+ top = top.mNextAffiliate;
+ topIndex--;
}
- loadUserRecentsLocked(userId);
-
- final Set<Integer> includedUsers = mUserController.getProfileIds(userId);
- includedUsers.add(Integer.valueOf(userId));
-
- final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
- final int size = mTasks.size();
- int numVisibleTasks = 0;
- for (int i = 0; i < size; i++) {
- final TaskRecord tr = mTasks.get(i);
-
- if (isVisibleRecentTask(tr)) {
- numVisibleTasks++;
- if (isInVisibleRange(tr, numVisibleTasks)) {
- // Fall through
- } else {
- // Not in visible range
- continue;
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
+ + topIndex + " from intial " + taskIndex);
+ // Find the end of the chain, doing a sanity check along the way.
+ boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
+ int endIndex = topIndex;
+ TaskRecord prev = top;
+ while (endIndex < recentsCount) {
+ TaskRecord cur = get(endIndex);
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
+ + endIndex + " " + cur);
+ if (cur == top) {
+ // Verify start of the chain.
+ if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": first task has next affiliate: " + prev);
+ sane = false;
+ break;
}
} else {
- // Not visible
- continue;
- }
-
- // Skip remaining tasks once we reach the requested size
- if (res.size() >= maxNum) {
- continue;
- }
-
- // Only add calling user or related users recent tasks
- if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
- continue;
- }
-
- if (tr.realActivitySuspended) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
- continue;
- }
-
- // Return the entry if desired by the caller. We always return
- // the first entry, because callers always expect this to be the
- // foreground app. We may filter others if the caller has
- // not supplied RECENT_WITH_EXCLUDED and there is some reason
- // we should exclude the entry.
-
- if (i == 0
- || withExcluded
- || (tr.intent == null)
- || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- == 0)) {
- if (!getTasksAllowed) {
- // If the caller doesn't have the GET_TASKS permission, then only
- // allow them to see a small subset of tasks -- their own and home.
- if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
- continue;
- }
- }
- if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
- // Don't include auto remove tasks that are finished or finishing.
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
- "Skipping, auto-remove without activity: " + tr);
- continue;
- }
- if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
- "Skipping, unavail real act: " + tr);
- continue;
+ // Verify middle of the chain's next points back to the one before.
+ if (cur.mNextAffiliate != prev
+ || cur.mNextAffiliateTaskId != prev.taskId) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": middle task " + cur + " @" + endIndex
+ + " has bad next affiliate "
+ + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
+ + ", expected " + prev);
+ sane = false;
+ break;
}
-
- if (!tr.mUserSetupComplete) {
- // Don't include task launched while user is not done setting-up.
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
- "Skipping, user setup not complete: " + tr);
- continue;
+ }
+ if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
+ // Chain ends here.
+ if (cur.mPrevAffiliate != null) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": last task " + cur + " has previous affiliate "
+ + cur.mPrevAffiliate);
+ sane = false;
}
-
- ActivityManager.RecentTaskInfo rti = RecentTasks.createRecentTaskInfo(tr);
- if (!getDetailedTasks) {
- rti.baseIntent.replaceExtras((Bundle)null);
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
+ break;
+ } else {
+ // Verify middle of the chain's prev points to a valid item.
+ if (cur.mPrevAffiliate == null) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": task " + cur + " has previous affiliate "
+ + cur.mPrevAffiliate + " but should be id "
+ + cur.mPrevAffiliate);
+ sane = false;
+ break;
}
-
- res.add(rti);
+ }
+ if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": task " + cur + " has affiliated id "
+ + cur.mAffiliatedTaskId + " but should be "
+ + task.mAffiliatedTaskId);
+ sane = false;
+ break;
+ }
+ prev = cur;
+ endIndex++;
+ if (endIndex >= recentsCount) {
+ Slog.wtf(TAG, "Bad chain ran off index " + endIndex
+ + ": last task " + prev);
+ sane = false;
+ break;
}
}
- return new ParceledListSlice<>(res);
- }
-
- /**
- * @return the list of persistable task ids.
- */
- void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
- final int size = mTasks.size();
- for (int i = 0; i < size; i++) {
- final TaskRecord task = mTasks.get(i);
- if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
- + " persistable=" + task.isPersistable);
- final ActivityStack stack = task.getStack();
- if ((task.isPersistable || task.inRecents)
- && (stack == null || !stack.isHomeOrRecentsStack())) {
- if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
- persistentTaskIds.add(task.taskId);
- } else {
- if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
- + task);
+ if (sane) {
+ if (endIndex < taskIndex) {
+ Slog.wtf(TAG, "Bad chain @" + endIndex
+ + ": did not extend to task " + task + " @" + taskIndex);
+ sane = false;
}
}
- }
-
- @VisibleForTesting
- ArrayList<TaskRecord> getRawTasks() {
- return mTasks;
- }
-
- /**
- * @return the task in the task list with the given {@param id} if one exists.
- */
- TaskRecord getTask(int id) {
- final int recentsCount = mTasks.size();
- for (int i = 0; i < recentsCount; i++) {
- TaskRecord tr = mTasks.get(i);
- if (tr.taskId == id) {
- return tr;
+ if (sane) {
+ // All looks good, we can just move all of the affiliated tasks
+ // to the top.
+ for (int i=topIndex; i<=endIndex; i++) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
+ + " from " + i + " to " + (i-topIndex));
+ TaskRecord cur = remove(i);
+ add(i - topIndex, cur);
}
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex
+ + " to " + endIndex);
+ return true;
}
- return null;
- }
- /**
- * Add a new task to the recent tasks list.
- */
- void add(TaskRecord task) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
+ // Whoops, couldn't do it.
+ return false;
+ }
+ final void addLocked(TaskRecord task) {
final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
|| task.mNextAffiliateTaskId != INVALID_TASK_ID
|| task.mPrevAffiliateTaskId != INVALID_TASK_ID;
- int recentsCount = mTasks.size();
+ int recentsCount = size();
// Quick case: never add voice sessions.
// TODO: VI what about if it's just an activity?
// Probably nothing to do here
@@ -761,15 +514,15 @@ class RecentTasks {
return;
}
// Another quick case: check if the top-most recent task is the same.
- if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
+ if (!isAffiliated && recentsCount > 0 && get(0) == task) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
return;
}
// Another quick case: check if this is part of a set of affiliated
// tasks that are at the top.
if (isAffiliated && recentsCount > 0 && task.inRecents
- && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
+ && task.mAffiliatedTaskId == get(0).mAffiliatedTaskId) {
+ if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + get(0)
+ " at top when adding " + task);
return;
}
@@ -779,12 +532,12 @@ class RecentTasks {
// Slightly less quick case: the task is already in recents, so all we need
// to do is move it.
if (task.inRecents) {
- int taskIndex = mTasks.indexOf(task);
+ int taskIndex = indexOf(task);
if (taskIndex >= 0) {
- if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
+ if (!isAffiliated || MOVE_AFFILIATED_TASKS_TO_FRONT) {
// Simple case: this is not an affiliated task, so we just move it to the front.
- mTasks.remove(taskIndex);
- mTasks.add(0, task);
+ remove(taskIndex);
+ add(0, task);
notifyTaskPersisterLocked(task, false);
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
+ " from " + taskIndex);
@@ -807,14 +560,20 @@ class RecentTasks {
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
- trimForAddTask(task);
+ trimForTaskLocked(task, true);
+ recentsCount = size();
+ final int maxRecents = ActivityManager.getMaxRecentTasksStatic();
+ while (recentsCount >= maxRecents) {
+ final TaskRecord tr = remove(recentsCount - 1);
+ tr.removedFromRecents();
+ recentsCount--;
+ }
task.inRecents = true;
if (!isAffiliated || needAffiliationFix) {
// If this is a simple non-affiliated task, or we had some failure trying to
// handle it as part of an affilated task, then just place it at the top.
- mTasks.add(0, task);
- notifyTaskAdded(task);
+ add(0, task);
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
} else if (isAffiliated) {
// If this is a new affiliated task, then move all of the affiliated tasks
@@ -824,7 +583,7 @@ class RecentTasks {
other = task.mPrevAffiliate;
}
if (other != null) {
- int otherIndex = mTasks.indexOf(other);
+ int otherIndex = indexOf(other);
if (otherIndex >= 0) {
// Insert new task at appropriate location.
int taskIndex;
@@ -839,8 +598,7 @@ class RecentTasks {
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"addRecent: new affiliated task added at " + taskIndex + ": " + task);
- mTasks.add(taskIndex, task);
- notifyTaskAdded(task);
+ add(taskIndex, task);
// Now move everything to the front.
if (moveAffiliatedTasksToFront(task, taskIndex)) {
@@ -867,235 +625,21 @@ class RecentTasks {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
cleanupLocked(task.userId);
}
-
- // Trim the set of tasks to the active set
- trimInactiveRecentTasks();
- }
-
- /**
- * Add the task to the bottom if possible.
- */
- boolean addToBottom(TaskRecord task) {
- if (!canAddTaskWithoutTrim(task)) {
- // Adding this task would cause the task to be removed (since it's appended at
- // the bottom and would be trimmed) so just return now
- return false;
- }
-
- add(task);
- return true;
- }
-
- /**
- * Remove a task from the recent tasks list.
- */
- void remove(TaskRecord task) {
- mTasks.remove(task);
- notifyTaskRemoved(task, !TRIMMED);
- }
-
- /**
- * Trims the recents task list to the global max number of recents.
- */
- private void trimInactiveRecentTasks() {
- int recentsCount = mTasks.size();
-
- // Remove from the end of the list until we reach the max number of recents
- while (recentsCount > mGlobalMaxNumTasks) {
- final TaskRecord tr = mTasks.remove(recentsCount - 1);
- notifyTaskRemoved(tr, TRIMMED);
- recentsCount--;
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
- + " max=" + mGlobalMaxNumTasks);
- }
-
- // Remove any tasks that belong to currently quiet profiles
- final int[] profileUserIds = mUserController.getCurrentProfileIds();
- mTmpQuietProfileUserIds.clear();
- for (int userId : profileUserIds) {
- final UserInfo userInfo = mUserController.getUserInfo(userId);
- if (userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
- mTmpQuietProfileUserIds.put(userId, true);
- }
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
- + " quiet=" + mTmpQuietProfileUserIds.get(userId));
- }
-
- // Remove any inactive tasks, calculate the latest set of visible tasks
- int numVisibleTasks = 0;
- for (int i = 0; i < mTasks.size();) {
- final TaskRecord task = mTasks.get(i);
-
- if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
- if (!mHasVisibleRecentTasks) {
- // Keep all active tasks if visible recent tasks is not supported
- i++;
- continue;
- }
-
- if (!isVisibleRecentTask(task)) {
- // Keep all active-but-invisible tasks
- i++;
- continue;
- } else {
- numVisibleTasks++;
- if (isInVisibleRange(task, numVisibleTasks)) {
- // Keep visible tasks in range
- i++;
- continue;
- } else {
- // Fall through to trim visible tasks that are no longer in range
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
- "Trimming out-of-range visible task=" + task);
- }
- }
- } else {
- // Fall through to trim inactive tasks
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
- }
-
- // Task is no longer active, trim it from the list
- mTasks.remove(task);
- notifyTaskRemoved(task, TRIMMED);
- notifyTaskPersisterLocked(task, false /* flush */);
- }
- }
-
- /**
- * @return whether the given task should be considered active.
- */
- private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
- + " globalMax=" + mGlobalMaxNumTasks);
-
- if (quietProfileUserIds.get(task.userId)) {
- // Quiet profile user's tasks are never active
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
- return false;
- }
-
- if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
- // Keep the task active if its affiliated task is also active
- final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
- if (affiliatedTask != null) {
- if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
- "\taffiliatedWithTask=" + affiliatedTask + " is not active");
- return false;
- }
- }
- }
-
- // All other tasks are considered active
- return true;
- }
-
- /**
- * @return whether the given active task should be presented to the user through SystemUI.
- */
- private boolean isVisibleRecentTask(TaskRecord task) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
- + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
- + " sessionDuration=" + mActiveTasksSessionDurationMs
- + " inactiveDuration=" + task.getInactiveDuration()
- + " activityType=" + task.getActivityType()
- + " windowingMode=" + task.getWindowingMode());
-
- // Ignore certain activity types completely
- switch (task.getActivityType()) {
- case ACTIVITY_TYPE_HOME:
- case ACTIVITY_TYPE_RECENTS:
- return false;
- }
-
- // Ignore certain windowing modes
- switch (task.getWindowingMode()) {
- case WINDOWING_MODE_PINNED:
- return false;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
- final ActivityStack stack = task.getStack();
- if (stack != null && stack.topTask() == task) {
- // Only the non-top task of the primary split screen mode is visible
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @return whether the given visible task is within the policy range.
- */
- private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
- // Keep the last most task even if it is excluded from recents
- final boolean isExcludeFromRecents =
- (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
- if (isExcludeFromRecents) {
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
- return numVisibleTasks == 1;
- }
-
- if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
- // Always keep up to the min number of recent tasks, after that fall through to the
- // checks below
- return true;
- }
-
- if (mMaxNumVisibleTasks >= 0) {
- // Always keep up to the max number of recent tasks, but return false afterwards
- return numVisibleTasks <= mMaxNumVisibleTasks;
- }
-
- if (mActiveTasksSessionDurationMs > 0) {
- // Keep the task if the inactive time is within the session window, this check must come
- // after the checks for the min/max visible task range
- if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
- return true;
- }
- }
-
- return false;
}
/**
* If needed, remove oldest existing entries in recents that are for the same kind
* of task as the given one.
*/
- private void trimForAddTask(TaskRecord task) {
- final int removeIndex = findTrimIndexForAddTask(task);
- if (removeIndex == -1) {
- // Nothing to trim
- return;
- }
-
- // There is a similar task that will be removed for the addition of {@param task}, but it
- // can be the same task, and if so, the task will be re-added in add(), so skip the
- // callbacks here.
- final TaskRecord removedTask = mTasks.remove(removeIndex);
- if (removedTask != task) {
- notifyTaskRemoved(removedTask, TRIMMED);
- if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
- + " for addition of task=" + task);
- }
- notifyTaskPersisterLocked(removedTask, false /* flush */);
- }
-
- /**
- * Find the task that would be removed if the given {@param task} is added to the recent tasks
- * list (if any).
- */
- private int findTrimIndexForAddTask(TaskRecord task) {
- int recentsCount = mTasks.size();
+ int trimForTaskLocked(TaskRecord task, boolean doTrim) {
+ int recentsCount = size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
int maxRecents = task.maxRecents - 1;
final ActivityStack stack = task.getStack();
for (int i = 0; i < recentsCount; i++) {
- final TaskRecord tr = mTasks.get(i);
+ final TaskRecord tr = get(i);
final ActivityStack trStack = tr.getStack();
-
if (task != tr) {
if (stack != null && trStack != null && stack != trStack) {
continue;
@@ -1106,7 +650,7 @@ class RecentTasks {
final Intent trIntent = tr.intent;
final boolean sameAffinity =
task.affinity != null && task.affinity.equals(tr.affinity);
- final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
+ final boolean sameIntentFilter = intent != null && intent.filterEquals(trIntent);
boolean multiTasksAllowed = false;
final int flags = intent.getFlags();
if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
@@ -1115,7 +659,7 @@ class RecentTasks {
}
final boolean trIsDocument = trIntent != null && trIntent.isDocument();
final boolean bothDocuments = document && trIsDocument;
- if (!sameAffinity && !sameIntent && !bothDocuments) {
+ if (!sameAffinity && !sameIntentFilter && !bothDocuments) {
continue;
}
@@ -1124,17 +668,17 @@ class RecentTasks {
final boolean sameActivity = task.realActivity != null
&& tr.realActivity != null
&& task.realActivity.equals(tr.realActivity);
+ // If the document is open in another app or is not the same
+ // document, we don't need to trim it.
if (!sameActivity) {
- // If the document is open in another app or is not the same document, we
- // don't need to trim it.
continue;
+ // Otherwise only trim if we are over our max recents for this task
} else if (maxRecents > 0) {
- // Otherwise only trim if we are over our max recents for this task
--maxRecents;
- if (!sameIntent || multiTasksAllowed) {
+ if (!doTrim || !sameIntentFilter || multiTasksAllowed) {
// We don't want to trim if we are not over the max allowed entries and
- // the tasks are not of the same intent filter, or multiple entries for
- // the task is allowed.
+ // the caller doesn't want us to trim, the tasks are not of the same
+ // intent filter, or multiple entries fot the task is allowed.
continue;
}
}
@@ -1145,14 +689,44 @@ class RecentTasks {
continue;
}
}
- return i;
+
+ if (!doTrim) {
+ // If the caller is not actually asking for a trim, just tell them we reached
+ // a point where the trim would happen.
+ return i;
+ }
+
+ // Either task and tr are the same or, their affinities match or their intents match
+ // and neither of them is a document, or they are documents using the same activity
+ // and their maxRecents has been reached.
+ remove(i);
+ if (task != tr) {
+ tr.removedFromRecents();
+ }
+ i--;
+ recentsCount--;
+ if (task.intent == null) {
+ // If the new recent task we are adding is not fully
+ // specified, then replace it with the existing recent task.
+ task = tr;
+ }
+ notifyTaskPersisterLocked(tr, false);
}
+
return -1;
}
+ // Sort by taskId
+ private static Comparator<TaskRecord> sTaskRecordComparator = new Comparator<TaskRecord>() {
+ @Override
+ public int compare(TaskRecord lhs, TaskRecord rhs) {
+ return rhs.taskId - lhs.taskId;
+ }
+ };
+
// Extract the affiliates of the chain containing recent at index start.
private int processNextAffiliateChainLocked(int start) {
- final TaskRecord startTask = mTasks.get(start);
+ final TaskRecord startTask = get(start);
final int affiliateId = startTask.mAffiliatedTaskId;
// Quick identification of isolated tasks. I.e. those not launched behind.
@@ -1167,17 +741,17 @@ class RecentTasks {
// Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
mTmpRecents.clear();
- for (int i = mTasks.size() - 1; i >= start; --i) {
- final TaskRecord task = mTasks.get(i);
+ for (int i = size() - 1; i >= start; --i) {
+ final TaskRecord task = get(i);
if (task.mAffiliatedTaskId == affiliateId) {
- mTasks.remove(i);
+ remove(i);
mTmpRecents.add(task);
}
}
// Sort them all by taskId. That is the order they were create in and that order will
// always be correct.
- Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
+ Collections.sort(mTmpRecents, sTaskRecordComparator);
// Go through and fix up the linked list.
// The first one is the end of the chain and has no next.
@@ -1215,197 +789,11 @@ class RecentTasks {
notifyTaskPersisterLocked(last, false);
}
- // Insert the group back into mTmpTasks at start.
- mTasks.addAll(start, mTmpRecents);
+ // Insert the group back into mRecentTasks at start.
+ addAll(start, mTmpRecents);
mTmpRecents.clear();
// Let the caller know where we left off.
return start + tmpSize;
}
-
- private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
- int recentsCount = mTasks.size();
- TaskRecord top = task;
- int topIndex = taskIndex;
- while (top.mNextAffiliate != null && topIndex > 0) {
- top = top.mNextAffiliate;
- topIndex--;
- }
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
- + topIndex + " from intial " + taskIndex);
- // Find the end of the chain, doing a sanity check along the way.
- boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
- int endIndex = topIndex;
- TaskRecord prev = top;
- while (endIndex < recentsCount) {
- TaskRecord cur = mTasks.get(endIndex);
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
- + endIndex + " " + cur);
- if (cur == top) {
- // Verify start of the chain.
- if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": first task has next affiliate: " + prev);
- sane = false;
- break;
- }
- } else {
- // Verify middle of the chain's next points back to the one before.
- if (cur.mNextAffiliate != prev
- || cur.mNextAffiliateTaskId != prev.taskId) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": middle task " + cur + " @" + endIndex
- + " has bad next affiliate "
- + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
- + ", expected " + prev);
- sane = false;
- break;
- }
- }
- if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
- // Chain ends here.
- if (cur.mPrevAffiliate != null) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": last task " + cur + " has previous affiliate "
- + cur.mPrevAffiliate);
- sane = false;
- }
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
- break;
- } else {
- // Verify middle of the chain's prev points to a valid item.
- if (cur.mPrevAffiliate == null) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": task " + cur + " has previous affiliate "
- + cur.mPrevAffiliate + " but should be id "
- + cur.mPrevAffiliate);
- sane = false;
- break;
- }
- }
- if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": task " + cur + " has affiliated id "
- + cur.mAffiliatedTaskId + " but should be "
- + task.mAffiliatedTaskId);
- sane = false;
- break;
- }
- prev = cur;
- endIndex++;
- if (endIndex >= recentsCount) {
- Slog.wtf(TAG, "Bad chain ran off index " + endIndex
- + ": last task " + prev);
- sane = false;
- break;
- }
- }
- if (sane) {
- if (endIndex < taskIndex) {
- Slog.wtf(TAG, "Bad chain @" + endIndex
- + ": did not extend to task " + task + " @" + taskIndex);
- sane = false;
- }
- }
- if (sane) {
- // All looks good, we can just move all of the affiliated tasks
- // to the top.
- for (int i=topIndex; i<=endIndex; i++) {
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
- + " from " + i + " to " + (i-topIndex));
- TaskRecord cur = mTasks.remove(i);
- mTasks.add(i - topIndex, cur);
- }
- if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks " + topIndex
- + " to " + endIndex);
- return true;
- }
-
- // Whoops, couldn't do it.
- return false;
- }
-
- void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
- pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
- if (mTasks.isEmpty()) {
- return;
- }
-
- final MutableBoolean printedAnything = new MutableBoolean(false);
- final MutableBoolean printedHeader = new MutableBoolean(false);
- final int size = mTasks.size();
- for (int i = 0; i < size; i++) {
- final TaskRecord tr = mTasks.get(i);
- if (dumpPackage != null && (tr.realActivity == null ||
- !dumpPackage.equals(tr.realActivity.getPackageName()))) {
- continue;
- }
-
- if (!printedHeader.value) {
- pw.println(" Recent tasks:");
- printedHeader.value = true;
- printedAnything.value = true;
- }
- pw.print(" * Recent #"); pw.print(i); pw.print(": ");
- pw.println(tr);
- if (dumpAll) {
- tr.dump(pw, " ");
- }
- }
-
- if (!printedAnything.value) {
- pw.println(" (nothing)");
- }
- }
-
- /**
- * Creates a new RecentTaskInfo from a TaskRecord.
- */
- static ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
- // Update the task description to reflect any changes in the task stack
- tr.updateTaskDescription();
-
- // Compose the recent task info
- ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
- rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
- rti.persistentId = tr.taskId;
- rti.baseIntent = new Intent(tr.getBaseIntent());
- rti.origActivity = tr.origActivity;
- rti.realActivity = tr.realActivity;
- rti.description = tr.lastDescription;
- rti.stackId = tr.getStackId();
- rti.userId = tr.userId;
- rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
- rti.lastActiveTime = tr.lastActiveTime;
- rti.affiliatedTaskId = tr.mAffiliatedTaskId;
- rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
- rti.numActivities = 0;
- if (tr.mBounds != null) {
- rti.bounds = new Rect(tr.mBounds);
- }
- rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
- rti.resizeMode = tr.mResizeMode;
- rti.configuration.setTo(tr.getConfiguration());
-
- ActivityRecord base = null;
- ActivityRecord top = null;
- ActivityRecord tmp;
-
- for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
- tmp = tr.mActivities.get(i);
- if (tmp.finishing) {
- continue;
- }
- base = tmp;
- if (top == null || (top.state == ActivityState.INITIALIZING)) {
- top = base;
- }
- rti.numActivities++;
- }
-
- rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
- rti.topActivity = (top != null) ? top.intent.getComponent() : null;
-
- return rti;
- }
}
diff --git a/com/android/server/am/ServiceRecord.java b/com/android/server/am/ServiceRecord.java
index 16995e50..ac85e6b1 100644
--- a/com/android/server/am/ServiceRecord.java
+++ b/com/android/server/am/ServiceRecord.java
@@ -33,7 +33,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -518,27 +517,14 @@ final class ServiceRecord extends Binder {
} catch (PackageManager.NameNotFoundException e) {
}
}
- if (nm.getNotificationChannel(localPackageName, appUid,
+ if (localForegroundNoti.getSmallIcon() == null
+ || nm.getNotificationChannel(localPackageName, appUid,
localForegroundNoti.getChannelId()) == null) {
- int targetSdkVersion = Build.VERSION_CODES.O_MR1;
- try {
- final ApplicationInfo applicationInfo =
- ams.mContext.getPackageManager().getApplicationInfoAsUser(
- appInfo.packageName, 0, userId);
- targetSdkVersion = applicationInfo.targetSdkVersion;
- } catch (PackageManager.NameNotFoundException e) {
- }
- if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
- throw new RuntimeException(
- "invalid channel for service notification: "
- + foregroundNoti);
- }
- }
- if (localForegroundNoti.getSmallIcon() == null) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
+ // Also every notification needs a channel.
throw new RuntimeException("invalid service notification: "
+ foregroundNoti);
}
diff --git a/com/android/server/am/TaskPersister.java b/com/android/server/am/TaskPersister.java
index 2689d6a4..61994b55 100644
--- a/com/android/server/am/TaskPersister.java
+++ b/com/android/server/am/TaskPersister.java
@@ -567,7 +567,7 @@ public class TaskPersister {
SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
synchronized (mService) {
for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
- SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
+ SparseBooleanArray taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId);
SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
continue;
@@ -640,7 +640,7 @@ public class TaskPersister {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ArraySet<Integer> persistentTaskIds = new ArraySet<>();
+ ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
while (true) {
// We can't lock mService while holding TaskPersister.this, but we don't want to
// call removeObsoleteFiles every time through the loop, only the last time before
@@ -654,7 +654,20 @@ public class TaskPersister {
persistentTaskIds.clear();
synchronized (mService) {
if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
- mRecentTasks.getPersistableTaskIds(persistentTaskIds);
+ for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mRecentTasks.get(taskNdx);
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
+ " persistable=" + task.isPersistable);
+ final ActivityStack stack = task.getStack();
+ if ((task.isPersistable || task.inRecents)
+ && (stack == null || !stack.isHomeOrRecentsStack())) {
+ if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+ persistentTaskIds.add(task.taskId);
+ } else {
+ if (DEBUG) Slog.d(TAG,
+ "omitting from persistentTaskIds task=" + task);
+ }
+ }
mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
mRecentTasks.usersWithRecentsLoadedLocked());
}
diff --git a/com/android/server/am/TaskRecord.java b/com/android/server/am/TaskRecord.java
index a1b45a1e..0d8df796 100644
--- a/com/android/server/am/TaskRecord.java
+++ b/com/android/server/am/TaskRecord.java
@@ -18,7 +18,10 @@ package com.android.server.am;
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -28,7 +31,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -43,6 +45,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -83,6 +86,7 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
@@ -98,7 +102,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
@@ -152,6 +155,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private static final String ATTR_EFFECTIVE_UID = "effective_uid";
@Deprecated
private static final String ATTR_TASKTYPE = "task_type";
+ private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
+ private static final String ATTR_LASTACTIVETIME = "last_active_time";
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
@@ -163,6 +168,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private static final String ATTR_CALLING_PACKAGE = "calling_package";
private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
private static final String ATTR_RESIZE_MODE = "resize_mode";
+ private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
private static final String ATTR_MIN_WIDTH = "min_width";
private static final String ATTR_MIN_HEIGHT = "min_height";
@@ -206,10 +212,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
ComponentName realActivity; // The actual activity component that started the task.
boolean realActivitySuspended; // True if the actual activity component that started the
// task is suspended.
+ long firstActiveTime; // First time this task was active.
+ long lastActiveTime; // Last time this task was active, including sleep.
boolean inRecents; // Actually in the recents list?
- long lastActiveTime; // Last time this task was active in the current device session,
- // including sleep. This time is initialized to the elapsed time when
- // restored from disk.
boolean isAvailable; // Is the activity available to be launched?
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
@@ -232,6 +237,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// of the root activity.
boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
// changes on a temporary basis.
+ private int mLockTaskMode; // Which tasklock mode to launch this task in. One of
+ // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
+ private boolean mPrivileged; // The root activity application of this task holds
+ // privileged permissions.
/** Can't be put in lockTask mode. */
final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
@@ -330,7 +339,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
TaskPersister.IMAGE_EXTENSION;
userId = UserHandle.getUserId(info.applicationInfo.uid);
taskId = _taskId;
- lastActiveTime = SystemClock.elapsedRealtime();
mAffiliatedTaskId = _taskId;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
@@ -351,7 +359,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
TaskPersister.IMAGE_EXTENSION;
userId = UserHandle.getUserId(info.applicationInfo.uid);
taskId = _taskId;
- lastActiveTime = SystemClock.elapsedRealtime();
mAffiliatedTaskId = _taskId;
voiceSession = null;
voiceInteractor = null;
@@ -378,11 +385,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
- long lastTimeMoved, boolean neverRelinquishIdentity,
- TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
- int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
- int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight) {
+ long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
+ boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
+ int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
+ int callingUid, String callingPackage, int resizeMode, boolean supportsPictureInPicture,
+ boolean privileged, boolean _realActivitySuspended, boolean userSetupComplete,
+ int minWidth, int minHeight) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -404,7 +412,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
userId = _userId;
mUserSetupComplete = userSetupComplete;
effectiveUid = _effectiveUid;
- lastActiveTime = SystemClock.elapsedRealtime();
+ firstActiveTime = _firstActiveTime;
+ lastActiveTime = _lastActiveTime;
lastDescription = _lastDescription;
mActivities = activities;
mLastTimeMoved = lastTimeMoved;
@@ -418,6 +427,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mCallingPackage = callingPackage;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
+ mPrivileged = privileged;
mMinWidth = minWidth;
mMinHeight = minHeight;
mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
@@ -510,7 +520,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
updateOverrideConfiguration(bounds);
- if (!inFreeformWindowingMode()) {
+ if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
// re-restore the task so it can have the proper stack association.
mService.mStackSupervisor.restoreRecentTaskLocked(this, null);
}
@@ -606,7 +616,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* @return whether the task was reparented
*/
// TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
- // re-parenting the task. Can only be done when we are no longer using static stack Ids.
+ // re-parenting the task. Can only be done when we are no longer using static stack Ids like
+ /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */
boolean reparent(ActivityStack preferredStack, int position,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
@@ -619,12 +630,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return false;
}
- final int toStackWindowingMode = toStack.getWindowingMode();
+ final int sourceStackId = getStackId();
+ final int stackId = toStack.getStackId();
final ActivityRecord topActivity = getTopActivity();
- final boolean mightReplaceWindow =
- replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode)
- && topActivity != null;
+ final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
+ && topActivity != null;
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
@@ -649,7 +660,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// In some cases the focused stack isn't the front stack. E.g. pinned stack.
// Whenever we are moving the top activity from the front stack we want to make sure to
// move the stack to the front.
- final boolean wasFront = r != null && sourceStack.isTopStackOnDisplay()
+ final boolean wasFront = r != null && supervisor.isFrontStackOnDisplay(sourceStack)
&& (sourceStack.topRunningActivityLocked() == r);
// Adjust the position for the new parent stack as needed.
@@ -696,10 +707,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
toStack.prepareFreezingTaskBounds();
// Make sure the task has the appropriate bounds/size for the stack it is in.
+ final int toStackWindowingMode = toStack.getWindowingMode();
final boolean toStackSplitScreenPrimary =
toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
- || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+ if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
&& !Objects.equals(mBounds, toStack.mBounds)) {
kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
deferResume);
@@ -738,9 +749,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
// TODO: Handle incorrect request to move before the actual move, not after.
- final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenPrimaryStack();
+ final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
- DEFAULT_DISPLAY, toStack);
+ DEFAULT_DISPLAY, stackId);
boolean successful = (preferredStack == toStack);
if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -750,18 +761,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return successful;
}
- /**
- * Returns true if the windows of tasks being moved to the target stack from the source
- * stack should be replaced, meaning that window manager will keep the old window around
- * until the new is ready.
- * @hide
- */
- private static boolean replaceWindowsOnTaskMove(
- int sourceWindowingMode, int targetWindowingMode) {
- return sourceWindowingMode == WINDOWING_MODE_FREEFORM
- || targetWindowingMode == WINDOWING_MODE_FREEFORM;
- }
-
void cancelWindowTransition() {
mWindowContainerController.cancelWindowTransition();
}
@@ -781,11 +780,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
void touchActiveTime() {
- lastActiveTime = SystemClock.elapsedRealtime();
+ lastActiveTime = System.currentTimeMillis();
+ if (firstActiveTime == 0) {
+ firstActiveTime = lastActiveTime;
+ }
}
long getInactiveDuration() {
- return SystemClock.elapsedRealtime() - lastActiveTime;
+ return System.currentTimeMillis() - lastActiveTime;
}
/** Sets the original intent, and the calling uid and package. */
@@ -793,7 +795,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
setIntent(r.intent, r.info);
- setLockTaskAuth(r);
}
/** Sets the original intent, _without_ updating the calling uid or package. */
@@ -877,6 +878,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
mResizeMode = info.resizeMode;
mSupportsPictureInPicture = info.supportsPictureInPicture();
+ mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
+ mLockTaskMode = info.lockTaskLaunchMode;
+ if (!mPrivileged && (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+ || mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+ // Non-priv apps are not allowed to use always or never, fall back to default
+ mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ }
+ setLockTaskAuth();
}
/** Sets the original minimal width and height. */
@@ -1254,7 +1263,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mService.notifyTaskPersisterLocked(this, false);
}
- if (inPinnedWindowingMode()) {
+ if (getStackId() == PINNED_STACK_ID) {
// We normally notify listeners of task stack changes on pause, however pinned stack
// activities are normally in the paused state so no notification will be sent there
// before the activity is removed. We send it here so instead.
@@ -1413,17 +1422,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
void setLockTaskAuth() {
- setLockTaskAuth(getRootActivity());
- }
-
- private void setLockTaskAuth(@Nullable ActivityRecord r) {
- if (r == null) {
- mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
- return;
- }
-
final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
- switch (r.lockTaskLaunchMode) {
+ switch (mLockTaskMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
@@ -1493,7 +1493,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* @return True if the requested bounds are okay for a resizing request.
*/
private boolean canResizeToBounds(Rect bounds) {
- if (bounds == null || !inFreeformWindowingMode()) {
+ if (bounds == null || getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
// Note: If not on the freeform workspace, we ignore the bounds.
return true;
}
@@ -1559,7 +1559,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// values in the TaskRecord.
String label = null;
String iconFilename = null;
- int iconResource = -1;
int colorPrimary = 0;
int colorBackground = 0;
int statusBarColor = 0;
@@ -1571,9 +1570,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
if (label == null) {
label = r.taskDescription.getLabel();
}
- if (iconResource == -1) {
- iconResource = r.taskDescription.getIconResource();
- }
if (iconFilename == null) {
iconFilename = r.taskDescription.getIconFilename();
}
@@ -1588,8 +1584,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
topActivity = false;
}
- lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
- colorPrimary, colorBackground, statusBarColor, navigationBarColor);
+ lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
+ colorBackground, statusBarColor, navigationBarColor);
if (mWindowContainerController != null) {
mWindowContainerController.setTaskDescription(lastTaskDescription);
}
@@ -1651,6 +1647,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
out.attribute(null, ATTR_USERID, String.valueOf(userId));
out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
+ out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
+ out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
if (lastDescription != null) {
@@ -1668,6 +1666,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
String.valueOf(mSupportsPictureInPicture));
+ out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
if (mLastNonFullscreenBounds != null) {
out.attribute(
null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
@@ -1722,6 +1721,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
boolean userSetupComplete = true;
int effectiveUid = -1;
String lastDescription = null;
+ long firstActiveTime = -1;
+ long lastActiveTime = -1;
long lastTimeOnTop = 0;
boolean neverRelinquishIdentity = true;
int taskId = INVALID_TASK_ID;
@@ -1735,6 +1736,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
String callingPackage = "";
int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean supportsPictureInPicture = false;
+ boolean privileged = false;
Rect bounds = null;
int minWidth = INVALID_MIN_SIZE;
int minHeight = INVALID_MIN_SIZE;
@@ -1772,6 +1774,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
effectiveUid = Integer.parseInt(attrValue);
} else if (ATTR_TASKTYPE.equals(attrName)) {
taskType = Integer.parseInt(attrValue);
+ } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
+ firstActiveTime = Long.parseLong(attrValue);
+ } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
+ lastActiveTime = Long.parseLong(attrValue);
} else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
lastDescription = attrValue;
} else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
@@ -1796,6 +1802,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
resizeMode = Integer.parseInt(attrValue);
} else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
supportsPictureInPicture = Boolean.parseBoolean(attrValue);
+ } else if (ATTR_PRIVILEGED.equals(attrName)) {
+ privileged = Boolean.parseBoolean(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
bounds = Rect.unflattenFromString(attrValue);
} else if (ATTR_MIN_WIDTH.equals(attrName)) {
@@ -1880,10 +1888,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
- activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
- taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
- callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
- userSetupComplete, minWidth, minHeight);
+ activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
+ taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
+ callingUid, callingPackage, resizeMode, supportsPictureInPicture, privileged,
+ realActivitySuspended, userSetupComplete, minWidth, minHeight);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1903,7 +1911,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// If the task has no requested minimal size, we'd like to enforce a minimal size
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
+ if (getStackId() != PINNED_STACK_ID) {
if (minWidth == INVALID_MIN_SIZE) {
minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
}
@@ -2077,7 +2085,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return;
}
- if (inStack.inFreeformWindowingMode()) {
+ if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
if (!isResizeable()) {
throw new IllegalArgumentException("Can not position non-resizeable task="
+ this + " in stack=" + inStack);
@@ -2212,6 +2220,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
pw.print(" isResizeable=" + isResizeable());
+ pw.print(" firstActiveTime=" + firstActiveTime);
pw.print(" lastActiveTime=" + lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
}
diff --git a/com/android/server/appwidget/AppWidgetServiceImpl.java b/com/android/server/appwidget/AppWidgetServiceImpl.java
index 51afada2..a6aaaa67 100644
--- a/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -71,6 +71,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.service.appwidget.AppWidgetServiceDumpProto;
import android.service.appwidget.WidgetProto;
import android.text.TextUtils;
@@ -158,9 +159,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Bump if the stored widgets need to be upgraded.
private static final int CURRENT_VERSION = 1;
- // Every widget update request is associated which an increasing sequence number. This is
- // used to verify which request has successfully been received by the host.
- private static final AtomicLong UPDATE_COUNTER = new AtomicLong();
+ private static final AtomicLong REQUEST_COUNTER = new AtomicLong();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -815,9 +814,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Host host = lookupOrAddHostLocked(id);
host.callbacks = callbacks;
- long updateSequenceNo = UPDATE_COUNTER.incrementAndGet();
int N = appWidgetIds.length;
ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N);
+
LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
for (int i = 0; i < N; i++) {
if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
@@ -829,8 +828,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
}
- // Reset the update counter once all the updates have been calculated
- host.lastWidgetUpdateSequenceNo = updateSequenceNo;
return new ParceledListSlice<>(outUpdates);
}
}
@@ -1917,9 +1914,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// method with a wrong id. In that case, ignore the call.
return;
}
- long requestId = UPDATE_COUNTER.incrementAndGet();
+ long requestId = REQUEST_COUNTER.incrementAndGet();
if (widget != null) {
- widget.updateSequenceNos.put(viewId, requestId);
+ widget.updateRequestIds.put(viewId, requestId);
}
if (widget == null || widget.host == null || widget.host.zombie
|| widget.host.callbacks == null || widget.provider == null
@@ -1944,7 +1941,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
int appWidgetId, int viewId, long requestId) {
try {
callbacks.viewDataChanged(appWidgetId, viewId);
- host.lastWidgetUpdateSequenceNo = requestId;
+ host.lastWidgetUpdateRequestId = requestId;
} catch (RemoteException re) {
// It failed; remove the callback. No need to prune because
// we know that this host is still referenced by this instance.
@@ -1991,9 +1988,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
- long requestId = UPDATE_COUNTER.incrementAndGet();
+ long requestId = REQUEST_COUNTER.incrementAndGet();
if (widget != null) {
- widget.updateSequenceNos.put(ID_VIEWS_UPDATE, requestId);
+ widget.updateRequestIds.put(ID_VIEWS_UPDATE, requestId);
}
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
@@ -2016,7 +2013,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
int appWidgetId, RemoteViews views, long requestId) {
try {
callbacks.updateAppWidget(appWidgetId, views);
- host.lastWidgetUpdateSequenceNo = requestId;
+ host.lastWidgetUpdateRequestId = requestId;
} catch (RemoteException re) {
synchronized (mLock) {
Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -2026,11 +2023,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
private void scheduleNotifyProviderChangedLocked(Widget widget) {
- long requestId = UPDATE_COUNTER.incrementAndGet();
+ long requestId = REQUEST_COUNTER.incrementAndGet();
if (widget != null) {
// When the provider changes, reset everything else.
- widget.updateSequenceNos.clear();
- widget.updateSequenceNos.append(ID_PROVIDER_CHANGED, requestId);
+ widget.updateRequestIds.clear();
+ widget.updateRequestIds.append(ID_PROVIDER_CHANGED, requestId);
}
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
@@ -2053,7 +2050,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
int appWidgetId, AppWidgetProviderInfo info, long requestId) {
try {
callbacks.providerChanged(appWidgetId, info);
- host.lastWidgetUpdateSequenceNo = requestId;
+ host.lastWidgetUpdateRequestId = requestId;
} catch (RemoteException re) {
synchronized (mLock){
Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3890,11 +3887,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
int tag = TAG_UNDEFINED; // for use while saving state (the index)
- // Sequence no for the last update successfully sent. This is updated whenever a
- // widget update is successfully sent to the host callbacks. As all new/undelivered updates
- // will have sequenceNo greater than this, all those updates will be sent when the host
- // callbacks are attached again.
- long lastWidgetUpdateSequenceNo;
+ long lastWidgetUpdateRequestId; // request id for the last update successfully sent
public int getUserId() {
return UserHandle.getUserId(id.uid);
@@ -3921,18 +3914,18 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
*/
public boolean getPendingUpdatesForId(int appWidgetId,
LongSparseArray<PendingHostUpdate> outUpdates) {
- long updateSequenceNo = lastWidgetUpdateSequenceNo;
+ long updateRequestId = lastWidgetUpdateRequestId;
int N = widgets.size();
for (int i = 0; i < N; i++) {
Widget widget = widgets.get(i);
if (widget.appWidgetId == appWidgetId) {
outUpdates.clear();
- for (int j = widget.updateSequenceNos.size() - 1; j >= 0; j--) {
- long requestId = widget.updateSequenceNos.valueAt(j);
- if (requestId <= updateSequenceNo) {
+ for (int j = widget.updateRequestIds.size() - 1; j >= 0; j--) {
+ long requestId = widget.updateRequestIds.valueAt(j);
+ if (requestId <= updateRequestId) {
continue;
}
- int id = widget.updateSequenceNos.keyAt(j);
+ int id = widget.updateRequestIds.keyAt(j);
final PendingHostUpdate update;
switch (id) {
case ID_PROVIDER_CHANGED:
@@ -4028,8 +4021,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
RemoteViews maskedViews;
Bundle options;
Host host;
- // Map of request type to updateSequenceNo.
- SparseLongArray updateSequenceNos = new SparseLongArray(2);
+ // Request ids for various operations
+ SparseLongArray updateRequestIds = new SparseLongArray(2);
@Override
public String toString() {
diff --git a/com/android/server/audio/PlaybackActivityMonitor.java b/com/android/server/audio/PlaybackActivityMonitor.java
index 49431733..6506cf7f 100644
--- a/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/com/android/server/audio/PlaybackActivityMonitor.java
@@ -184,15 +184,11 @@ public final class PlaybackActivityMonitor
}
}
- private static final int FLAGS_FOR_SILENCE_OVERRIDE =
- AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
- AudioAttributes.FLAG_BYPASS_MUTE;
-
private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
- == FLAGS_FOR_SILENCE_OVERRIDE &&
+ if ((apc.getAudioAttributes().getAllFlags() &
+ AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 &&
apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
apc.getClientPid(), apc.getClientUid()) ==
diff --git a/com/android/server/autofill/AutofillManagerServiceImpl.java b/com/android/server/autofill/AutofillManagerServiceImpl.java
index 880f236c..862070ad 100644
--- a/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -216,12 +216,9 @@ final class AutofillManagerServiceImpl {
serviceComponent = ComponentName.unflattenFromString(componentName);
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
- if (serviceInfo == null) {
- Slog.e(TAG, "Bad AutofillService name: " + componentName);
- }
} catch (RuntimeException | RemoteException e) {
- Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
- serviceInfo = null;
+ Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
+ return;
}
}
try {
@@ -231,24 +228,21 @@ final class AutofillManagerServiceImpl {
if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
} else {
mInfo = null;
- if (sDebug) {
- Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
- }
+ if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
}
- } catch (Exception e) {
- Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
- mInfo = null;
- }
- final boolean isEnabled = isEnabled();
- if (wasEnabled != isEnabled) {
- if (!isEnabled) {
- final int sessionCount = mSessions.size();
- for (int i = sessionCount - 1; i >= 0; i--) {
- final Session session = mSessions.valueAt(i);
- session.removeSelfLocked();
+ final boolean isEnabled = isEnabled();
+ if (wasEnabled != isEnabled) {
+ if (!isEnabled) {
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ final Session session = mSessions.valueAt(i);
+ session.removeSelfLocked();
+ }
}
+ sendStateToClients(false);
}
- sendStateToClients(false);
+ } catch (Exception e) {
+ Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
}
}
diff --git a/com/android/server/autofill/Session.java b/com/android/server/autofill/Session.java
index 3c12d670..ed00ffed 100644
--- a/com/android/server/autofill/Session.java
+++ b/com/android/server/autofill/Session.java
@@ -495,7 +495,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
notifyUnavailableToClient(false);
}
synchronized (mLock) {
- processResponseLocked(response, null, requestFlags);
+ processResponseLocked(response, requestFlags);
}
final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
@@ -762,21 +762,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
- final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
- if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
- + ", clientState=" + newClientState);
- }
+ if (sDebug) Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result);
if (result instanceof FillResponse) {
writeLog(MetricsEvent.AUTOFILL_AUTHENTICATED);
- replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
+ replaceResponseLocked(authenticatedResponse, (FillResponse) result);
} else if (result instanceof Dataset) {
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
writeLog(MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
- if (newClientState != null) {
- if (sDebug) Slog.d(TAG, "Updating client state from auth dataset");
- mClientState = newClientState;
- }
final Dataset dataset = (Dataset) result;
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
autoFill(requestId, datasetIdx, dataset, false);
@@ -1499,14 +1491,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ArraySet<AutofillId> trackedViews = null;
boolean saveOnAllViewsInvisible = false;
- boolean saveOnFinish = true;
final SaveInfo saveInfo = response.getSaveInfo();
- final AutofillId saveTriggerId;
if (saveInfo != null) {
- saveTriggerId = saveInfo.getTriggerId();
- if (saveTriggerId != null) {
- writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
- }
saveOnAllViewsInvisible =
(saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
@@ -1523,12 +1509,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Collections.addAll(trackedViews, saveInfo.getOptionalIds());
}
}
- if ((saveInfo.getFlags() & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) {
- saveOnFinish = false;
- }
-
- } else {
- saveTriggerId = null;
}
// Must also track that are part of datasets, otherwise the FillUI won't be hidden when
@@ -1553,18 +1533,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
if (sVerbose) {
- Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
- + " (triggering on " + saveTriggerId + ")");
+ Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds);
}
mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
- saveOnFinish, toArray(fillableIds), saveTriggerId);
+ toArray(fillableIds));
} catch (RemoteException e) {
Slog.w(TAG, "Cannot set tracked ids", e);
}
}
private void replaceResponseLocked(@NonNull FillResponse oldResponse,
- @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
+ @NonNull FillResponse newResponse) {
// Disassociate view states with the old response
setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, true);
// Move over the id
@@ -1572,7 +1551,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Replace the old response
mResponses.put(newResponse.getRequestId(), newResponse);
// Now process the new response
- processResponseLocked(newResponse, newClientState, 0);
+ processResponseLocked(newResponse, 0);
}
private void processNullResponseLocked(int flags) {
@@ -1586,8 +1565,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
removeSelf();
}
- private void processResponseLocked(@NonNull FillResponse newResponse,
- @Nullable Bundle newClientState, int flags) {
+ private void processResponseLocked(@NonNull FillResponse newResponse, int flags) {
// Make sure we are hiding the UI which will be shown
// only if handling the current response requires it.
mUi.hideAll(this);
@@ -1595,15 +1573,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int requestId = newResponse.getRequestId();
if (sVerbose) {
Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
- + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse
- + ",newClientState=" + newClientState);
+ + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse);
}
if (mResponses == null) {
mResponses = new SparseArray<>(4);
}
mResponses.put(requestId, newResponse);
- mClientState = newClientState != null ? newClientState : newResponse.getClientState();
+ mClientState = newResponse.getClientState();
setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
updateTrackedIdsLocked();
diff --git a/com/android/server/backup/BackupManagerService.java b/com/android/server/backup/BackupManagerService.java
index f9213aab..eabe21fe 100644
--- a/com/android/server/backup/BackupManagerService.java
+++ b/com/android/server/backup/BackupManagerService.java
@@ -319,6 +319,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
boolean mProvisioned;
boolean mAutoRestore;
PowerManager.WakeLock mWakelock;
+ HandlerThread mHandlerThread;
BackupHandler mBackupHandler;
PendingIntent mRunBackupIntent, mRunInitIntent;
BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
@@ -408,37 +409,43 @@ public class BackupManagerService implements BackupManagerServiceInterface {
// Called through the trampoline from onUnlockUser(), then we buck the work
// off to the background thread to keep the unlock time down.
public void unlockSystemUser() {
- // Migrate legacy setting
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
- if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
- if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
- }
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
- if (enableState >= 0) {
+ mBackupHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+ sInstance.initialize(UserHandle.USER_SYSTEM);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ // Migrate legacy setting
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+ if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ Slog.i(TAG, "Backup enable apparently not migrated");
}
- writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ final int enableState = Settings.Secure.getIntForUser(r,
+ Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+ if (enableState >= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ }
+ writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ }
}
}
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ try {
+ sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+ } catch (RemoteException e) {
+ // can't happen; it's a local object
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ });
}
class ProvisionedObserver extends ContentObserver {
@@ -1213,7 +1220,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
// ----- Main service implementation -----
- public BackupManagerService(Context context, Trampoline parent, HandlerThread backupThread) {
+ public BackupManagerService(Context context, Trampoline parent) {
mContext = context;
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -1226,7 +1233,9 @@ public class BackupManagerService implements BackupManagerServiceInterface {
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
// spin up the backup/restore handler thread
- mBackupHandler = new BackupHandler(backupThread.getLooper());
+ mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
+ mHandlerThread.start();
+ mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
@@ -1351,7 +1360,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
- mTransportBoundListener, backupThread.getLooper());
+ mTransportBoundListener, mHandlerThread.getLooper());
mTransportManager.registerAllTransports();
// Now that we know about valid backup participants, parse any
diff --git a/com/android/server/backup/RefactoredBackupManagerService.java b/com/android/server/backup/RefactoredBackupManagerService.java
index 20f23690..f2980659 100644
--- a/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/com/android/server/backup/RefactoredBackupManagerService.java
@@ -237,6 +237,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
private boolean mProvisioned;
private boolean mAutoRestore;
private PowerManager.WakeLock mWakelock;
+ private HandlerThread mHandlerThread;
private BackupHandler mBackupHandler;
private PendingIntent mRunBackupIntent;
private PendingIntent mRunInitIntent;
@@ -555,37 +556,43 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// Called through the trampoline from onUnlockUser(), then we buck the work
// off to the background thread to keep the unlock time down.
public void unlockSystemUser() {
- // Migrate legacy setting
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
- if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
- if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
- }
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
- if (enableState >= 0) {
+ mBackupHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+ sInstance.initialize(UserHandle.USER_SYSTEM);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ // Migrate legacy setting
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+ if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ Slog.i(TAG, "Backup enable apparently not migrated");
}
- writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ final int enableState = Settings.Secure.getIntForUser(r,
+ Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+ if (enableState >= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ }
+ writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ }
}
}
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ try {
+ sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+ } catch (RemoteException e) {
+ // can't happen; it's a local object
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ });
}
// Bookkeeping of in-flight operations for timeout etc. purposes. The operation
@@ -722,8 +729,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// ----- Main service implementation -----
- public RefactoredBackupManagerService(Context context, Trampoline parent,
- HandlerThread backupThread) {
+ public RefactoredBackupManagerService(Context context, Trampoline parent) {
mContext = context;
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -736,7 +742,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
// spin up the backup/restore handler thread
- mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+ mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
+ mHandlerThread.start();
+ mBackupHandler = new BackupHandler(this, mHandlerThread.getLooper());
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
@@ -816,7 +824,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
- mTransportBoundListener, backupThread.getLooper());
+ mTransportBoundListener, mHandlerThread.getLooper());
mTransportManager.registerAllTransports();
// Now that we know about valid backup participants, parse any
diff --git a/com/android/server/backup/Trampoline.java b/com/android/server/backup/Trampoline.java
index 9847edf8..9739e380 100644
--- a/com/android/server/backup/Trampoline.java
+++ b/com/android/server/backup/Trampoline.java
@@ -28,15 +28,11 @@ import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -79,8 +75,6 @@ public class Trampoline extends IBackupManager.Stub {
final boolean mGlobalDisable;
volatile BackupManagerServiceInterface mService;
- private HandlerThread mHandlerThread;
-
public Trampoline(Context context) {
mContext = context;
mGlobalDisable = isBackupDisabled();
@@ -117,11 +111,11 @@ public class Trampoline extends IBackupManager.Stub {
}
protected BackupManagerServiceInterface createRefactoredBackupManagerService() {
- return new RefactoredBackupManagerService(mContext, this, mHandlerThread);
+ return new RefactoredBackupManagerService(mContext, this);
}
protected BackupManagerServiceInterface createBackupManagerService() {
- return new BackupManagerService(mContext, this, mHandlerThread);
+ return new BackupManagerService(mContext, this);
}
// internal control API
@@ -146,21 +140,10 @@ public class Trampoline extends IBackupManager.Stub {
}
void unlockSystemUser() {
- mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
- mHandlerThread.start();
-
- Handler h = new Handler(mHandlerThread.getLooper());
- h.post(() -> {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
- initialize(UserHandle.USER_SYSTEM);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- BackupManagerServiceInterface svc = mService;
- Slog.i(TAG, "Unlocking system user; mService=" + mService);
- if (svc != null) {
- svc.unlockSystemUser();
- }
- });
+ BackupManagerServiceInterface svc = mService;
+ if (svc != null) {
+ svc.unlockSystemUser();
+ }
}
public void setBackupServiceActive(final int userHandle, boolean makeActive) {
diff --git a/com/android/server/clipboard/ClipboardService.java b/com/android/server/clipboard/ClipboardService.java
index 0c9d70a9..e6228d46 100644
--- a/com/android/server/clipboard/ClipboardService.java
+++ b/com/android/server/clipboard/ClipboardService.java
@@ -435,12 +435,11 @@ public class ClipboardService extends SystemService {
}
private boolean isDeviceLocked() {
- int callingUserId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final KeyguardManager keyguardManager = getContext().getSystemService(
KeyguardManager.class);
- return keyguardManager != null && keyguardManager.isDeviceLocked(callingUserId);
+ return keyguardManager != null && keyguardManager.isDeviceLocked();
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/com/android/server/connectivity/Tethering.java b/com/android/server/connectivity/Tethering.java
index d7cd81ff..5583e86c 100644
--- a/com/android/server/connectivity/Tethering.java
+++ b/com/android/server/connectivity/Tethering.java
@@ -1371,7 +1371,6 @@ public class Tethering extends BaseNetworkObserver {
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
}
- mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null);
setUpstreamNetwork(ns);
}
diff --git a/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index b35ed751..c5f75280 100644
--- a/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -95,10 +95,7 @@ public class UpstreamNetworkMonitor {
private NetworkCallback mDefaultNetworkCallback;
private NetworkCallback mMobileNetworkCallback;
private boolean mDunRequired;
- // The current system default network (not really used yet).
- private Network mDefaultInternetNetwork;
- // The current upstream network used for tethering.
- private Network mTetheringUpstreamNetwork;
+ private Network mCurrentDefault;
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
mContext = ctx;
@@ -133,12 +130,10 @@ public class UpstreamNetworkMonitor {
releaseCallback(mDefaultNetworkCallback);
mDefaultNetworkCallback = null;
- mDefaultInternetNetwork = null;
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
- mTetheringUpstreamNetwork = null;
mNetworkMap.clear();
}
@@ -212,7 +207,7 @@ public class UpstreamNetworkMonitor {
break;
default:
/* If we've found an active upstream connection that's not DUN/HIPRI
- * we should stop any outstanding DUN/HIPRI requests.
+ * we should stop any outstanding DUN/HIPRI start requests.
*
* If we found NONE we don't want to do this as we want any previous
* requests to keep trying to bring up something we can use.
@@ -224,10 +219,6 @@ public class UpstreamNetworkMonitor {
return typeStatePair.ns;
}
- public void setCurrentUpstream(Network upstream) {
- mTetheringUpstreamNetwork = upstream;
- }
-
public Set<IpPrefix> getLocalPrefixes() {
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
@@ -259,7 +250,7 @@ public class UpstreamNetworkMonitor {
// These request*() calls can be deleted post oag/339444.
return;
}
- mDefaultInternetNetwork = network;
+ mCurrentDefault = network;
break;
case CALLBACK_MOBILE_REQUEST:
@@ -311,13 +302,6 @@ public class UpstreamNetworkMonitor {
network, newNc));
}
- // Log changes in upstream network signal strength, if available.
- if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
- final int newSignal = newNc.getSignalStrength();
- final String prevSignal = getSignalStrength(prev.networkCapabilities);
- mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
- }
-
mNetworkMap.put(network, new NetworkState(
null, prev.linkProperties, newNc, network, null, null));
// TODO: If sufficient information is available to select a more
@@ -346,21 +330,9 @@ public class UpstreamNetworkMonitor {
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
}
- private void handleSuspended(int callbackType, Network network) {
- if (callbackType != CALLBACK_LISTEN_ALL) return;
- if (!network.equals(mTetheringUpstreamNetwork)) return;
- mLog.log("SUSPENDED current upstream: " + network);
- }
-
- private void handleResumed(int callbackType, Network network) {
- if (callbackType != CALLBACK_LISTEN_ALL) return;
- if (!network.equals(mTetheringUpstreamNetwork)) return;
- mLog.log("RESUMED current upstream: " + network);
- }
-
private void handleLost(int callbackType, Network network) {
if (callbackType == CALLBACK_TRACK_DEFAULT) {
- mDefaultInternetNetwork = null;
+ mCurrentDefault = null;
// Receiving onLost() for a default network does not necessarily
// mean the network is gone. We wait for a separate notification
// on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
@@ -429,15 +401,8 @@ public class UpstreamNetworkMonitor {
recomputeLocalPrefixes();
}
- @Override
- public void onNetworkSuspended(Network network) {
- handleSuspended(mCallbackType, network);
- }
-
- @Override
- public void onNetworkResumed(Network network) {
- handleResumed(mCallbackType, network);
- }
+ // TODO: Handle onNetworkSuspended();
+ // TODO: Handle onNetworkResumed();
@Override
public void onLost(Network network) {
@@ -502,9 +467,4 @@ public class UpstreamNetworkMonitor {
return prefixSet;
}
-
- private static String getSignalStrength(NetworkCapabilities nc) {
- if (nc == null || !nc.hasSignalStrength()) return "unknown";
- return Integer.toString(nc.getSignalStrength());
- }
}
diff --git a/com/android/server/display/AutomaticBrightnessController.java b/com/android/server/display/AutomaticBrightnessController.java
index 9a6e6094..9aabdab7 100644
--- a/com/android/server/display/AutomaticBrightnessController.java
+++ b/com/android/server/display/AutomaticBrightnessController.java
@@ -29,7 +29,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.os.Trace;
import android.text.format.DateUtils;
import android.util.EventLog;
import android.util.MathUtils;
@@ -305,7 +304,6 @@ class AutomaticBrightnessController {
}
private void handleLightSensorEvent(long time, float lux) {
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
if (mAmbientLightRingBuffer.size() == 0) {
diff --git a/com/android/server/display/DisplayPowerController.java b/com/android/server/display/DisplayPowerController.java
index f930b523..f8e58362 100644
--- a/com/android/server/display/DisplayPowerController.java
+++ b/com/android/server/display/DisplayPowerController.java
@@ -683,8 +683,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Configure auto-brightness.
boolean autoBrightnessEnabled = false;
if (mAutomaticBrightnessController != null) {
- final boolean autoBrightnessEnabledInDoze =
- mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
+ final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
+ && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
autoBrightnessEnabled = mPowerRequest.useAutoBrightness
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightness < 0;
@@ -726,7 +726,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
// Use default brightness when dozing unless overridden.
- if (brightness < 0 && Display.isDozeState(state)) {
+ if (brightness < 0 && (state == Display.STATE_DOZE
+ || state == Display.STATE_DOZE_SUSPEND)) {
brightness = mScreenBrightnessDozeConfig;
}
@@ -776,6 +777,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Skip the animation when the screen is off or suspended or transition to/from VR.
if (!mPendingScreenOff) {
if (mSkipScreenOnBrightnessRamp) {
+
if (state == Display.STATE_ON) {
if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
mInitialAutoBrightness = brightness;
@@ -792,25 +794,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
- final boolean wasOrWillBeInVr =
- (state == Display.STATE_VR || oldState == Display.STATE_VR);
- final boolean initialRampSkip =
- state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE;
- // While dozing, sometimes the brightness is split into buckets. Rather than animating
- // through the buckets, which is unlikely to be smooth in the first place, just jump
- // right to the suggested brightness.
- final boolean hasBrightnessBuckets =
- Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
- // If the color fade is totally covering the screen then we can change the backlight
- // level without it being a noticeable jump since any actual content isn't yet visible.
- final boolean isDisplayContentVisible =
- mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
- if (initialRampSkip || hasBrightnessBuckets
- || wasOrWillBeInVr || !isDisplayContentVisible) {
- animateScreenBrightness(brightness, 0);
- } else {
+ boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR);
+ if ((state == Display.STATE_ON
+ && mSkipRampState == RAMP_STATE_SKIP_NONE
+ || state == Display.STATE_DOZE && !mBrightnessBucketsInDozeConfig)
+ && !wasOrWillBeInVr) {
animateScreenBrightness(brightness,
slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
+ } else {
+ animateScreenBrightness(brightness, 0);
}
}
@@ -933,7 +925,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
if (!reportOnly) {
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
try {
diff --git a/com/android/server/job/controllers/TimeController.java b/com/android/server/job/controllers/TimeController.java
index ee4c606f..d90699a6 100644
--- a/com/android/server/job/controllers/TimeController.java
+++ b/com/android/server/job/controllers/TimeController.java
@@ -110,7 +110,7 @@ public final class TimeController extends StateController {
maybeUpdateAlarmsLocked(
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE,
- new WorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ job.getSourceUid());
}
}
@@ -156,7 +156,6 @@ public final class TimeController extends StateController {
synchronized (mLock) {
long nextExpiryTime = Long.MAX_VALUE;
int nextExpiryUid = 0;
- String nextExpiryPackageName = null;
final long nowElapsedMillis = SystemClock.elapsedRealtime();
Iterator<JobStatus> it = mTrackedJobs.iterator();
@@ -172,13 +171,10 @@ public final class TimeController extends StateController {
} else { // Sorted by expiry time, so take the next one and stop.
nextExpiryTime = job.getLatestRunTimeElapsed();
nextExpiryUid = job.getSourceUid();
- nextExpiryPackageName = job.getSourcePackageName();
break;
}
}
- setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryPackageName != null
- ? new WorkSource(nextExpiryUid, nextExpiryPackageName)
- : new WorkSource(nextExpiryUid));
+ setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryUid);
}
}
@@ -204,7 +200,6 @@ public final class TimeController extends StateController {
final long nowElapsedMillis = SystemClock.elapsedRealtime();
long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
- String nextDelayPackageName = null;
boolean ready = false;
Iterator<JobStatus> it = mTrackedJobs.iterator();
while (it.hasNext()) {
@@ -226,16 +221,13 @@ public final class TimeController extends StateController {
if (nextDelayTime > jobDelayTime) {
nextDelayTime = jobDelayTime;
nextDelayUid = job.getSourceUid();
- nextDelayPackageName = job.getSourcePackageName();
}
}
}
if (ready) {
mStateChangedListener.onControllerStateChanged();
}
- setDelayExpiredAlarmLocked(nextDelayTime, nextDelayPackageName != null
- ? new WorkSource(nextDelayUid, nextDelayPackageName)
- : new WorkSource(nextDelayUid));
+ setDelayExpiredAlarmLocked(nextDelayTime, nextDelayUid);
}
}
@@ -249,12 +241,12 @@ public final class TimeController extends StateController {
}
private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed,
- WorkSource ws) {
+ int uid) {
if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
- setDelayExpiredAlarmLocked(delayExpiredElapsed, ws);
+ setDelayExpiredAlarmLocked(delayExpiredElapsed, uid);
}
if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
- setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, ws);
+ setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, uid);
}
}
@@ -263,11 +255,11 @@ public final class TimeController extends StateController {
* delay will expire.
* This alarm <b>will</b> wake up the phone.
*/
- private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
+ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
- mNextDelayExpiredElapsedMillis, ws);
+ mNextDelayExpiredElapsedMillis, uid);
}
/**
@@ -275,11 +267,11 @@ public final class TimeController extends StateController {
* deadline will expire.
* This alarm <b>will</b> wake up the phone.
*/
- private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
+ private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
- mNextJobExpiredElapsedMillis, ws);
+ mNextJobExpiredElapsedMillis, uid);
}
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
@@ -291,7 +283,7 @@ public final class TimeController extends StateController {
}
private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
- long alarmTimeElapsed, WorkSource ws) {
+ long alarmTimeElapsed, int uid) {
ensureAlarmServiceLocked();
if (alarmTimeElapsed == Long.MAX_VALUE) {
mAlarmService.cancel(listener);
@@ -300,7 +292,7 @@ public final class TimeController extends StateController {
Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
}
mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed,
- AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws);
+ AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, new WorkSource(uid));
}
}
diff --git a/com/android/server/location/GnssLocationProvider.java b/com/android/server/location/GnssLocationProvider.java
index e41c17df..0aa6a90e 100644
--- a/com/android/server/location/GnssLocationProvider.java
+++ b/com/android/server/location/GnssLocationProvider.java
@@ -417,11 +417,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
// stops output right at 600m/s, depriving this of the information of a device that reaches
// greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
private static final float ITAR_SPEED_LIMIT_METERS_PER_SECOND = 400.0F;
-
- // TODO: improve comment
- // Volatile to ensure that potentially near-concurrent outputs from HAL
- // react to this value change promptly
- private volatile boolean mItarSpeedLimitExceeded = false;
+ private boolean mItarSpeedLimitExceeded = false;
// GNSS Metrics
private GnssMetrics mGnssMetrics;
diff --git a/com/android/server/media/MediaSessionRecord.java b/com/android/server/media/MediaSessionRecord.java
index 664d2f97..0b11479a 100644
--- a/com/android/server/media/MediaSessionRecord.java
+++ b/com/android/server/media/MediaSessionRecord.java
@@ -784,14 +784,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mService.enforcePhoneStatePermission(pid, uid);
}
mFlags = flags;
- if ((flags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
- final long token = Binder.clearCallingIdentity();
- try {
- mService.setGlobalPrioritySession(MediaSessionRecord.this);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
}
diff --git a/com/android/server/media/MediaSessionService.java b/com/android/server/media/MediaSessionService.java
index aa652445..b9a2d184 100644
--- a/com/android/server/media/MediaSessionService.java
+++ b/com/android/server/media/MediaSessionService.java
@@ -178,6 +178,17 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
+ if (mGlobalPrioritySession != record) {
+ Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+ + " to " + record);
+ mGlobalPrioritySession = record;
+ if (user != null && user.mPriorityStack.contains(record)) {
+ // Handle the global priority session separately.
+ // Otherwise, it will be the media button session even after it becomes
+ // inactive because it has been the lastly played media app.
+ user.mPriorityStack.removeSession(record);
+ }
+ }
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
}
@@ -193,27 +204,10 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- public void setGlobalPrioritySession(MediaSessionRecord record) {
- synchronized (mLock) {
- FullUserRecord user = getFullUserRecordLocked(record.getUserId());
- if (mGlobalPrioritySession != record) {
- Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
- + " to " + record);
- mGlobalPrioritySession = record;
- if (user != null && user.mPriorityStack.contains(record)) {
- // Handle the global priority session separately.
- // Otherwise, it can be the media button session regardless of the active state
- // because it or other system components might have been the lastly played media
- // app.
- user.mPriorityStack.removeSession(record);
- }
- }
- }
- }
-
private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
- List<MediaSessionRecord> records = new ArrayList<>();
+ List<MediaSessionRecord> records;
if (userId == UserHandle.USER_ALL) {
+ records = new ArrayList<>();
int size = mUserRecords.size();
for (int i = 0; i < size; i++) {
records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
@@ -222,9 +216,9 @@ public class MediaSessionService extends SystemService implements Monitor {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.w(TAG, "getSessions failed. Unknown user " + userId);
- return records;
+ return new ArrayList<>();
}
- records.addAll(user.mPriorityStack.getActiveSessions(userId));
+ records = user.mPriorityStack.getActiveSessions(userId);
}
// Return global priority session at the first whenever it's asked.
diff --git a/com/android/server/notification/NotificationManagerService.java b/com/android/server/notification/NotificationManagerService.java
index 238d87b7..14cd0555 100644
--- a/com/android/server/notification/NotificationManagerService.java
+++ b/com/android/server/notification/NotificationManagerService.java
@@ -133,6 +133,7 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationRecordProto;
import android.service.notification.NotificationServiceDumpProto;
+import android.service.notification.NotificationServiceProto;
import android.service.notification.NotificationStats;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -281,7 +282,6 @@ public class NotificationManagerService extends SystemService {
private WindowManagerInternal mWindowManagerInternal;
private AlarmManager mAlarmManager;
private ICompanionDeviceManager mCompanionManager;
- private AccessibilityManager mAccessibilityManager;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -1221,12 +1221,6 @@ public class NotificationManagerService extends SystemService {
mUsageStats = us;
}
- @VisibleForTesting
- void setAccessibilityManager(AccessibilityManager am) {
- mAccessibilityManager = am;
- }
-
-
// TODO: All tests should use this init instead of the one-off setters above.
@VisibleForTesting
void init(Looper looper, IPackageManager packageManager,
@@ -1241,8 +1235,6 @@ public class NotificationManagerService extends SystemService {
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
- mAccessibilityManager =
- (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAm = ActivityManager.getService();
mPackageManager = packageManager;
mPackageManagerClient = packageManagerClient;
@@ -3262,7 +3254,7 @@ public class NotificationManagerService extends SystemService {
final NotificationRecord nr = mNotificationList.get(i);
if (filter.filtered && !filter.matches(nr.sbn)) continue;
nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.POSTED);
+ proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
}
}
N = mEnqueuedNotifications.size();
@@ -3271,7 +3263,7 @@ public class NotificationManagerService extends SystemService {
final NotificationRecord nr = mEnqueuedNotifications.get(i);
if (filter.filtered && !filter.matches(nr.sbn)) continue;
nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.ENQUEUED);
+ proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
}
}
List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
@@ -3281,7 +3273,7 @@ public class NotificationManagerService extends SystemService {
final NotificationRecord nr = snoozed.get(i);
if (filter.filtered && !filter.matches(nr.sbn)) continue;
nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.SNOOZED);
+ proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
}
}
proto.end(records);
@@ -4125,16 +4117,13 @@ public class NotificationManagerService extends SystemService {
// These are set inside the conditional if the notification is allowed to make noise.
boolean hasValidVibrate = false;
boolean hasValidSound = false;
- boolean sentAccessibilityEvent = false;
- // If the notification will appear in the status bar, it should send an accessibility
- // event
- if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
- sentAccessibilityEvent = true;
- }
if (aboveThreshold && isNotificationForCurrentUser(record)) {
-
+ // If the notification will appear in the status bar, it should send an accessibility
+ // event
+ if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+ }
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -4152,10 +4141,6 @@ public class NotificationManagerService extends SystemService {
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
- if (!sentAccessibilityEvent) {
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
- sentAccessibilityEvent = true;
- }
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
mSoundNotificationKey = key;
@@ -4676,7 +4661,8 @@ public class NotificationManagerService extends SystemService {
}
void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
- if (!mAccessibilityManager.isEnabled()) {
+ AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
+ if (!manager.isEnabled()) {
return;
}
@@ -4690,7 +4676,7 @@ public class NotificationManagerService extends SystemService {
event.getText().add(tickerText);
}
- mAccessibilityManager.sendAccessibilityEvent(event);
+ manager.sendAccessibilityEvent(event);
}
/**
diff --git a/com/android/server/notification/ZenModeFiltering.java b/com/android/server/notification/ZenModeFiltering.java
index abf29006..a7a2743d 100644
--- a/com/android/server/notification/ZenModeFiltering.java
+++ b/com/android/server/notification/ZenModeFiltering.java
@@ -117,19 +117,15 @@ public class ZenModeFiltering {
ZenLog.traceIntercepted(record, "alarmsOnly");
return true;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ if (isAlarm(record)) {
+ // Alarms are always priority
+ return false;
+ }
// allow user-prioritized packages through in priority mode
if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
ZenLog.traceNotIntercepted(record, "priorityApp");
return false;
}
-
- if (isAlarm(record)) {
- if (!config.allowAlarms) {
- ZenLog.traceIntercepted(record, "!allowAlarms");
- return true;
- }
- return false;
- }
if (isCall(record)) {
if (config.allowRepeatCallers
&& REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
@@ -163,15 +159,6 @@ public class ZenModeFiltering {
}
return false;
}
- AudioAttributes aa = record.getAudioAttributes();
- if (aa != null && AudioAttributes.SUPPRESSIBLE_USAGES.get(aa.getUsage()) ==
- AudioAttributes.SUPPRESSIBLE_MEDIA_SYSTEM_OTHER) {
- if (!config.allowMediaSystemOther) {
- ZenLog.traceIntercepted(record, "!allowMediaSystemOther");
- return true;
- }
- return false;
- }
ZenLog.traceIntercepted(record, "!priority");
return true;
default:
diff --git a/com/android/server/notification/ZenModeHelper.java b/com/android/server/notification/ZenModeHelper.java
index 710684f4..9fcc67df 100644
--- a/com/android/server/notification/ZenModeHelper.java
+++ b/com/android/server/notification/ZenModeHelper.java
@@ -59,7 +59,6 @@ import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.server.LocalServices;
@@ -88,7 +87,7 @@ public class ZenModeHelper {
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
- @VisibleForTesting protected final AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
protected ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
@@ -103,9 +102,9 @@ public class ZenModeHelper {
private final String SCHEDULED_DEFAULT_RULE_1 = "SCHEDULED_DEFAULT_RULE_1";
private final String SCHEDULED_DEFAULT_RULE_2 = "SCHEDULED_DEFAULT_RULE_2";
- @VisibleForTesting protected int mZenMode;
+ private int mZenMode;
private int mUser = UserHandle.USER_SYSTEM;
- @VisibleForTesting protected ZenModeConfig mConfig;
+ protected ZenModeConfig mConfig;
private AudioManagerInternal mAudioManager;
protected PackageManager mPm;
private long mSuppressedEffects;
@@ -596,9 +595,8 @@ public class ZenModeHelper {
pw.println(config);
return;
}
- pw.printf("allow(alarms=%b,media=%bcalls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s,"
+ pw.printf("allow(calls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s,"
+ "events=%b,reminders=%b,whenScreenOff=%b,whenScreenOn=%b)\n",
- config.allowAlarms, config.allowMediaSystemOther,
config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
config.allowRepeatCallers, config.allowMessages,
ZenModeConfig.sourceToString(config.allowMessagesFrom),
@@ -815,8 +813,7 @@ public class ZenModeHelper {
}
}
- @VisibleForTesting
- protected void applyRestrictions() {
+ private void applyRestrictions() {
final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
// notification restrictions
@@ -825,10 +822,6 @@ public class ZenModeHelper {
// call restrictions
final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
|| (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
- // alarm restrictions
- final boolean muteAlarms = zen && !mConfig.allowAlarms;
- // alarm restrictions
- final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
// total silence restrictions
final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
@@ -840,18 +833,13 @@ public class ZenModeHelper {
applyRestrictions(muteNotifications || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
applyRestrictions(muteCalls || muteEverything, usage);
- } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) {
- applyRestrictions(muteAlarms || muteEverything, usage);
- } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA_SYSTEM_OTHER) {
- applyRestrictions(muteMediaAndSystemSounds || muteEverything, usage);
} else {
applyRestrictions(muteEverything, usage);
}
}
}
- @VisibleForTesting
- protected void applyRestrictions(boolean mute, int usage) {
+ private void applyRestrictions(boolean mute, int usage) {
final String[] exceptionPackages = null; // none (for now)
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
diff --git a/com/android/server/pm/LauncherAppsService.java b/com/android/server/pm/LauncherAppsService.java
index b06b5838..4a5ce120 100644
--- a/com/android/server/pm/LauncherAppsService.java
+++ b/com/android/server/pm/LauncherAppsService.java
@@ -30,10 +30,12 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -62,6 +64,7 @@ import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -86,14 +89,10 @@ public class LauncherAppsService extends SystemService {
static class BroadcastCookie {
public final UserHandle user;
public final String packageName;
- public final int callingUid;
- public final int callingPid;
- BroadcastCookie(UserHandle userHandle, String packageName, int callingPid, int callingUid) {
+ BroadcastCookie(UserHandle userHandle, String packageName) {
this.user = userHandle;
this.packageName = packageName;
- this.callingUid = callingUid;
- this.callingPid = callingPid;
}
}
@@ -128,11 +127,6 @@ public class LauncherAppsService extends SystemService {
return getCallingUid();
}
- @VisibleForTesting
- int injectBinderCallingPid() {
- return getCallingPid();
- }
-
final int injectCallingUserId() {
return UserHandle.getUserId(injectBinderCallingUid());
}
@@ -172,7 +166,7 @@ public class LauncherAppsService extends SystemService {
}
mListeners.unregister(listener);
mListeners.register(listener, new BroadcastCookie(UserHandle.of(getCallingUserId()),
- callingPackage, injectBinderCallingPid(), injectBinderCallingUid()));
+ callingPackage));
}
}
@@ -444,7 +438,7 @@ public class LauncherAppsService extends SystemService {
private void ensureShortcutPermission(@NonNull String callingPackage) {
verifyCallingPackage(callingPackage);
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
- callingPackage, injectBinderCallingPid(), injectBinderCallingUid())) {
+ callingPackage)) {
throw new SecurityException("Caller can't access shortcut information");
}
}
@@ -467,8 +461,7 @@ public class LauncherAppsService extends SystemService {
return new ParceledListSlice<>((List<ShortcutInfo>)
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
callingPackage, changedSince, packageName, shortcutIds,
- componentName, flags, targetUser.getIdentifier(),
- injectBinderCallingPid(), injectBinderCallingUid()));
+ componentName, flags, targetUser.getIdentifier()));
}
@Override
@@ -521,7 +514,7 @@ public class LauncherAppsService extends SystemService {
public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
- callingPackage, injectBinderCallingPid(), injectBinderCallingUid());
+ callingPackage);
}
@Override
@@ -543,8 +536,7 @@ public class LauncherAppsService extends SystemService {
}
final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
- getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId,
- injectBinderCallingPid(), injectBinderCallingUid());
+ getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId);
if (intents == null || intents.length == 0) {
return false;
}
@@ -909,8 +901,7 @@ public class LauncherAppsService extends SystemService {
// Make sure the caller has the permission.
if (!mShortcutServiceInternal.hasShortcutHostPermission(
- launcherUserId, cookie.packageName,
- cookie.callingPid, cookie.callingUid)) {
+ launcherUserId, cookie.packageName)) {
continue;
}
// Each launcher has a different set of pinned shortcuts, so we need to do a
@@ -923,8 +914,8 @@ public class LauncherAppsService extends SystemService {
/* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
/* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
- | ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED
- , userId, cookie.callingPid, cookie.callingUid);
+ | ShortcutQuery.FLAG_GET_ALL_KINDS
+ , userId);
try {
listener.onShortcutChanged(user, packageName,
new ParceledListSlice<>(list));
diff --git a/com/android/server/pm/PackageDexOptimizer.java b/com/android/server/pm/PackageDexOptimizer.java
index cf0ffbb1..8ebeeae2 100644
--- a/com/android/server/pm/PackageDexOptimizer.java
+++ b/com/android/server/pm/PackageDexOptimizer.java
@@ -236,10 +236,9 @@ public class PackageDexOptimizer {
*/
@GuardedBy("mInstallLock")
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
- String compilerFilter, boolean profileUpdated, String classLoaderContext,
+ String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
- int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
- profileUpdated, downgrade);
+ int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
return DEX_OPT_SKIPPED;
}
@@ -252,8 +251,8 @@ public class PackageDexOptimizer {
Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
+ " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
- + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
- + " classLoaderContext=" + classLoaderContext);
+ + " target-filter=" + compilerFilter + " oatDir=" + oatDir
+ + " sharedLibraries=" + sharedLibrariesPath);
try {
long startTime = System.currentTimeMillis();
@@ -262,7 +261,7 @@ public class PackageDexOptimizer {
// installd only uses downgrade flag for secondary dex files and ignores it for
// primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
- compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
+ compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
false /* downgrade*/);
if (packageStats != null) {
@@ -509,11 +508,11 @@ public class PackageDexOptimizer {
* configuration (isa, compiler filter, profile).
*/
private int getDexoptNeeded(String path, String isa, String compilerFilter,
- String classLoaderContext, boolean newProfile, boolean downgrade) {
+ boolean newProfile, boolean downgrade) {
int dexoptNeeded;
try {
- dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext,
- newProfile, downgrade);
+ dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
+ downgrade);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
diff --git a/com/android/server/pm/PackageInstallerSession.java b/com/android/server/pm/PackageInstallerSession.java
index 496ebc23..d62f0934 100644
--- a/com/android/server/pm/PackageInstallerSession.java
+++ b/com/android/server/pm/PackageInstallerSession.java
@@ -1127,8 +1127,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mResolvedInstructionSets.add(archSubDir.getName());
List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
- if (!oatFiles.isEmpty()) {
- mResolvedInheritedFiles.addAll(oatFiles);
+
+ // Only add compiled files associated with the base.
+ // Once b/62269291 is resolved, we can add all compiled files again.
+ for (File oatFile : oatFiles) {
+ if (oatFile.getName().equals("base.art")
+ || oatFile.getName().equals("base.odex")
+ || oatFile.getName().equals("base.vdex")) {
+ mResolvedInheritedFiles.add(oatFile);
+ }
}
}
}
diff --git a/com/android/server/pm/PackageManagerService.java b/com/android/server/pm/PackageManagerService.java
index 275db1fa..7d1a6470 100644
--- a/com/android/server/pm/PackageManagerService.java
+++ b/com/android/server/pm/PackageManagerService.java
@@ -398,7 +398,7 @@ public class PackageManagerService extends IPackageManager.Stub
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
private static final boolean DEBUG_INSTALL = false;
- public static final boolean DEBUG_REMOVE = false;
+ private static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -406,7 +406,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_FILTERS = false;
- public static final boolean DEBUG_PERMISSIONS = false;
+ private static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
@@ -954,6 +954,9 @@ public class PackageManagerService extends IPackageManager.Stub
final SparseArray<PackageVerificationState> mPendingVerification
= new SparseArray<PackageVerificationState>();
+ /** Set of packages associated with each app op permission. */
+ final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
final PackageInstallerService mInstallerService;
private final PackageDexOptimizer mPackageDexOptimizer;
@@ -2878,7 +2881,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
+ updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
@@ -3261,6 +3264,23 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
+ // If we have a profile for a compressed APK, copy it to the reference location.
+ // Since the package is the stub one, remove the stub suffix to get the normal package and
+ // APK name.
+ File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, ""));
+ if (profileFile.exists()) {
+ try {
+ // We could also do this lazily before calling dexopt in
+ // PackageDexOptimizer to prevent this happening on first boot. The issue
+ // is that we don't have a good way to say "do this only once".
+ if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+ pkg.applicationInfo.uid, pkg.packageName)) {
+ Log.e(TAG, "decompressPackage failed to copy system profile!");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e);
+ }
+ }
return dstCodePath;
}
@@ -3348,9 +3368,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isUpgrade() {
// allow instant applications
- // The system property allows testing ota flow when upgraded to the same image.
- return mIsUpgrade || SystemProperties.getBoolean(
- "persist.pm.mock-upgrade", false /* default */);
+ return mIsUpgrade;
}
private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
@@ -5164,7 +5182,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
- if (mSettings.mPermissions.isPermissionInstant(permName)) {
+ if (mPermissionManager.isPermissionInstant(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -5233,8 +5251,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
- return mPermissionManager.addDynamicPermission(
+ boolean addPermission(PermissionInfo info, final boolean async) {
+ return mPermissionManager.addPermission(
info, async, getCallingUid(), new PermissionCallback() {
@Override
public void onPermissionChanged() {
@@ -5250,20 +5268,20 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
- return addDynamicPermission(info, false);
+ return addPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
- return addDynamicPermission(info, true);
+ return addPermission(info, true);
}
}
@Override
public void removePermission(String permName) {
- mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
+ mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
@@ -5924,8 +5942,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public String[] getAppOpPermissionPackages(String permName) {
- return mPermissionManager.getAppOpPermissionPackages(permName);
+ public String[] getAppOpPermissionPackages(String permissionName) {
+ if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ synchronized (mPackages) {
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
+ if (pkgs == null) {
+ return null;
+ }
+ return pkgs.toArray(new String[pkgs.size()]);
+ }
}
@Override
@@ -9097,30 +9124,10 @@ public class PackageManagerService extends IPackageManager.Stub
// package and APK names.
String systemProfilePath =
getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
- profileFile = new File(systemProfilePath);
- // If we have a profile for a compressed APK, copy it to the reference
- // location.
- // Note that copying the profile here will cause it to override the
- // reference profile every OTA even though the existing reference profile
- // may have more data. We can't copy during decompression since the
- // directories are not set up at that point.
- if (profileFile.exists()) {
- try {
- // We could also do this lazily before calling dexopt in
- // PackageDexOptimizer to prevent this happening on first boot. The
- // issue is that we don't have a good way to say "do this only
- // once".
- if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
- pkg.applicationInfo.uid, pkg.packageName)) {
- Log.e(TAG, "Failed to copy system profile for stub package!");
- } else {
- useProfileForDexopt = true;
- }
- } catch (Exception e) {
- Log.e(TAG, "Failed to copy profile " +
- profileFile.getAbsolutePath() + " ", e);
- }
- }
+ File systemProfile = new File(systemProfilePath);
+ // Use the profile for compilation if there exists one for the same package
+ // in the system partition.
+ useProfileForDexopt = systemProfile.exists();
}
}
}
@@ -10339,7 +10346,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.packageName);
// SIDE EFFECTS; updates permissions system state; move elsewhere
- mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
+ mSettings.transferPermissionsLPw(origName, pkg.packageName);
}
}
}
@@ -11187,13 +11194,54 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
}
+ N = pkg.permissions.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Permission p = pkg.permissions.get(i);
- // Dont allow ephemeral apps to define new permissions.
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- Slog.w(TAG, "Permissions from package " + pkg.packageName
- + " ignored: instant apps cannot define new permissions.");
- } else {
- mPermissionManager.addAllPermissions(pkg, chatty);
+ // Dont allow ephemeral apps to define new permissions.
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName
+ + " ignored: instant apps cannot define new permissions.");
+ continue;
+ }
+
+ // Assume by default that we did not install this permission into the system.
+ p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
+
+ // Now that permission groups have a special meaning, we ignore permission
+ // groups for legacy apps to prevent unexpected behavior. In particular,
+ // permissions for one app being granted to someone just because they happen
+ // to be in a group defined by another app (before this had no implications).
+ if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ p.group = mPermissionGroups.get(p.info.group);
+ // Warn for a permission in an unknown group.
+ if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
+ Slog.i(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " in an unknown group " + p.info.group);
+ }
+ }
+
+ // TODO Move to PermissionManager once mPermissionTrees moves there.
+// p.tree ? mSettings.mPermissionTrees
+// : mSettings.mPermissions;
+// final BasePermission bp = BasePermission.createOrUpdate(
+// permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
+// permissionMap.put(p.info.name, bp);
+ if (p.tree) {
+ final ArrayMap<String, BasePermission> permissionMap =
+ mSettings.mPermissionTrees;
+ final BasePermission bp = BasePermission.createOrUpdate(
+ permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
+ chatty);
+ permissionMap.put(p.info.name, bp);
+ } else {
+ final BasePermission bp = BasePermission.createOrUpdate(
+ (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
+ p, pkg, mSettings.mPermissionTrees, chatty);
+ mPermissionManager.putPermissionTEMP(p.info.name, bp);
+ }
}
N = pkg.instrumentation.size();
@@ -11919,7 +11967,53 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
}
- mPermissionManager.removeAllPermissions(pkg, chatty);
+ N = pkg.permissions.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ PackageParser.Permission p = pkg.permissions.get(i);
+ BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
+ if (bp == null) {
+ bp = mSettings.mPermissionTrees.get(p.info.name);
+ }
+ if (bp != null && bp.isPermission(p)) {
+ bp.setPermission(null);
+ if (DEBUG_REMOVE && chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(p.info.name);
+ }
+ }
+ if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
+ }
+ }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+
+ N = pkg.requestedPermissions.size();
+ r = null;
+ for (i=0; i<N; i++) {
+ String perm = pkg.requestedPermissions.get(i);
+ if (mPermissionManager.isPermissionAppOp(perm)) {
+ ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
+ if (appOpPkgs.isEmpty()) {
+ mAppOpPermissionPackages.remove(perm);
+ }
+ }
+ }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
N = pkg.instrumentation.size();
r = null;
@@ -11980,9 +12074,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- public static final int UPDATE_PERMISSIONS_ALL = 1<<0;
- public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
- public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
+ for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+ if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+ static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+ static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
// Update the parent permissions
@@ -11998,10 +12101,10 @@ public class PackageManagerService extends IPackageManager.Stub
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
int flags) {
final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
- updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags);
+ updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
}
- private void updatePermissionsLocked(String changingPkg,
+ private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
// TODO: Most of the methods exposing BasePermission internals [source package name,
// etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
@@ -12013,11 +12116,55 @@ public class PackageManagerService extends IPackageManager.Stub
// normal permissions. Today, we need two separate loops because these BasePermission
// objects are stored separately.
// Make sure there are no dangling permission trees.
- flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags);
+ Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
+ while (it.hasNext()) {
+ final BasePermission bp = it.next();
+ if (bp.getSourcePackageSetting() == null) {
+ // We may not yet have parsed the package, so just see if
+ // we still know about its settings.
+ bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
+ }
+ if (bp.getSourcePackageSetting() == null) {
+ Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ it.remove();
+ } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
+ Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ flags |= UPDATE_PERMISSIONS_ALL;
+ it.remove();
+ }
+ }
+ }
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
- flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags);
+ final Iterator<BasePermission> permissionIter =
+ mPermissionManager.getPermissionIteratorTEMP();
+ while (permissionIter.hasNext()) {
+ final BasePermission bp = permissionIter.next();
+ if (bp.isDynamic()) {
+ bp.updateDynamicPermission(mSettings.mPermissionTrees);
+ }
+ if (bp.getSourcePackageSetting() == null) {
+ // We may not yet have parsed the package, so just see if
+ // we still know about its settings.
+ bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
+ }
+ if (bp.getSourcePackageSetting() == null) {
+ Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ permissionIter.remove();
+ } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
+ Slog.i(TAG, "Removing old permission: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ flags |= UPDATE_PERMISSIONS_ALL;
+ permissionIter.remove();
+ }
+ }
+ }
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
// Now update the permissions for all packages, in particular
@@ -12139,7 +12286,12 @@ public class PackageManagerService extends IPackageManager.Stub
// Keep track of app op permissions.
if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.packageName);
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mAppOpPermissionPackages.put(perm, pkgs);
+ }
+ pkgs.add(pkg.packageName);
}
if (bp.isNormal()) {
@@ -21086,7 +21238,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// permissions, ensure permissions are updated. Beware of dragons if you
// try optimizing this.
synchronized (mPackages) {
- updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
+ updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
UPDATE_PERMISSIONS_ALL);
}
@@ -21137,8 +21289,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
if (mPrivappPermissionsViolations != null) {
- throw new IllegalStateException("Signature|privileged permissions not in "
+ Slog.wtf(TAG,"Signature|privileged permissions not in "
+ "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
+ mPrivappPermissionsViolations = null;
}
}
@@ -21631,6 +21784,22 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
+ if (packageName == null && permissionNames == null) {
+ for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+ if (iperm == 0) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("AppOp Permissions:");
+ }
+ pw.print(" AppOp Permission ");
+ pw.print(mAppOpPermissionPackages.keyAt(iperm));
+ pw.println(":");
+ ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+ for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+ pw.print(" "); pw.println(pkgs.valueAt(ipkg));
+ }
+ }
+ }
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
@@ -21860,7 +22029,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
synchronized (mAvailableFeatures) {
final int count = mAvailableFeatures.size();
for (int i = 0; i < count; i++) {
- mAvailableFeatures.valueAt(i).writeToProto(proto, PackageServiceDumpProto.FEATURES);
+ final FeatureInfo feat = mAvailableFeatures.valueAt(i);
+ final long featureToken = proto.start(PackageServiceDumpProto.FEATURES);
+ proto.write(PackageServiceDumpProto.FeatureProto.NAME, feat.name);
+ proto.write(PackageServiceDumpProto.FeatureProto.VERSION, feat.version);
+ proto.end(featureToken);
}
}
}
@@ -21892,7 +22065,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
@@ -21919,7 +22092,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println();
ipw.println("Compiler stats:");
ipw.increaseIndent();
@@ -22130,7 +22303,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
+ mSdkVersion + "; regranting permissions for " + volumeUuid);
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLocked(null, null, volumeUuid, updateFlags);
+ updatePermissionsLPw(null, null, volumeUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -23138,15 +23311,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
void onNewUserCreated(final int userId) {
synchronized(mPackages) {
mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
- // If permission review for legacy apps is required, we represent
- // dagerous permissions for such apps as always granted runtime
- // permissions to keep per user flag state whether review is needed.
- // Hence, if a new user is added we have to propagate dangerous
- // permission grants for these legacy apps.
- if (mPermissionReviewRequired) {
- updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
- | UPDATE_PERMISSIONS_REPLACE_ALL);
- }
+ }
+ // If permission review for legacy apps is required, we represent
+ // dagerous permissions for such apps as always granted runtime
+ // permissions to keep per user flag state whether review is needed.
+ // Hence, if a new user is added we have to propagate dangerous
+ // permission grants for these legacy apps.
+ if (mPermissionReviewRequired) {
+ updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+ | UPDATE_PERMISSIONS_REPLACE_ALL);
}
}
@@ -23590,12 +23763,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
- public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) {
+ public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
synchronized (mPackages) {
- return mPermissionGroups.get(groupName);
+ return BasePermission.enforcePermissionTreeLP(
+ mSettings.mPermissionTrees, permName, callingUid);
}
}
-
@Override
public boolean isInstantApp(String packageName, int userId) {
return PackageManagerService.this.isInstantApp(packageName, userId);
diff --git a/com/android/server/pm/Settings.java b/com/android/server/pm/Settings.java
index 191b43a6..00844114 100644
--- a/com/android/server/pm/Settings.java
+++ b/com/android/server/pm/Settings.java
@@ -378,6 +378,10 @@ public final class Settings {
private final ArrayMap<Long, Integer> mKeySetRefs =
new ArrayMap<Long, Integer>();
+ // Mapping from permission tree names to info about them.
+ final ArrayMap<String, BasePermission> mPermissionTrees =
+ new ArrayMap<String, BasePermission>();
+
// Packages that have been uninstalled and still need their external
// storage data deleted.
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
@@ -412,7 +416,7 @@ public final class Settings {
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
/** Settings and other information about permissions */
- final PermissionSettings mPermissions;
+ private final PermissionSettings mPermissions;
Settings(PermissionSettings permissions, Object lock) {
this(Environment.getDataDirectory(), permissions, lock);
@@ -618,10 +622,6 @@ public final class Settings {
return null;
}
- void addAppOpPackage(String permName, String packageName) {
- mPermissions.addAppOpPackage(permName, packageName);
- }
-
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
@@ -666,6 +666,13 @@ public final class Settings {
}
/**
+ * Transfers ownership of permissions from one package to another.
+ */
+ void transferPermissionsLPw(String origPackageName, String newPackageName) {
+ mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
+ }
+
+ /**
* Creates a new {@code PackageSetting} object.
* Use this method instead of the constructor to ensure a settings object is created
* with the correct base.
@@ -2489,7 +2496,9 @@ public final class Settings {
}
serializer.startTag(null, "permission-trees");
- mPermissions.writePermissionTrees(serializer);
+ for (BasePermission bp : mPermissionTrees.values()) {
+ writePermissionLPr(serializer, bp);
+ }
serializer.endTag(null, "permission-trees");
serializer.startTag(null, "permissions");
@@ -3033,7 +3042,7 @@ public final class Settings {
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
- mPermissions.readPermissionTrees(parser);
+ PermissionSettings.readPermissions(mPermissionTrees, parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if (tagName.equals("preferred-packages")) {
@@ -4929,7 +4938,11 @@ public final class Settings {
void dumpSharedUsersProto(ProtoOutputStream proto) {
final int count = mSharedUsers.size();
for (int i = 0; i < count; i++) {
- mSharedUsers.valueAt(i).writeToProto(proto, PackageServiceDumpProto.SHARED_USERS);
+ final SharedUserSetting su = mSharedUsers.valueAt(i);
+ final long sharedUserToken = proto.start(PackageServiceDumpProto.SHARED_USERS);
+ proto.write(PackageServiceDumpProto.SharedUserProto.USER_ID, su.userId);
+ proto.write(PackageServiceDumpProto.SharedUserProto.NAME, su.name);
+ proto.end(sharedUserToken);
}
}
diff --git a/com/android/server/pm/SharedUserSetting.java b/com/android/server/pm/SharedUserSetting.java
index 877da144..a0dadae3 100644
--- a/com/android/server/pm/SharedUserSetting.java
+++ b/com/android/server/pm/SharedUserSetting.java
@@ -18,9 +18,7 @@ package com.android.server.pm;
import android.annotation.Nullable;
import android.content.pm.PackageParser;
-import android.service.pm.PackageServiceDumpProto;
import android.util.ArraySet;
-import android.util.proto.ProtoOutputStream;
import java.util.ArrayList;
import java.util.Collection;
@@ -55,13 +53,6 @@ public final class SharedUserSetting extends SettingBase {
+ name + "/" + userId + "}";
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(PackageServiceDumpProto.SharedUserProto.USER_ID, userId);
- proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name);
- proto.end(token);
- }
-
void removePackage(PackageSetting packageSetting) {
if (packages.remove(packageSetting)) {
// recalculate the pkgFlags for this shared user if needed
diff --git a/com/android/server/pm/ShortcutBitmapSaver.java b/com/android/server/pm/ShortcutBitmapSaver.java
index 815f8851..4f5d1560 100644
--- a/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/com/android/server/pm/ShortcutBitmapSaver.java
@@ -21,8 +21,6 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.drawable.Icon;
-import android.os.StrictMode;
-import android.os.StrictMode.ThreadPolicy;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
@@ -167,13 +165,7 @@ public class ShortcutBitmapSaver {
// Compress it and enqueue to the requests.
final byte[] bytes;
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
- // compress() triggers a slow call, but in this case it's needed to save RAM and also
- // the target bitmap is of an icon size, so let's just permit it.
- StrictMode.setThreadPolicy(new ThreadPolicy.Builder(oldPolicy)
- .permitCustomSlowCalls()
- .build());
final Bitmap shrunk = mService.shrinkBitmap(original, maxDimension);
try {
try (final ByteArrayOutputStream out = new ByteArrayOutputStream(64 * 1024)) {
@@ -192,8 +184,6 @@ public class ShortcutBitmapSaver {
} catch (IOException | RuntimeException | OutOfMemoryError e) {
Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
return;
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
}
shortcut.addFlags(
diff --git a/com/android/server/pm/ShortcutLauncher.java b/com/android/server/pm/ShortcutLauncher.java
index cedf4763..f922ad19 100644
--- a/com/android/server/pm/ShortcutLauncher.java
+++ b/com/android/server/pm/ShortcutLauncher.java
@@ -83,16 +83,11 @@ class ShortcutLauncher extends ShortcutPackageItem {
return mOwnerUserId;
}
- @Override
- protected boolean canRestoreAnyVersion() {
- // Launcher's pinned shortcuts can be restored to an older version.
- return true;
- }
-
/**
* Called when the new package can't receive the backup, due to signature or version mismatch.
*/
- private void onRestoreBlocked() {
+ @Override
+ protected void onRestoreBlocked() {
final ArrayList<PackageWithUser> pinnedPackages =
new ArrayList<>(mPinnedShortcuts.keySet());
mPinnedShortcuts.clear();
@@ -106,21 +101,15 @@ class ShortcutLauncher extends ShortcutPackageItem {
}
@Override
- protected void onRestored(int restoreBlockReason) {
- // For launcher, possible reasons here are DISABLED_REASON_SIGNATURE_MISMATCH or
- // DISABLED_REASON_BACKUP_NOT_SUPPORTED.
- // DISABLED_REASON_VERSION_LOWER will NOT happen because we don't check version
- // code for launchers.
- if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- onRestoreBlocked();
- }
+ protected void onRestored() {
+ // Nothing to do.
}
/**
* Pin the given shortcuts, replacing the current pinned ones.
*/
public void pinShortcuts(@UserIdInt int packageUserId,
- @NonNull String packageName, @NonNull List<String> ids, boolean forPinRequest) {
+ @NonNull String packageName, @NonNull List<String> ids) {
final ShortcutPackage packageShortcuts =
mShortcutUser.getPackageShortcutsIfExists(packageName);
if (packageShortcuts == null) {
@@ -135,12 +124,8 @@ class ShortcutLauncher extends ShortcutPackageItem {
} else {
final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
- // Actually pin shortcuts.
- // This logic here is to make sure a launcher cannot pin a shortcut that is floating
- // (i.e. not dynamic nor manifest but is pinned) and pinned by another launcher.
- // In this case, technically the shortcut doesn't exist to this launcher, so it can't
- // pin it.
- // (Maybe unnecessarily strict...)
+ // Pin shortcuts. Make sure only pin the ones that were visible to the caller.
+ // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
final ArraySet<String> newSet = new ArraySet<>();
@@ -150,10 +135,8 @@ class ShortcutLauncher extends ShortcutPackageItem {
if (si == null) {
continue;
}
- if (si.isDynamic()
- || si.isManifestShortcut()
- || (prevSet != null && prevSet.contains(id))
- || forPinRequest) {
+ if (si.isDynamic() || si.isManifestShortcut()
+ || (prevSet != null && prevSet.contains(id))) {
newSet.add(id);
}
}
@@ -172,7 +155,7 @@ class ShortcutLauncher extends ShortcutPackageItem {
}
/**
- * Return true if the given shortcut is pinned by this launcher.<code></code>
+ * Return true if the given shortcut is pinned by this launcher.
*/
public boolean hasPinned(ShortcutInfo shortcut) {
final ArraySet<String> pinned =
@@ -181,10 +164,10 @@ class ShortcutLauncher extends ShortcutPackageItem {
}
/**
- * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List, boolean)}
+ * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List)}
*/
public void addPinnedShortcut(@NonNull String packageName, @UserIdInt int packageUserId,
- String id, boolean forPinRequest) {
+ String id) {
final ArraySet<String> pinnedSet = getPinnedShortcutIds(packageName, packageUserId);
final ArrayList<String> pinnedList;
if (pinnedSet != null) {
@@ -195,21 +178,21 @@ class ShortcutLauncher extends ShortcutPackageItem {
}
pinnedList.add(id);
- pinShortcuts(packageUserId, packageName, pinnedList, forPinRequest);
+ pinShortcuts(packageUserId, packageName, pinnedList);
}
boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
}
- public void ensurePackageInfo() {
+ public void ensureVersionInfo() {
final PackageInfo pi = mShortcutUser.mService.getPackageInfoWithSignatures(
getPackageName(), getPackageUserId());
if (pi == null) {
Slog.w(TAG, "Package not found: " + getPackageName());
return;
}
- getPackageInfo().updateFromPackageInfo(pi);
+ getPackageInfo().updateVersionInfo(pi);
}
/**
@@ -218,10 +201,6 @@ class ShortcutLauncher extends ShortcutPackageItem {
@Override
public void saveToXml(XmlSerializer out, boolean forBackup)
throws IOException {
- if (forBackup && !getPackageInfo().isBackupAllowed()) {
- // If an launcher app doesn't support backup&restore, then nothing to do.
- return;
- }
final int size = mPinnedShortcuts.size();
if (size == 0) {
return; // Nothing to write.
@@ -230,7 +209,7 @@ class ShortcutLauncher extends ShortcutPackageItem {
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
- getPackageInfo().saveToXml(out, forBackup);
+ getPackageInfo().saveToXml(out);
for (int i = 0; i < size; i++) {
final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
diff --git a/com/android/server/pm/ShortcutPackage.java b/com/android/server/pm/ShortcutPackage.java
index 12f490fa..6fc1e738 100644
--- a/com/android/server/pm/ShortcutPackage.java
+++ b/com/android/server/pm/ShortcutPackage.java
@@ -84,7 +84,6 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_DISABLED_MESSAGE = "dmessage";
private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
- private static final String ATTR_DISABLED_REASON = "disabled-reason";
private static final String ATTR_INTENT_LEGACY = "intent";
private static final String ATTR_INTENT_NO_EXTRA = "intent-base";
private static final String ATTR_RANK = "rank";
@@ -157,25 +156,13 @@ class ShortcutPackage extends ShortcutPackageItem {
}
@Override
- protected boolean canRestoreAnyVersion() {
- return false;
+ protected void onRestoreBlocked() {
+ // Can't restore due to version/signature mismatch. Remove all shortcuts.
+ mShortcuts.clear();
}
@Override
- protected void onRestored(int restoreBlockReason) {
- // Shortcuts have been restored.
- // - Unshadow all shortcuts.
- // - Set disabled reason.
- // - Disable if needed.
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- si.clearFlags(ShortcutInfo.FLAG_SHADOW);
-
- si.setDisabledReason(restoreBlockReason);
- if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- si.addFlags(ShortcutInfo.FLAG_DISABLED);
- }
- }
+ protected void onRestored() {
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
refreshPinnedFlags();
@@ -189,47 +176,31 @@ class ShortcutPackage extends ShortcutPackageItem {
return mShortcuts.get(id);
}
- public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
- ShortcutInfo si = findShortcutById(id);
- return si != null && !si.isVisibleToPublisher();
- }
-
- public boolean isShortcutExistsAndVisibleToPublisher(String id) {
- ShortcutInfo si = findShortcutById(id);
- return si != null && si.isVisibleToPublisher();
- }
-
- private void ensureNotImmutable(@Nullable ShortcutInfo shortcut, boolean ignoreInvisible) {
- if (shortcut != null && shortcut.isImmutable()
- && (!ignoreInvisible || shortcut.isVisibleToPublisher())) {
+ private void ensureNotImmutable(@Nullable ShortcutInfo shortcut) {
+ if (shortcut != null && shortcut.isImmutable()) {
throw new IllegalArgumentException(
"Manifest shortcut ID=" + shortcut.getId()
+ " may not be manipulated via APIs");
}
}
- public void ensureNotImmutable(@NonNull String id, boolean ignoreInvisible) {
- ensureNotImmutable(mShortcuts.get(id), ignoreInvisible);
+ public void ensureNotImmutable(@NonNull String id) {
+ ensureNotImmutable(mShortcuts.get(id));
}
- public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds,
- boolean ignoreInvisible) {
+ public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds) {
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- ensureNotImmutable(shortcutIds.get(i), ignoreInvisible);
+ ensureNotImmutable(shortcutIds.get(i));
}
}
- public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts,
- boolean ignoreInvisible) {
+ public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts) {
for (int i = shortcuts.size() - 1; i >= 0; i--) {
- ensureNotImmutable(shortcuts.get(i).getId(), ignoreInvisible);
+ ensureNotImmutable(shortcuts.get(i).getId());
}
}
- /**
- * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
- */
- private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
+ private ShortcutInfo deleteShortcutInner(@NonNull String id) {
final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
mShortcutUser.mService.removeIconLocked(shortcut);
@@ -239,14 +210,10 @@ class ShortcutPackage extends ShortcutPackageItem {
return shortcut;
}
- /**
- * Force replace a shortcut. If there's already a shortcut with the same ID, it'll be removed,
- * even if it's invisible.
- */
- private void forceReplaceShortcutInner(@NonNull ShortcutInfo newShortcut) {
+ private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
final ShortcutService s = mShortcutUser.mService;
- forceDeleteShortcutInner(newShortcut.getId());
+ deleteShortcutInner(newShortcut.getId());
// Extract Icon and update the icon res ID and the bitmap path.
s.saveIconAndFixUpShortcutLocked(newShortcut);
@@ -255,12 +222,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
/**
- * Add a shortcut. If there's already a one with the same ID, it'll be removed, even if it's
- * invisible.
+ * Add a shortcut, or update one with the same ID, with taking over existing flags.
*
* It checks the max number of dynamic shortcuts.
*/
- public void addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+ public void addOrUpdateDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"add/setDynamicShortcuts() cannot publish disabled shortcuts");
@@ -276,7 +242,7 @@ class ShortcutPackage extends ShortcutPackageItem {
} else {
// It's an update case.
// Make sure the target is updatable. (i.e. should be mutable.)
- oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
+ oldShortcut.ensureUpdatableWith(newShortcut);
wasPinned = oldShortcut.isPinned();
}
@@ -286,7 +252,7 @@ class ShortcutPackage extends ShortcutPackageItem {
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
}
- forceReplaceShortcutInner(newShortcut);
+ addShortcutInner(newShortcut);
}
/**
@@ -307,7 +273,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (removeList != null) {
for (int i = removeList.size() - 1; i >= 0; i--) {
- forceDeleteShortcutInner(removeList.get(i));
+ deleteShortcutInner(removeList.get(i));
}
}
}
@@ -315,13 +281,13 @@ class ShortcutPackage extends ShortcutPackageItem {
/**
* Remove all dynamic shortcuts.
*/
- public void deleteAllDynamicShortcuts(boolean ignoreInvisible) {
+ public void deleteAllDynamicShortcuts() {
final long now = mShortcutUser.mService.injectCurrentTimeMillis();
boolean changed = false;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
+ if (si.isDynamic()) {
changed = true;
si.setTimestamp(now);
@@ -341,10 +307,9 @@ class ShortcutPackage extends ShortcutPackageItem {
* @return true if it's actually removed because it wasn't pinned, or false if it's still
* pinned.
*/
- public boolean deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+ public boolean deleteDynamicWithId(@NonNull String shortcutId) {
final ShortcutInfo removed = deleteOrDisableWithId(
- shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
return removed == null;
}
@@ -355,11 +320,9 @@ class ShortcutPackage extends ShortcutPackageItem {
* @return true if it's actually removed because it wasn't pinned, or false if it's still
* pinned.
*/
- private boolean disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
- int disabledReason) {
+ private boolean disableDynamicWithId(@NonNull String shortcutId) {
final ShortcutInfo disabled = deleteOrDisableWithId(
- shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false, ignoreInvisible,
- disabledReason);
+ shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false);
return disabled == null;
}
@@ -368,10 +331,9 @@ class ShortcutPackage extends ShortcutPackageItem {
* is pinned, it'll remain as a pinned shortcut but will be disabled.
*/
public void disableWithId(@NonNull String shortcutId, String disabledMessage,
- int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
- int disabledReason) {
+ int disabledMessageResId, boolean overrideImmutable) {
final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
- overrideImmutable, ignoreInvisible, disabledReason);
+ overrideImmutable);
if (disabled != null) {
if (disabledMessage != null) {
@@ -386,18 +348,14 @@ class ShortcutPackage extends ShortcutPackageItem {
@Nullable
private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
- boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
- Preconditions.checkState(
- (disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
- "disable and disabledReason disagree: " + disable + " vs " + disabledReason);
+ boolean overrideImmutable) {
final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
- if (oldShortcut == null || !oldShortcut.isEnabled()
- && (ignoreInvisible && !oldShortcut.isVisibleToPublisher())) {
+ if (oldShortcut == null || !oldShortcut.isEnabled()) {
return null; // Doesn't exist or already disabled.
}
if (!overrideImmutable) {
- ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
+ ensureNotImmutable(oldShortcut);
}
if (oldShortcut.isPinned()) {
@@ -405,10 +363,6 @@ class ShortcutPackage extends ShortcutPackageItem {
oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
if (disable) {
oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
- // Do not overwrite the disabled reason if one is alreay set.
- if (oldShortcut.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- oldShortcut.setDisabledReason(disabledReason);
- }
}
oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
@@ -419,7 +373,7 @@ class ShortcutPackage extends ShortcutPackageItem {
return oldShortcut;
} else {
- forceDeleteShortcutInner(shortcutId);
+ deleteShortcutInner(shortcutId);
return null;
}
}
@@ -427,25 +381,11 @@ class ShortcutPackage extends ShortcutPackageItem {
public void enableWithId(@NonNull String shortcutId) {
final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
if (shortcut != null) {
- ensureNotImmutable(shortcut, /*ignoreInvisible=*/ true);
+ ensureNotImmutable(shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
- shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
}
}
- public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
- final ShortcutInfo source = mShortcuts.get(shortcut.getId());
- Preconditions.checkNotNull(source);
-
- mShortcutUser.mService.validateShortcutForPinRequest(shortcut);
-
- shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
-
- forceReplaceShortcutInner(shortcut);
-
- adjustRanks();
- }
-
/**
* Called after a launcher updates the pinned set. For each shortcut in this package,
* set FLAG_PINNED if any launcher has pinned it. Otherwise, clear it.
@@ -570,7 +510,7 @@ class ShortcutPackage extends ShortcutPackageItem {
*/
public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
- findAll(result, query, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
+ findAll(result, query, cloneFlag, null, 0);
}
/**
@@ -582,7 +522,7 @@ class ShortcutPackage extends ShortcutPackageItem {
*/
public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag,
- @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
+ @Nullable String callingLauncher, int launcherUserId) {
if (getPackageInfo().isShadow()) {
// Restored and the app not installed yet, so don't return any.
return;
@@ -604,11 +544,9 @@ class ShortcutPackage extends ShortcutPackageItem {
final boolean isPinnedByCaller = (callingLauncher == null)
|| ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
- if (!getPinnedByAnyLauncher) {
- if (si.isFloating()) {
- if (!isPinnedByCaller) {
- continue;
- }
+ if (si.isFloating()) {
+ if (!isPinnedByCaller) {
+ continue;
}
}
final ShortcutInfo clone = si.clone(cloneFlag);
@@ -755,27 +693,7 @@ class ShortcutPackage extends ShortcutPackageItem {
getPackageInfo().getVersionCode(), pi.versionCode));
}
- getPackageInfo().updateFromPackageInfo(pi);
- final int newVersionCode = getPackageInfo().getVersionCode();
-
- // See if there are any shortcuts that were prevented restoring because the app was of a
- // lower version, and re-enable them.
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
- continue;
- }
- if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Shortcut %s require version %s, still not restored.",
- si.getId(), getPackageInfo().getBackupSourceVersionCode()));
- }
- continue;
- }
- Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
- si.clearFlags(ShortcutInfo.FLAG_DISABLED);
- si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- }
+ getPackageInfo().updateVersionInfo(pi);
// For existing shortcuts, update timestamps if they have any resources.
// Also check if shortcuts' activities are still main activities. Otherwise, disable them.
@@ -795,8 +713,7 @@ class ShortcutPackage extends ShortcutPackageItem {
Slog.w(TAG, String.format(
"%s is no longer main activity. Disabling shorcut %s.",
getPackageName(), si.getId()));
- if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED)) {
+ if (disableDynamicWithId(si.getId())) {
continue; // Actually removed.
}
// Still pinned, so fall-through and possibly update the resources.
@@ -892,7 +809,7 @@ class ShortcutPackage extends ShortcutPackageItem {
// Note even if enabled=false, we still need to update all fields, so do it
// regardless.
- forceReplaceShortcutInner(newShortcut); // This will clean up the old one too.
+ addShortcutInner(newShortcut); // This will clean up the old one too.
if (!newDisabled && toDisableList != null) {
// Still alive, don't remove.
@@ -914,8 +831,7 @@ class ShortcutPackage extends ShortcutPackageItem {
final String id = toDisableList.valueAt(i);
disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
- /* overrideImmutable=*/ true, /*ignoreInvisible=*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED);
+ /* overrideImmutable=*/ true);
}
removeOrphans();
}
@@ -953,7 +869,7 @@ class ShortcutPackage extends ShortcutPackageItem {
service.wtf("Found manifest shortcuts in excess list.");
continue;
}
- deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+ deleteDynamicWithId(shortcut.getId());
}
}
@@ -1159,7 +1075,7 @@ class ShortcutPackage extends ShortcutPackageItem {
if (ret != 0) {
return ret;
}
- // If they're still tie, just sort by their IDs.
+ // If they're stil tie, just sort by their IDs.
// This may happen with updateShortcuts() -- see
// the testUpdateShortcuts_noManifestShortcuts() test.
return a.getId().compareTo(b.getId());
@@ -1341,34 +1257,25 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
- getPackageInfo().saveToXml(out, forBackup);
+ getPackageInfo().saveToXml(out);
for (int j = 0; j < size; j++) {
- saveShortcut(out, mShortcuts.valueAt(j), forBackup,
- getPackageInfo().isBackupAllowed());
+ saveShortcut(out, mShortcuts.valueAt(j), forBackup);
}
out.endTag(null, TAG_ROOT);
}
- private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup,
- boolean appSupportsBackup)
+ private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
throws IOException, XmlPullParserException {
final ShortcutService s = mShortcutUser.mService;
if (forBackup) {
if (!(si.isPinned() && si.isEnabled())) {
- // We only backup pinned shortcuts that are enabled.
- // Note, this means, shortcuts that are restored but are blocked restore, e.g. due
- // to a lower version code, will not be ported to a new device.
- return;
+ return; // We only backup pinned shortcuts that are enabled.
}
}
- final boolean shouldBackupDetails =
- !forBackup // It's not backup
- || appSupportsBackup; // Or, it's a backup and app supports backup.
-
// Note: at this point no shortcuts should have bitmaps pending save, but if they do,
// just remove the bitmap.
if (si.isIconPendingSave()) {
@@ -1385,31 +1292,20 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
- if (shouldBackupDetails) {
- ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
- ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
- si.getDisabledMessageResourceId());
- ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
- si.getDisabledMessageResName());
- }
- ShortcutService.writeAttr(out, ATTR_DISABLED_REASON, si.getDisabledReason());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
+ si.getDisabledMessageResourceId());
+ ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
+ si.getDisabledMessageResName());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
si.getLastChangedTimestamp());
if (forBackup) {
// Don't write icon information. Also drop the dynamic flag.
-
- int flags = si.getFlags() &
- ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
+ ShortcutService.writeAttr(out, ATTR_FLAGS,
+ si.getFlags() &
+ ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE
- | ShortcutInfo.FLAG_DYNAMIC);
- ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
-
- // Set the publisher version code at every backup.
- final int packageVersionCode = getPackageInfo().getVersionCode();
- if (packageVersionCode == 0) {
- s.wtf("Package version code should be available at this point.");
- // However, 0 is a valid version code, so we just go ahead with it...
- }
+ | ShortcutInfo.FLAG_DYNAMIC));
} else {
// When writing for backup, ranks shouldn't be saved, since shortcuts won't be restored
// as dynamic.
@@ -1421,28 +1317,26 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
}
- if (shouldBackupDetails) {
- {
- final Set<String> cat = si.getCategories();
- if (cat != null && cat.size() > 0) {
- out.startTag(null, TAG_CATEGORIES);
- XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
- NAME_CATEGORIES, out);
- out.endTag(null, TAG_CATEGORIES);
- }
- }
- final Intent[] intentsNoExtras = si.getIntentsNoExtras();
- final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
- final int numIntents = intentsNoExtras.length;
- for (int i = 0; i < numIntents; i++) {
- out.startTag(null, TAG_INTENT);
- ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
- ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
- out.endTag(null, TAG_INTENT);
+ {
+ final Set<String> cat = si.getCategories();
+ if (cat != null && cat.size() > 0) {
+ out.startTag(null, TAG_CATEGORIES);
+ XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
+ NAME_CATEGORIES, out);
+ out.endTag(null, TAG_CATEGORIES);
}
-
- ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
}
+ final Intent[] intentsNoExtras = si.getIntentsNoExtras();
+ final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
+ final int numIntents = intentsNoExtras.length;
+ for (int i = 0; i < numIntents; i++) {
+ out.startTag(null, TAG_INTENT);
+ ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
+ ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
+ out.endTag(null, TAG_INTENT);
+ }
+
+ ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
out.endTag(null, TAG_SHORTCUT);
}
@@ -1462,7 +1356,6 @@ class ShortcutPackage extends ShortcutPackageItem {
ret.mLastResetTime =
ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
-
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1476,11 +1369,10 @@ class ShortcutPackage extends ShortcutPackageItem {
switch (tag) {
case ShortcutPackageInfo.TAG_ROOT:
ret.getPackageInfo().loadFromXml(parser, fromBackup);
-
continue;
case TAG_SHORTCUT:
final ShortcutInfo si = parseShortcut(parser, packageName,
- shortcutUser.getUserId(), fromBackup);
+ shortcutUser.getUserId());
// Don't use addShortcut(), we don't need to save the icon.
ret.mShortcuts.put(si.getId(), si);
@@ -1493,8 +1385,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
- @UserIdInt int userId, boolean fromBackup)
- throws IOException, XmlPullParserException {
+ @UserIdInt int userId) throws IOException, XmlPullParserException {
String id;
ComponentName activityComponent;
// Icon icon;
@@ -1507,7 +1398,6 @@ class ShortcutPackage extends ShortcutPackageItem {
String disabledMessage;
int disabledMessageResId;
String disabledMessageResName;
- int disabledReason;
Intent intentLegacy;
PersistableBundle intentPersistableExtrasLegacy = null;
ArrayList<Intent> intents = new ArrayList<>();
@@ -1518,7 +1408,6 @@ class ShortcutPackage extends ShortcutPackageItem {
int iconResId;
String iconResName;
String bitmapPath;
- int backupVersionCode;
ArraySet<String> categories = null;
id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
@@ -1535,7 +1424,6 @@ class ShortcutPackage extends ShortcutPackageItem {
ATTR_DISABLED_MESSAGE_RES_ID);
disabledMessageResName = ShortcutService.parseStringAttribute(parser,
ATTR_DISABLED_MESSAGE_RES_NAME);
- disabledReason = ShortcutService.parseIntAttribute(parser, ATTR_DISABLED_REASON);
intentLegacy = ShortcutService.parseIntentAttributeNoDefault(parser, ATTR_INTENT_LEGACY);
rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
@@ -1592,19 +1480,6 @@ class ShortcutPackage extends ShortcutPackageItem {
intents.add(intentLegacy);
}
-
- if ((disabledReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
- && ((flags & ShortcutInfo.FLAG_DISABLED) != 0)) {
- // We didn't used to have the disabled reason, so if a shortcut is disabled
- // and has no reason, we assume it was disabled by publisher.
- disabledReason = ShortcutInfo.DISABLED_REASON_BY_APP;
- }
-
- // All restored shortcuts are initially "shadow".
- if (fromBackup) {
- flags |= ShortcutInfo.FLAG_SHADOW;
- }
-
return new ShortcutInfo(
userId, id, packageName, activityComponent, /* icon =*/ null,
title, titleResId, titleResName, text, textResId, textResName,
@@ -1612,7 +1487,7 @@ class ShortcutPackage extends ShortcutPackageItem {
categories,
intents.toArray(new Intent[intents.size()]),
rank, extras, lastChangedTimestamp, flags,
- iconResId, iconResName, bitmapPath, disabledReason);
+ iconResId, iconResName, bitmapPath);
}
private static Intent parseIntent(XmlPullParser parser)
@@ -1727,20 +1602,6 @@ class ShortcutPackage extends ShortcutPackageItem {
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " has both resource and bitmap icons");
}
- if (si.isEnabled()
- != (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)) {
- failed = true;
- Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
- + " isEnabled() and getDisabledReason() disagree: "
- + si.isEnabled() + " vs " + si.getDisabledReason());
- }
- if ((si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
- && (getPackageInfo().getBackupSourceVersionCode()
- == ShortcutInfo.VERSION_CODE_UNKNOWN)) {
- failed = true;
- Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
- + " RESTORED_VERSION_LOWER with no backup source version code.");
- }
if (s.isDummyMainActivity(si.getActivity())) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
diff --git a/com/android/server/pm/ShortcutPackageInfo.java b/com/android/server/pm/ShortcutPackageInfo.java
index 3a9bbc89..e5a2f5ac 100644
--- a/com/android/server/pm/ShortcutPackageInfo.java
+++ b/com/android/server/pm/ShortcutPackageInfo.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.PackageInfo;
-import android.content.pm.ShortcutInfo;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,45 +45,32 @@ class ShortcutPackageInfo {
static final String TAG_ROOT = "package-info";
private static final String ATTR_VERSION = "version";
private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
- private static final String ATTR_BACKUP_SOURCE_VERSION = "bk_src_version";
- private static final String ATTR_BACKUP_ALLOWED = "allow-backup";
- private static final String ATTR_BACKUP_SOURCE_BACKUP_ALLOWED = "bk_src_backup-allowed";
private static final String ATTR_SHADOW = "shadow";
private static final String TAG_SIGNATURE = "signature";
private static final String ATTR_SIGNATURE_HASH = "hash";
+ private static final int VERSION_UNKNOWN = -1;
+
/**
* When true, this package information was restored from the previous device, and the app hasn't
* been installed yet.
*/
private boolean mIsShadow;
- private int mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
- private int mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+ private int mVersionCode = VERSION_UNKNOWN;
private long mLastUpdateTime;
private ArrayList<byte[]> mSigHashes;
- // mBackupAllowed didn't used to be parsisted, so we don't restore it from a file.
- // mBackupAllowed will always start with false, and will have been updated before making a
- // backup next time, which works file.
- // We just don't want to print an uninitialzied mBackupAlldowed value on dumpsys, so
- // we use this boolean to control dumpsys.
- private boolean mBackupAllowedInitialized;
- private boolean mBackupAllowed;
- private boolean mBackupSourceBackupAllowed;
-
private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
ArrayList<byte[]> sigHashes, boolean isShadow) {
mVersionCode = versionCode;
mLastUpdateTime = lastUpdateTime;
mIsShadow = isShadow;
mSigHashes = sigHashes;
- mBackupAllowed = false; // By default, we assume false.
- mBackupSourceBackupAllowed = false;
}
public static ShortcutPackageInfo newEmpty() {
- return new ShortcutPackageInfo(ShortcutInfo.VERSION_CODE_UNKNOWN, /* last update time =*/ 0,
+ return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
new ArrayList<>(0), /* isShadow */ false);
}
@@ -100,33 +86,15 @@ class ShortcutPackageInfo {
return mVersionCode;
}
- public int getBackupSourceVersionCode() {
- return mBackupSourceVersionCode;
- }
-
- @VisibleForTesting
- public boolean isBackupSourceBackupAllowed() {
- return mBackupSourceBackupAllowed;
- }
-
public long getLastUpdateTime() {
return mLastUpdateTime;
}
- public boolean isBackupAllowed() {
- return mBackupAllowed;
- }
-
- /**
- * Set {@link #mVersionCode}, {@link #mLastUpdateTime} and {@link #mBackupAllowed}
- * from a {@link PackageInfo}.
- */
- public void updateFromPackageInfo(@NonNull PackageInfo pi) {
+ /** Set {@link #mVersionCode} and {@link #mLastUpdateTime} from a {@link PackageInfo}. */
+ public void updateVersionInfo(@NonNull PackageInfo pi) {
if (pi != null) {
mVersionCode = pi.versionCode;
mLastUpdateTime = pi.lastUpdateTime;
- mBackupAllowed = ShortcutService.shouldBackupApp(pi);
- mBackupAllowedInitialized = true;
}
}
@@ -134,24 +102,23 @@ class ShortcutPackageInfo {
return mSigHashes.size() > 0;
}
- //@DisabledReason
- public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) {
- if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) {
- Slog.w(TAG, "Can't restore: Package signature mismatch");
- return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
- }
- if (!ShortcutService.shouldBackupApp(currentPackage) || !mBackupSourceBackupAllowed) {
+ public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
+ if (!s.shouldBackupApp(target)) {
// "allowBackup" was true when backed up, but now false.
- Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup");
- return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
+ Slog.w(TAG, "Can't restore: package no longer allows backup");
+ return false;
}
- if (!anyVersionOkay && (currentPackage.versionCode < mBackupSourceVersionCode)) {
+ if (target.versionCode < mVersionCode) {
Slog.w(TAG, String.format(
"Can't restore: package current version %d < backed up version %d",
- currentPackage.versionCode, mBackupSourceVersionCode));
- return ShortcutInfo.DISABLED_REASON_VERSION_LOWER;
+ target.versionCode, mVersionCode));
+ return false;
+ }
+ if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
+ Slog.w(TAG, "Can't restore: Package signature mismatch");
+ return false;
}
- return ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
+ return true;
}
@VisibleForTesting
@@ -165,8 +132,6 @@ class ShortcutPackageInfo {
final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
- ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi);
- ret.mBackupSourceVersionCode = pi.versionCode;
return ret;
}
@@ -186,19 +151,13 @@ class ShortcutPackageInfo {
mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
}
- public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException {
+ public void saveToXml(XmlSerializer out) throws IOException {
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
- ShortcutService.writeAttr(out, ATTR_BACKUP_ALLOWED, mBackupAllowed);
-
- ShortcutService.writeAttr(out, ATTR_BACKUP_SOURCE_VERSION, mBackupSourceVersionCode);
- ShortcutService.writeAttr(out,
- ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, mBackupSourceBackupAllowed);
-
for (int i = 0; i < mSigHashes.size(); i++) {
out.startTag(null, TAG_SIGNATURE);
@@ -212,9 +171,7 @@ class ShortcutPackageInfo {
public void loadFromXml(XmlPullParser parser, boolean fromBackup)
throws IOException, XmlPullParserException {
- // Don't use the version code from the backup file.
- final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION,
- ShortcutInfo.VERSION_CODE_UNKNOWN);
+ final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final long lastUpdateTime = ShortcutService.parseLongAttribute(
parser, ATTR_LAST_UPDATE_TIME);
@@ -223,20 +180,6 @@ class ShortcutPackageInfo {
final boolean shadow =
fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
- // We didn't used to save these attributes, and all backed up shortcuts were from
- // apps that support backups, so the default values take this fact into consideration.
- final int backupSourceVersion = ShortcutService.parseIntAttribute(parser,
- ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN);
-
- // Note the only time these "true" default value is used is when restoring from an old
- // build that didn't save ATTR_BACKUP_ALLOWED, and that means all the data included in
- // a backup file were from apps that support backup, so we can just use "true" as the
- // default.
- final boolean backupAllowed = ShortcutService.parseBooleanAttribute(
- parser, ATTR_BACKUP_ALLOWED, true);
- final boolean backupSourceBackupAllowed = ShortcutService.parseBooleanAttribute(
- parser, ATTR_BACKUP_SOURCE_BACKUP_ALLOWED, true);
-
final ArrayList<byte[]> hashes = new ArrayList<>();
final int outerDepth = parser.getDepth();
@@ -264,28 +207,11 @@ class ShortcutPackageInfo {
ShortcutService.warnForInvalidTag(depth, tag);
}
- // Successfully loaded; replace the fields.
- if (fromBackup) {
- mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
- mBackupSourceVersionCode = versionCode;
- mBackupSourceBackupAllowed = backupAllowed;
- } else {
- mVersionCode = versionCode;
- mBackupSourceVersionCode = backupSourceVersion;
- mBackupSourceBackupAllowed = backupSourceBackupAllowed;
- }
+ // Successfully loaded; replace the feilds.
+ mVersionCode = versionCode;
mLastUpdateTime = lastUpdateTime;
mIsShadow = shadow;
mSigHashes = hashes;
-
- // Note we don't restore it from the file because it didn't used to be saved.
- // We always start by assuming backup is disabled for the current package,
- // and this field will have been updated before we actually create a backup, at the same
- // time when we update the version code.
- // Until then, the value of mBackupAllowed shouldn't matter, but we don't want to print
- // a false flag on dumpsys, so set mBackupAllowedInitialized to false.
- mBackupAllowed = false;
- mBackupAllowedInitialized = false;
}
public void dump(PrintWriter pw, String prefix) {
@@ -297,7 +223,6 @@ class ShortcutPackageInfo {
pw.print(prefix);
pw.print(" IsShadow: ");
pw.print(mIsShadow);
- pw.print(mIsShadow ? " (not installed)" : " (installed)");
pw.println();
pw.print(prefix);
@@ -305,25 +230,6 @@ class ShortcutPackageInfo {
pw.print(mVersionCode);
pw.println();
- if (mBackupAllowedInitialized) {
- pw.print(prefix);
- pw.print(" Backup Allowed: ");
- pw.print(mBackupAllowed);
- pw.println();
- }
-
- if (mBackupSourceVersionCode != ShortcutInfo.VERSION_CODE_UNKNOWN) {
- pw.print(prefix);
- pw.print(" Backup source version: ");
- pw.print(mBackupSourceVersionCode);
- pw.println();
-
- pw.print(prefix);
- pw.print(" Backup source backup allowed: ");
- pw.print(mBackupSourceBackupAllowed);
- pw.println();
- }
-
pw.print(prefix);
pw.print(" Last package update time: ");
pw.print(mLastUpdateTime);
diff --git a/com/android/server/pm/ShortcutPackageItem.java b/com/android/server/pm/ShortcutPackageItem.java
index 689099cf..e59d69f4 100644
--- a/com/android/server/pm/ShortcutPackageItem.java
+++ b/com/android/server/pm/ShortcutPackageItem.java
@@ -17,7 +17,6 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.content.pm.PackageInfo;
-import android.content.pm.ShortcutInfo;
import android.util.Slog;
import com.android.internal.util.Preconditions;
@@ -102,42 +101,51 @@ abstract class ShortcutPackageItem {
final ShortcutService s = mShortcutUser.mService;
if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package still not installed: %s/u%d",
+ Slog.d(TAG, String.format("Package still not installed: %s user=%d",
mPackageName, mPackageUserId));
}
return; // Not installed, no need to restore yet.
}
- int restoreBlockReason;
- int currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
-
+ boolean blockRestore = false;
if (!mPackageInfo.hasSignatures()) {
- s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId
+ s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId
+ " but signatures not found in the restore data.");
- restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
- } else {
+ blockRestore = true;
+ }
+ if (!blockRestore) {
final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
- currentVersionCode = pi.versionCode;
- restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion());
+ if (!mPackageInfo.canRestoreTo(s, pi)) {
+ // Package is now installed, but can't restore. Let the subclass do the cleanup.
+ blockRestore = true;
+ }
}
+ if (blockRestore) {
+ onRestoreBlocked();
+ } else {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Restored package: %s/%d on user %d", mPackageName,
+ mPackageUserId, getOwnerUserId()));
+ }
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Restoring package: %s/u%d (version=%d) %s for u%d",
- mPackageName, mPackageUserId, currentVersionCode,
- ShortcutInfo.getDisabledReasonDebugString(restoreBlockReason),
- getOwnerUserId()));
+ onRestored();
}
- onRestored(restoreBlockReason);
-
// Either way, it's no longer a shadow.
mPackageInfo.setShadow(false);
s.scheduleSaveUser(mPackageUserId);
}
- protected abstract boolean canRestoreAnyVersion();
+ /**
+ * Called when the new package can't be restored because it has a lower version number
+ * or different signatures.
+ */
+ protected abstract void onRestoreBlocked();
- protected abstract void onRestored(int restoreBlockReason);
+ /**
+ * Called when the new package is successfully restored.
+ */
+ protected abstract void onRestored();
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
diff --git a/com/android/server/pm/ShortcutParser.java b/com/android/server/pm/ShortcutParser.java
index 866c46c6..3cf4200b 100644
--- a/com/android/server/pm/ShortcutParser.java
+++ b/com/android/server/pm/ShortcutParser.java
@@ -337,9 +337,6 @@ public class ShortcutParser {
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
| ShortcutInfo.FLAG_IMMUTABLE
| ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
- final int disabledReason =
- enabled ? ShortcutInfo.DISABLED_REASON_NOT_DISABLED
- : ShortcutInfo.DISABLED_REASON_BY_APP;
// Note we don't need to set resource names here yet. They'll be set when they're about
// to be published.
@@ -366,7 +363,6 @@ public class ShortcutParser {
flags,
iconResId,
null, // icon res name
- null, // bitmap path
- disabledReason);
+ null); // bitmap path
}
}
diff --git a/com/android/server/pm/ShortcutRequestPinProcessor.java b/com/android/server/pm/ShortcutRequestPinProcessor.java
index 3e44de98..8a8128db 100644
--- a/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -300,12 +300,10 @@ class ShortcutRequestPinProcessor {
final ShortcutInfo existing = ps.findShortcutById(inShortcut.getId());
final boolean existsAlready = existing != null;
- final boolean existingIsVisible = existsAlready && existing.isVisibleToPublisher();
if (DEBUG) {
Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage()
+ " existsAlready=" + existsAlready
- + " existingIsVisible=" + existingIsVisible
+ " shortcut=" + inShortcut.toInsecureString());
}
@@ -380,6 +378,7 @@ class ShortcutRequestPinProcessor {
// manifest shortcut.)
Preconditions.checkArgument(shortcutInfo.isEnabled(),
"Shortcut ID=" + shortcutInfo + " already exists but disabled.");
+
}
private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
@@ -464,7 +463,7 @@ class ShortcutRequestPinProcessor {
launcher.attemptToRestoreIfNeededAndSave();
if (launcher.hasPinned(original)) {
if (DEBUG) {
- Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
+ Slog.d(TAG, "Shortcut " + original + " already pinned.");
}
return true;
}
@@ -498,7 +497,7 @@ class ShortcutRequestPinProcessor {
if (original.getActivity() == null) {
original.setActivity(mService.getDummyMainActivity(appPackageName));
}
- ps.addOrReplaceDynamicShortcut(original);
+ ps.addOrUpdateDynamicShortcut(original);
}
// Pin the shortcut.
@@ -506,14 +505,13 @@ class ShortcutRequestPinProcessor {
Slog.d(TAG, "Pinning " + shortcutId);
}
- launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
- /*forPinRequest=*/ true);
+ launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId);
if (current == null) {
if (DEBUG) {
Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
}
- ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
+ ps.deleteDynamicWithId(shortcutId);
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/com/android/server/pm/ShortcutService.java b/com/android/server/pm/ShortcutService.java
index 1c002aa4..27560c5f 100644
--- a/com/android/server/pm/ShortcutService.java
+++ b/com/android/server/pm/ShortcutService.java
@@ -553,9 +553,6 @@ public class ShortcutService extends IShortcutService.Stub {
public Lifecycle(Context context) {
super(context);
- if (DEBUG) {
- Binder.LOG_RUNTIME_EXCEPTION = true;
- }
mService = new ShortcutService(context);
}
@@ -741,10 +738,6 @@ public class ShortcutService extends IShortcutService.Stub {
return parseLongAttribute(parser, attribute) == 1;
}
- static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) {
- return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1;
- }
-
static int parseIntAttribute(XmlPullParser parser, String attribute) {
return (int) parseLongAttribute(parser, attribute);
}
@@ -842,8 +835,6 @@ public class ShortcutService extends IShortcutService.Stub {
static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
if (value) {
writeAttr(out, name, "1");
- } else {
- writeAttr(out, name, "0");
}
}
@@ -1698,7 +1689,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
fillInDefaultActivity(newShortcuts);
@@ -1718,12 +1709,12 @@ public class ShortcutService extends IShortcutService.Stub {
}
// First, remove all un-pinned; dynamic shortcuts
- ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ ps.deleteAllDynamicShortcuts();
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
final ShortcutInfo newShortcut = newShortcuts.get(i);
- ps.addOrReplaceDynamicShortcut(newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
}
// Lastly, adjust the ranks.
@@ -1749,7 +1740,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
// For update, don't fill in the default activity. Having null activity means
// "don't update the activity" here.
@@ -1770,9 +1761,7 @@ public class ShortcutService extends IShortcutService.Stub {
fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
final ShortcutInfo target = ps.findShortcutById(source.getId());
-
- // Invisible shortcuts can't be updated.
- if (target == null || !target.isVisibleToPublisher()) {
+ if (target == null) {
continue;
}
@@ -1819,7 +1808,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
- public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
@@ -1831,7 +1820,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
fillInDefaultActivity(newShortcuts);
@@ -1856,7 +1845,7 @@ public class ShortcutService extends IShortcutService.Stub {
newShortcut.setRankChanged();
// Add it.
- ps.addOrReplaceDynamicShortcut(newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
}
// Lastly, adjust the ranks.
@@ -1912,22 +1901,6 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
"Calling application must have a foreground activity or a foreground service");
- // If it's a pin shortcut request, and there's already a shortcut with the same ID
- // that's not visible to the caller (i.e. restore-blocked; meaning it's pinned by
- // someone already), then we just replace the existing one with this new one,
- // and then proceed the rest of the process.
- if (shortcut != null) {
- final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(
- packageName, userId);
- final String id = shortcut.getId();
- if (ps.isShortcutExistsAndInvisibleToPublisher(id)) {
-
- ps.updateInvisibleShortcutForPinRequestWith(shortcut);
-
- packageShortcutsChanged(packageName, userId);
- }
- }
-
// Send request to the launcher, if supported.
ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, extras,
userId, resultIntent);
@@ -1949,21 +1922,15 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
- /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
final String disabledMessageString =
(disabledMessage == null) ? null : disabledMessage.toString();
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
- if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
- continue;
- }
- ps.disableWithId(id,
+ ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
disabledMessageString, disabledMessageResId,
- /* overrideImmutable=*/ false, /*ignoreInvisible=*/ true,
- ShortcutInfo.DISABLED_REASON_BY_APP);
+ /* overrideImmutable=*/ false);
}
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
@@ -1984,15 +1951,10 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
- /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
- if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
- continue;
- }
- ps.enableWithId(id);
+ ps.enableWithId((String) shortcutIds.get(i));
}
}
packageShortcutsChanged(packageName, userId);
@@ -2011,15 +1973,11 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds,
- /*ignoreInvisible=*/ true);
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
- if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
- continue;
- }
- ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ ps.deleteDynamicWithId(
+ Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
}
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
@@ -2038,7 +1996,7 @@ public class ShortcutService extends IShortcutService.Stub {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ ps.deleteAllDynamicShortcuts();
}
packageShortcutsChanged(packageName, userId);
@@ -2055,7 +2013,7 @@ public class ShortcutService extends IShortcutService.Stub {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isDynamicVisible);
+ ShortcutInfo::isDynamic);
}
}
@@ -2069,7 +2027,7 @@ public class ShortcutService extends IShortcutService.Stub {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isManifestVisible);
+ ShortcutInfo::isManifestShortcut);
}
}
@@ -2083,7 +2041,7 @@ public class ShortcutService extends IShortcutService.Stub {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isPinnedVisible);
+ ShortcutInfo::isPinned);
}
}
@@ -2232,11 +2190,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
// We override this method in unit tests to do a simpler check.
- boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId,
- int callingPid, int callingUid) {
- if (injectCheckAccessShortcutsPermission(callingPid, callingUid)) {
- return true;
- }
+ boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
final long start = injectElapsedRealtime();
try {
return hasShortcutHostPermissionInner(callingPackage, userId);
@@ -2245,14 +2199,6 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
- /**
- * Returns true if the caller has the "ACCESS_SHORTCUTS" permission.
- */
- boolean injectCheckAccessShortcutsPermission(int callingPid, int callingUid) {
- return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
- }
-
// This method is extracted so we can directly call this method from unit tests,
// even when hasShortcutPermission() is overridden.
@VisibleForTesting
@@ -2433,7 +2379,7 @@ public class ShortcutService extends IShortcutService.Stub {
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName,
- int queryFlags, int userId, int callingPid, int callingUid) {
+ int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final boolean cloneKeyFieldOnly =
@@ -2454,15 +2400,13 @@ public class ShortcutService extends IShortcutService.Stub {
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, shortcutIds, changedSince,
- componentName, queryFlags, userId, ret, cloneFlag,
- callingPid, callingUid);
+ componentName, queryFlags, userId, ret, cloneFlag);
} else {
final List<String> shortcutIdsF = shortcutIds;
getUserShortcutsLocked(userId).forAllPackages(p -> {
getShortcutsInnerLocked(launcherUserId,
callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
- componentName, queryFlags, userId, ret, cloneFlag,
- callingPid, callingUid);
+ componentName, queryFlags, userId, ret, cloneFlag);
});
}
}
@@ -2472,8 +2416,7 @@ public class ShortcutService extends IShortcutService.Stub {
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
- int userId, ArrayList<ShortcutInfo> ret, int cloneFlag,
- int callingPid, int callingUid) {
+ int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
final ArraySet<String> ids = shortcutIds == null ? null
: new ArraySet<>(shortcutIds);
@@ -2482,13 +2425,6 @@ public class ShortcutService extends IShortcutService.Stub {
if (p == null) {
return; // No need to instantiate ShortcutPackage.
}
- final boolean matchDynamic = (queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
- final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
- final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
-
- final boolean getPinnedByAnyLauncher =
- ((queryFlags & ShortcutQuery.FLAG_MATCH_ALL_PINNED) != 0)
- && injectCheckAccessShortcutsPermission(callingPid, callingUid);
p.findAll(ret,
(ShortcutInfo si) -> {
@@ -2504,17 +2440,20 @@ public class ShortcutService extends IShortcutService.Stub {
return false;
}
}
- if (matchDynamic && si.isDynamic()) {
+ if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
+ && si.isDynamic()) {
return true;
}
- if ((matchPinned && si.isPinned()) || getPinnedByAnyLauncher) {
+ if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
+ && si.isPinned()) {
return true;
}
- if (matchManifest && si.isDeclaredInManifest()) {
+ if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
+ && si.isManifestShortcut()) {
return true;
}
return false;
- }, cloneFlag, callingPackage, launcherUserId, getPinnedByAnyLauncher);
+ }, cloneFlag, callingPackage, launcherUserId);
}
@Override
@@ -2531,16 +2470,14 @@ public class ShortcutService extends IShortcutService.Stub {
.attemptToRestoreIfNeededAndSave();
final ShortcutInfo si = getShortcutInfoLocked(
- launcherUserId, callingPackage, packageName, shortcutId, userId,
- /*getPinnedByAnyLauncher=*/ false);
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
return si != null && si.isPinned();
}
}
private ShortcutInfo getShortcutInfoLocked(
int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- boolean getPinnedByAnyLauncher) {
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
@@ -2556,7 +2493,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
p.findAll(list,
(ShortcutInfo si) -> shortcutId.equals(si.getId()),
- /* clone flags=*/ 0, callingPackage, launcherUserId, getPinnedByAnyLauncher);
+ /* clone flags=*/ 0, callingPackage, launcherUserId);
return list.size() == 0 ? null : list.get(0);
}
@@ -2576,7 +2513,7 @@ public class ShortcutService extends IShortcutService.Stub {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
launcher.attemptToRestoreIfNeededAndSave();
- launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
+ launcher.pinShortcuts(userId, packageName, shortcutIds);
}
packageShortcutsChanged(packageName, userId);
@@ -2586,8 +2523,7 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public Intent[] createShortcutIntents(int launcherUserId,
@NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- int callingPid, int callingUid) {
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
@@ -2599,13 +2535,9 @@ public class ShortcutService extends IShortcutService.Stub {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
- final boolean getPinnedByAnyLauncher =
- injectCheckAccessShortcutsPermission(callingPid, callingUid);
-
// Make sure the shortcut is actually visible to the launcher.
final ShortcutInfo si = getShortcutInfoLocked(
- launcherUserId, callingPackage, packageName, shortcutId, userId,
- getPinnedByAnyLauncher);
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
// "si == null" should suffice here, but check the flags too just to make sure.
if (si == null || !si.isEnabled() || !si.isAlive()) {
Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
@@ -2691,9 +2623,8 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public boolean hasShortcutHostPermission(int launcherUserId,
- @NonNull String callingPackage, int callingPid, int callingUid) {
- return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId,
- callingPid, callingUid);
+ @NonNull String callingPackage) {
+ return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
@Override
@@ -3412,7 +3343,7 @@ public class ShortcutService extends IShortcutService.Stub {
return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
}
- static boolean shouldBackupApp(PackageInfo pi) {
+ boolean shouldBackupApp(PackageInfo pi) {
return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
}
@@ -3440,7 +3371,7 @@ public class ShortcutService extends IShortcutService.Stub {
// Set the version code for the launchers.
// We shouldn't do this for publisher packages, because we don't want to update the
// version code without rescanning the manifest.
- user.forAllLaunchers(launcher -> launcher.ensurePackageInfo());
+ user.forAllLaunchers(launcher -> launcher.ensureVersionInfo());
// Save to the filesystem.
scheduleSaveUser(userId);
diff --git a/com/android/server/pm/ShortcutUser.java b/com/android/server/pm/ShortcutUser.java
index 48eccd02..55e6d28a 100644
--- a/com/android/server/pm/ShortcutUser.java
+++ b/com/android/server/pm/ShortcutUser.java
@@ -364,6 +364,9 @@ class ShortcutUser {
private void saveShortcutPackageItem(XmlSerializer out,
ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
if (forBackup) {
+ if (!mService.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
+ return; // Don't save.
+ }
if (spi.getPackageUserId() != spi.getOwnerUserId()) {
return; // Don't save cross-user information.
}
diff --git a/com/android/server/pm/UserRestrictionsUtils.java b/com/android/server/pm/UserRestrictionsUtils.java
index c18a71d3..a6b05d71 100644
--- a/com/android/server/pm/UserRestrictionsUtils.java
+++ b/com/android/server/pm/UserRestrictionsUtils.java
@@ -96,7 +96,6 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_FUN,
UserManager.DISALLOW_CREATE_WINDOWS,
- UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_WALLPAPER,
@@ -157,7 +156,6 @@ public class UserRestrictionsUtils {
private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_BLUETOOTH_SHARING,
- UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_RUN_IN_BACKGROUND,
UserManager.DISALLOW_UNMUTE_MICROPHONE,
UserManager.DISALLOW_UNMUTE_DEVICE
diff --git a/com/android/server/pm/permission/BasePermission.java b/com/android/server/pm/permission/BasePermission.java
index 71d3202d..09a6e9c0 100644
--- a/com/android/server/pm/permission/BasePermission.java
+++ b/com/android/server/pm/permission/BasePermission.java
@@ -48,7 +48,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -78,7 +77,7 @@ public final class BasePermission {
final String name;
- final @PermissionType int type;
+ @PermissionType final int type;
String sourcePackageName;
@@ -253,12 +252,12 @@ public final class BasePermission {
return changed;
}
- public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
+ public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) {
if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ getName() + " pkg=" + getSourcePackageName()
+ " info=" + pendingPermissionInfo);
if (sourcePackageSetting == null && pendingPermissionInfo != null) {
- final BasePermission tree = findPermissionTree(permissionTrees, name);
+ final BasePermission tree = findPermissionTreeLP(permissionTrees, name);
if (tree != null && tree.perm != null) {
sourcePackageSetting = tree.sourcePackageSetting;
perm = new PackageParser.Permission(tree.perm.owner,
@@ -270,8 +269,8 @@ public final class BasePermission {
}
}
- static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
- @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
+ public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
+ @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees,
boolean chatty) {
final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
// Allow system apps to redefine non-system permissions
@@ -301,7 +300,7 @@ public final class BasePermission {
if (bp.perm == null) {
if (bp.sourcePackageName == null
|| bp.sourcePackageName.equals(p.info.packageName)) {
- final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
+ final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
if (tree == null
|| tree.sourcePackageName.equals(p.info.packageName)) {
bp.sourcePackageSetting = pkgSetting;
@@ -346,12 +345,12 @@ public final class BasePermission {
return bp;
}
- static BasePermission enforcePermissionTree(
- Collection<BasePermission> permissionTrees, String permName, int callingUid) {
+ public static BasePermission enforcePermissionTreeLP(
+ Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
if (permName != null) {
- BasePermission bp = findPermissionTree(permissionTrees, permName);
+ BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
if (bp != null) {
- if (bp.uid == UserHandle.getAppId(callingUid)) {
+ if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid " + callingUid
@@ -374,9 +373,9 @@ public final class BasePermission {
}
}
- private static BasePermission findPermissionTree(
- Collection<BasePermission> permissionTrees, String permName) {
- for (BasePermission bp : permissionTrees) {
+ private static BasePermission findPermissionTreeLP(
+ Map<String, BasePermission> permissionTrees, String permName) {
+ for (BasePermission bp : permissionTrees.values()) {
if (permName.startsWith(bp.name) &&
permName.length() > bp.name.length() &&
permName.charAt(bp.name.length()) == '.') {
diff --git a/com/android/server/pm/permission/PermissionManagerInternal.java b/com/android/server/pm/permission/PermissionManagerInternal.java
index 8aac52ae..3b20b42b 100644
--- a/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -31,7 +31,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Internal interfaces to be used by other components within the system server.
@@ -82,26 +81,11 @@ public abstract class PermissionManagerInternal {
@NonNull int[] allUserIds);
- /**
- * Add all permissions in the given package.
- * <p>
- * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
- * the permission settings.
- */
- public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
- public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
- public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
+ public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
int callingUid, @Nullable PermissionCallback callback);
- public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
+ public abstract void removePermission(@NonNull String permName, int callingUid,
@Nullable PermissionCallback callback);
- public abstract int updatePermissions(@Nullable String changingPkg,
- @Nullable PackageParser.Package pkgInfo, int flags);
- public abstract int updatePermissionTrees(@Nullable String changingPkg,
- @Nullable PackageParser.Package pkgInfo, int flags);
-
- public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
-
public abstract int getPermissionFlags(@NonNull String permName,
@NonNull String packageName, int callingUid, int userId);
/**
@@ -114,6 +98,8 @@ public abstract class PermissionManagerInternal {
*/
public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
@PermissionInfoFlags int flags, int callingUid);
+ public abstract boolean isPermissionAppOp(@NonNull String permName);
+ public abstract boolean isPermissionInstant(@NonNull String permName);
/**
* Updates the flags associated with a permission by replacing the flags in
diff --git a/com/android/server/pm/permission/PermissionManagerService.java b/com/android/server/pm/permission/PermissionManagerService.java
index d2d857ca..6c031a6a 100644
--- a/com/android/server/pm/permission/PermissionManagerService.java
+++ b/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,7 +69,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
/**
* Manages all permissions and handles permissions related tasks.
@@ -261,7 +260,7 @@ public class PermissionManagerService {
// }
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
- for (BasePermission bp : mSettings.mPermissions.values()) {
+ for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
if (pi != null) {
out.add(pi);
@@ -306,98 +305,7 @@ public class PermissionManagerService {
return protectionLevel;
}
- private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
- final int N = pkg.permissions.size();
- for (int i=0; i<N; i++) {
- PackageParser.Permission p = pkg.permissions.get(i);
-
- // Assume by default that we did not install this permission into the system.
- p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
-
- // Now that permission groups have a special meaning, we ignore permission
- // groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just because they happen
- // to be in a group defined by another app (before this had no implications).
- if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group);
- // Warn for a permission in an unknown group.
- if (PackageManagerService.DEBUG_PERMISSIONS
- && p.info.group != null && p.group == null) {
- Slog.i(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " in an unknown group " + p.info.group);
- }
- }
-
- synchronized (PermissionManagerService.this.mLock) {
- if (p.tree) {
- final BasePermission bp = BasePermission.createOrUpdate(
- mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
- mSettings.getAllPermissionTreesLocked(), chatty);
- mSettings.putPermissionTreeLocked(p.info.name, bp);
- } else {
- final BasePermission bp = BasePermission.createOrUpdate(
- mSettings.getPermissionLocked(p.info.name),
- p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
- mSettings.putPermissionLocked(p.info.name, bp);
- }
- }
- }
- }
-
- private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
- synchronized (mLock) {
- int N = pkg.permissions.size();
- StringBuilder r = null;
- for (int i=0; i<N; i++) {
- PackageParser.Permission p = pkg.permissions.get(i);
- BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
- if (bp == null) {
- bp = mSettings.mPermissionTrees.get(p.info.name);
- }
- if (bp != null && bp.isPermission(p)) {
- bp.setPermission(null);
- if (PackageManagerService.DEBUG_REMOVE && chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
- }
- if (p.isAppOp()) {
- ArraySet<String> appOpPkgs =
- mSettings.mAppOpPermissionPackages.get(p.info.name);
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.packageName);
- }
- }
- }
- if (r != null) {
- if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
- }
-
- N = pkg.requestedPermissions.size();
- r = null;
- for (int i=0; i<N; i++) {
- String perm = pkg.requestedPermissions.get(i);
- if (mSettings.isPermissionAppOp(perm)) {
- ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.packageName);
- if (appOpPkgs.isEmpty()) {
- mSettings.mAppOpPermissionPackages.remove(perm);
- }
- }
- }
- }
- if (r != null) {
- if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
- }
- }
- }
-
- private boolean addDynamicPermission(
+ private boolean addPermission(
PermissionInfo info, int callingUid, PermissionCallback callback) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant apps can't add permissions");
@@ -405,7 +313,8 @@ public class PermissionManagerService {
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
- final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
+ final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ info.name, callingUid);
final boolean added;
final boolean changed;
synchronized (mLock) {
@@ -417,8 +326,8 @@ public class PermissionManagerService {
bp = new BasePermission(info.name, tree.getSourcePackageName(),
BasePermission.TYPE_DYNAMIC);
} else if (bp.isDynamic()) {
- // TODO: switch this back to SecurityException
- Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ info.name);
}
changed = bp.addToTree(fixedLevel, info, tree);
@@ -432,20 +341,21 @@ public class PermissionManagerService {
return added;
}
- private void removeDynamicPermission(
+ private void removePermission(
String permName, int callingUid, PermissionCallback callback) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
- final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
+ final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ permName, callingUid);
synchronized (mLock) {
final BasePermission bp = mSettings.getPermissionLocked(permName);
if (bp == null) {
return;
}
if (bp.isDynamic()) {
- // TODO: switch this back to SecurityException
- Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ permName);
}
mSettings.removePermissionLocked(permName);
@@ -803,21 +713,7 @@ public class PermissionManagerService {
return runtimePermissionChangedUserIds;
}
- private String[] getAppOpPermissionPackages(String permName) {
- if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- synchronized (mLock) {
- final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
- if (pkgs == null) {
- return null;
- }
- return pkgs.toArray(new String[pkgs.size()]);
- }
- }
-
- private int getPermissionFlags(
- String permName, String packageName, int callingUid, int userId) {
+ private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
if (!mUserManagerInt.exists(userId)) {
return 0;
}
@@ -845,96 +741,6 @@ public class PermissionManagerService {
return permissionsState.getPermissionFlags(permName, userId);
}
- private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) {
- Set<BasePermission> needsUpdate = null;
- synchronized (mLock) {
- final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.isDynamic()) {
- bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
- }
- if (bp.getSourcePackageSetting() != null) {
- if (packageName != null && packageName.equals(bp.getSourcePackageName())
- && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
- Slog.i(TAG, "Removing old permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
- it.remove();
- }
- continue;
- }
- if (needsUpdate == null) {
- needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
- }
- needsUpdate.add(bp);
- }
- }
- if (needsUpdate != null) {
- for (final BasePermission bp : needsUpdate) {
- final PackageParser.Package pkg =
- mPackageManagerInt.getPackage(bp.getSourcePackageName());
- synchronized (mLock) {
- if (pkg != null && pkg.mExtras != null) {
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(ps);
- }
- continue;
- }
- Slog.w(TAG, "Removing dangling permission: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- mSettings.removePermissionLocked(bp.getName());
- }
- }
- }
- return flags;
- }
-
- private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo,
- int flags) {
- Set<BasePermission> needsUpdate = null;
- synchronized (mLock) {
- final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.getSourcePackageSetting() != null) {
- if (packageName != null && packageName.equals(bp.getSourcePackageName())
- && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
- Slog.i(TAG, "Removing old permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
- it.remove();
- }
- continue;
- }
- if (needsUpdate == null) {
- needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
- }
- needsUpdate.add(bp);
- }
- }
- if (needsUpdate != null) {
- for (final BasePermission bp : needsUpdate) {
- final PackageParser.Package pkg =
- mPackageManagerInt.getPackage(bp.getSourcePackageName());
- synchronized (mLock) {
- if (pkg != null && pkg.mExtras != null) {
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(ps);
- }
- continue;
- }
- Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- mSettings.removePermissionLocked(bp.getName());
- }
- }
- }
- return flags;
- }
-
private void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, PermissionCallback callback) {
if (!mUserManagerInt.exists(userId)) {
@@ -1066,7 +872,7 @@ public class PermissionManagerService {
private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
int size = 0;
- for (BasePermission perm : mSettings.mPermissions.values()) {
+ for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
size += tree.calculateFootprint(perm);
}
return size;
@@ -1083,15 +889,6 @@ public class PermissionManagerService {
}
}
- private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
- for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
- if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
- return true;
- }
- }
- return false;
- }
-
/**
* Get the first event id for the permission.
*
@@ -1154,22 +951,14 @@ public class PermissionManagerService {
private class PermissionManagerInternalImpl extends PermissionManagerInternal {
@Override
- public void addAllPermissions(Package pkg, boolean chatty) {
- PermissionManagerService.this.addAllPermissions(pkg, chatty);
- }
- @Override
- public void removeAllPermissions(Package pkg, boolean chatty) {
- PermissionManagerService.this.removeAllPermissions(pkg, chatty);
- }
- @Override
- public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
+ public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
PermissionCallback callback) {
- return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
+ return PermissionManagerService.this.addPermission(info, callingUid, callback);
}
@Override
- public void removeDynamicPermission(String permName, int callingUid,
+ public void removePermission(String permName, int callingUid,
PermissionCallback callback) {
- PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
+ PermissionManagerService.this.removePermission(permName, callingUid, callback);
}
@Override
public void grantRuntimePermission(String permName, String packageName,
@@ -1204,26 +993,12 @@ public class PermissionManagerService {
(SharedUserSetting) suSetting, allUserIds);
}
@Override
- public String[] getAppOpPermissionPackages(String permName) {
- return PermissionManagerService.this.getAppOpPermissionPackages(permName);
- }
- @Override
public int getPermissionFlags(String permName, String packageName, int callingUid,
int userId) {
return PermissionManagerService.this.getPermissionFlags(permName, packageName,
callingUid, userId);
}
@Override
- public int updatePermissions(String packageName,
- PackageParser.Package pkgInfo, int flags) {
- return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags);
- }
- @Override
- public int updatePermissionTrees(String packageName,
- PackageParser.Package pkgInfo, int flags) {
- return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags);
- }
- @Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, PermissionCallback callback) {
PermissionManagerService.this.updatePermissionFlags(
@@ -1263,6 +1038,20 @@ public class PermissionManagerService {
return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
}
@Override
+ public boolean isPermissionInstant(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ return (bp != null && bp.isInstant());
+ }
+ }
+ @Override
+ public boolean isPermissionAppOp(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ return (bp != null && bp.isAppOp());
+ }
+ }
+ @Override
public PermissionSettings getPermissionSettings() {
return mSettings;
}
diff --git a/com/android/server/pm/permission/PermissionSettings.java b/com/android/server/pm/permission/PermissionSettings.java
index 7d125c9e..7a2e5ecc 100644
--- a/com/android/server/pm/permission/PermissionSettings.java
+++ b/com/android/server/pm/permission/PermissionSettings.java
@@ -24,7 +24,6 @@ import android.util.ArraySet;
import android.util.Log;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
@@ -36,7 +35,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
-import java.util.Set;
/**
* Permissions and other related data. This class is not meant for
@@ -51,25 +49,8 @@ public class PermissionSettings {
* All of the permissions known to the system. The mapping is from permission
* name to permission object.
*/
- @GuardedBy("mLock")
- final ArrayMap<String, BasePermission> mPermissions =
+ private final ArrayMap<String, BasePermission> mPermissions =
new ArrayMap<String, BasePermission>();
-
- /**
- * All permission trees known to the system. The mapping is from permission tree
- * name to permission object.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, BasePermission> mPermissionTrees =
- new ArrayMap<String, BasePermission>();
-
- /**
- * Set of packages that request a particular app op. The mapping is from permission
- * name to package names.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
-
private final Object mLock;
PermissionSettings(@NonNull Context context, @NonNull Object lock) {
@@ -84,23 +65,15 @@ public class PermissionSettings {
}
}
- public void addAppOpPackage(String permName, String packageName) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName);
- if (pkgs == null) {
- pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(permName, pkgs);
- }
- pkgs.add(packageName);
- }
-
/**
* Transfers ownership of permissions from one package to another.
*/
- public void transferPermissions(String origPackageName, String newPackageName) {
+ public void transferPermissions(String origPackageName, String newPackageName,
+ ArrayMap<String, BasePermission> permissionTrees) {
synchronized (mLock) {
for (int i=0; i<2; i++) {
ArrayMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
+ i == 0 ? permissionTrees : mPermissions;
for (BasePermission bp : permissions.values()) {
bp.transfer(origPackageName, newPackageName);
}
@@ -121,26 +94,9 @@ public class PermissionSettings {
}
}
- public void readPermissionTrees(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- synchronized (mLock) {
- readPermissions(mPermissionTrees, parser);
- }
- }
-
public void writePermissions(XmlSerializer serializer) throws IOException {
- synchronized (mLock) {
- for (BasePermission bp : mPermissions.values()) {
- bp.writeLPr(serializer);
- }
- }
- }
-
- public void writePermissionTrees(XmlSerializer serializer) throws IOException {
- synchronized (mLock) {
- for (BasePermission bp : mPermissionTrees.values()) {
- bp.writeLPr(serializer);
- }
+ for (BasePermission bp : mPermissions.values()) {
+ bp.writeLPr(serializer);
}
}
@@ -172,22 +128,6 @@ public class PermissionSettings {
printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
externalStorageEnforced, printedSomething, dumpState);
}
- if (packageName == null && permissionNames == null) {
- for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) {
- if (iperm == 0) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("AppOp Permissions:");
- }
- pw.print(" AppOp Permission ");
- pw.print(mAppOpPermissionPackages.keyAt(iperm));
- pw.println(":");
- ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
- for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
- pw.print(" "); pw.println(pkgs.valueAt(ipkg));
- }
- }
- }
}
}
@@ -195,58 +135,15 @@ public class PermissionSettings {
return mPermissions.get(permName);
}
- @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
- return mPermissionTrees.get(permName);
- }
-
void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissions.put(permName, permission);
}
- void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
- mPermissionTrees.put(permName, permission);
- }
-
void removePermissionLocked(@NonNull String permName) {
mPermissions.remove(permName);
}
- void removePermissionTreeLocked(@NonNull String permName) {
- mPermissionTrees.remove(permName);
- }
-
- @NonNull Collection<BasePermission> getAllPermissionsLocked() {
+ Collection<BasePermission> getAllPermissionsLocked() {
return mPermissions.values();
}
-
- @NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
- return mPermissionTrees.values();
- }
-
- /**
- * Returns the permission tree for the given permission.
- * @throws SecurityException If the calling UID is not allowed to add permissions to the
- * found permission tree.
- */
- @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) {
- synchronized (mLock) {
- return BasePermission.enforcePermissionTree(
- mPermissionTrees.values(), permName, callingUid);
- }
- }
-
- public boolean isPermissionInstant(String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && bp.isInstant());
- }
- }
-
- boolean isPermissionAppOp(String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && bp.isAppOp());
- }
- }
-
}
diff --git a/com/android/server/policy/GlobalActions.java b/com/android/server/policy/GlobalActions.java
index 7a2e630c..342ec4b7 100644
--- a/com/android/server/policy/GlobalActions.java
+++ b/com/android/server/policy/GlobalActions.java
@@ -58,9 +58,6 @@ class GlobalActions implements GlobalActionsListener {
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
- if (mStatusBarInternal.isGlobalActionsDisabled()) {
- return;
- }
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = deviceProvisioned;
mShowing = true;
diff --git a/com/android/server/policy/PhoneWindowManager.java b/com/android/server/policy/PhoneWindowManager.java
index ceb0ad07..db7817ec 100644
--- a/com/android/server/policy/PhoneWindowManager.java
+++ b/com/android/server/policy/PhoneWindowManager.java
@@ -18,14 +18,13 @@ package com.android.server.policy;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.DISPLAY_SERVICE;
@@ -553,6 +552,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
+ boolean mAccelerometerDefault;
boolean mSupportAutoRotation;
int mAllowAllRotations = -1;
@@ -707,6 +707,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent mVrHeadsetHomeIntent;
boolean mSearchKeyShortcutPending;
boolean mConsumeSearchKeyUp;
+ boolean mAssistKeyLongPressed;
boolean mPendingMetaAction;
boolean mPendingCapsLockToggle;
int mMetaState;
@@ -837,8 +838,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 24;
private static final int MSG_SYSTEM_KEY_PRESS = 25;
private static final int MSG_HANDLE_ALL_APPS = 26;
- private static final int MSG_LAUNCH_ASSIST = 27;
- private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 28;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -880,16 +879,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case MSG_HIDE_BOOT_MESSAGE:
handleHideBootMessage();
break;
- case MSG_LAUNCH_ASSIST:
- final int deviceId = msg.arg1;
- final String hint = (String) msg.obj;
- launchAssistAction(hint, deviceId);
- break;
- case MSG_LAUNCH_ASSIST_LONG_PRESS:
- launchAssistLongPressAction();
- break;
case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
- launchVoiceAssistWithWakeLock();
+ launchVoiceAssistWithWakeLock(msg.arg1 != 0);
break;
case MSG_POWER_DELAYED_PRESS:
powerPress((Long)msg.obj, msg.arg1 != 0, msg.arg2);
@@ -919,7 +910,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
disposeInputConsumer((InputConsumer) msg.obj);
break;
case MSG_BACK_DELAYED_PRESS:
- backMultiPressAction(msg.arg1);
+ backMultiPressAction((Long) msg.obj, msg.arg1);
finishBackKeyPress();
break;
case MSG_ACCESSIBILITY_SHORTCUT:
@@ -1423,7 +1414,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void backMultiPressAction(int count) {
+ private void backMultiPressAction(long eventTime, int count) {
if (count >= PANIC_PRESS_BACK_COUNT) {
switch (mPanicPressOnBackBehavior) {
case PANIC_PRESS_BACK_NOTHING:
@@ -1592,7 +1583,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void sleepPress() {
+ private void sleepPress(long eventTime) {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
}
@@ -2279,11 +2270,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
// http://developer.android.com/guide/practices/screens_support.html#range
- // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
- // so if the orientation is forced, we need to respect that no matter what.
- boolean isCar = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar) &&
+ mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 &&
res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
@@ -2857,7 +2844,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean keyguardLocked = isKeyguardLocked();
boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
- && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
|| hideDockDivider;
}
@@ -3550,11 +3537,44 @@ public class PhoneWindowManager implements WindowManagerPolicy {
toggleKeyboardShortcutsMenu(event.getDeviceId());
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
- Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
+ if (down) {
+ if (repeatCount == 0) {
+ mAssistKeyLongPressed = false;
+ } else if (repeatCount == 1) {
+ mAssistKeyLongPressed = true;
+ if (!keyguardOn) {
+ launchAssistLongPressAction();
+ }
+ }
+ } else {
+ if (mAssistKeyLongPressed) {
+ mAssistKeyLongPressed = false;
+ } else {
+ if (!keyguardOn) {
+ launchAssistAction(null, event.getDeviceId());
+ }
+ }
+ }
return -1;
} else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) {
- Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing");
- return -1;
+ if (!down) {
+ Intent voiceIntent;
+ if (!keyguardOn) {
+ voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+ } else {
+ IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ if (dic != null) {
+ try {
+ dic.exitIdle("voice-search");
+ } catch (RemoteException e) {
+ }
+ }
+ voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
+ }
+ startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
+ }
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
if (down && repeatCount == 0) {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -5425,10 +5445,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
- final int windowingMode = win.getWindowingMode();
- final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
- windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
mForceStatusBar = true;
@@ -5447,7 +5464,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// represent should be hidden or if we should hide the lockscreen. For attached app
// windows we defer the decision to the window it is attached to.
if (appWindow && attached == null) {
- if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+ if (attrs.isFullscreen() && StackId.normallyFullscreenWindows(stackId)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -5478,7 +5495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Keep track of the window if it's dimming but not necessarily fullscreen.
if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
- && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
+ && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
@@ -5486,7 +5503,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// separately, because both the "real fullscreen" opaque window and the one for the docked
// stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
- && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ && attrs.isFullscreen() && stackId == DOCKED_STACK_ID) {
mTopDockedOpaqueWindowState = win;
if (mTopDockedOpaqueOrDimmingWindowState == null) {
mTopDockedOpaqueOrDimmingWindowState = win;
@@ -5496,7 +5513,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Also keep track of any windows that are dimming but not necessarily fullscreen in the
// docked stack.
if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
- && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ && stackId == DOCKED_STACK_ID) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
@@ -5591,9 +5608,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
} else if (topIsFullscreen
- && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM)
- && !mWindowManagerInternal.isStackVisible(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+ && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
+ && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBarController.setBarShowingLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -6175,7 +6191,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
useHapticFeedback = false; // suppress feedback if already non-interactive
}
if (down) {
- sleepPress();
+ sleepPress(event.getEventTime());
} else {
sleepRelease(event.getEventTime());
}
@@ -6246,30 +6262,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
}
- case KeyEvent.KEYCODE_ASSIST: {
- final boolean longPressed = event.getRepeatCount() > 0;
- if (down && longPressed) {
- Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST_LONG_PRESS);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- }
- if (!down && !longPressed) {
- Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
- 0 /* unused */, null /* hint */);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- }
- result &= ~ACTION_PASS_TO_USER;
- break;
- }
case KeyEvent.KEYCODE_VOICE_ASSIST: {
- if (!down) {
+ // Only do this if we would otherwise not pass it to the user. In that case,
+ // interceptKeyBeforeDispatching would apply a similar but different policy in
+ // order to invoke voice assist actions. Note that we need to make a copy of the
+ // key event here because the original key event will be recycled when we return.
+ if ((result & ACTION_PASS_TO_USER) == 0 && !down) {
mBroadcastWakeLock.acquire();
- Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
+ Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK,
+ keyguardActive ? 1 : 0, 0);
msg.setAsynchronous(true);
msg.sendToTarget();
}
- result &= ~ACTION_PASS_TO_USER;
break;
}
case KeyEvent.KEYCODE_WINDOW: {
@@ -6538,22 +6542,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- void launchVoiceAssistWithWakeLock() {
- final Intent voiceIntent;
- if (!keyguardOn()) {
- voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
- } else {
- IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- if (dic != null) {
- try {
- dic.exitIdle("voice-search");
- } catch (RemoteException e) {
- }
+ void launchVoiceAssistWithWakeLock(boolean keyguardActive) {
+ IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ if (dic != null) {
+ try {
+ dic.exitIdle("voice-search");
+ } catch (RemoteException e) {
}
- voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
}
+ Intent voiceIntent =
+ new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, keyguardActive);
startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
mBroadcastWakeLock.release();
}
@@ -8009,10 +8009,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
- final boolean dockedStackVisible =
- mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
final boolean freeformStackVisible =
- mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
+ mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
// We need to force system bars when the docked stack is visible, when the freeform stack
diff --git a/com/android/server/stats/StatsCompanionService.java b/com/android/server/stats/StatsCompanionService.java
index ca3dd058..f1fb3e7b 100644
--- a/com/android/server/stats/StatsCompanionService.java
+++ b/com/android/server/stats/StatsCompanionService.java
@@ -20,25 +20,15 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.IntentFilter;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
@@ -63,8 +53,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final PendingIntent mAnomalyAlarmIntent;
private final PendingIntent mPollingAlarmIntent;
- private final BroadcastReceiver mAppUpdateReceiver;
- private final BroadcastReceiver mUserUpdateReceiver;
public StatsCompanionService(Context context) {
super();
@@ -75,113 +63,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, PollingAlarmReceiver.class), 0);
- mAppUpdateReceiver = new AppUpdateReceiver();
- mUserUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (sStatsdLock) {
- sStatsd = fetchStatsdService();
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd");
- return;
- }
- try {
- // Pull the latest state of UID->app name, version mapping.
- // Needed since the new user basically has a version of every app.
- informAllUidsLocked(context);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
- forgetEverything();
- }
- }
- }
- };
- Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
}
- private final static int[] toIntArray(List<Integer> list){
- int[] ret = new int[list.size()];
- for(int i = 0;i < ret.length;i++) {
- ret[i] = list.get(i);
- }
- return ret;
- }
-
- // Assumes that sStatsdLock is held.
- private final void informAllUidsLocked(Context context) throws RemoteException {
- UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
- PackageManager pm = context.getPackageManager();
- final List<UserInfo> users = um.getUsers(true);
- if (DEBUG) {
- Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
- }
-
- List<Integer> uids = new ArrayList();
- List<Integer> versions = new ArrayList();
- List<String> apps = new ArrayList();
-
- // Add in all the apps for every user/profile.
- for (UserInfo profile : users) {
- List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
- for (int j = 0; j < pi.size(); j++) {
- if (pi.get(j).applicationInfo != null) {
- uids.add(pi.get(j).applicationInfo.uid);
- versions.add(pi.get(j).versionCode);
- apps.add(pi.get(j).packageName);
- }
- }
- }
- sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
- String[apps.size()]));
- if (DEBUG) {
- Slog.w(TAG, "Sent data for "+uids.size() +" apps");
- }
- }
-
- public final static class AppUpdateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
- /**
- * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
- * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
- */
- if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- return; // Keep only replacing or normal add and remove.
- }
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
- return;
- }
- try {
- if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
- Bundle b = intent.getExtras();
- int uid = b.getInt(Intent.EXTRA_UID);
- boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (!replacing) {
- // Don't bother sending an update if we're right about to get another
- // intent for the new version that's added.
- PackageManager pm = context.getPackageManager();
- String app = intent.getData().getSchemeSpecificPart();
- sStatsd.informOnePackageRemoved(app, uid);
- }
- } else {
- PackageManager pm = context.getPackageManager();
- Bundle b = intent.getExtras();
- int uid = b.getInt(Intent.EXTRA_UID);
- String app = intent.getData().getSchemeSpecificPart();
- PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
- sStatsd.informOnePackage(app, uid, pi.versionCode);
- }
- } catch (Exception e) {
- Slog.w(TAG, "Failed to inform statsd of an app update", e);
- }
- }
- }
- };
-
public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -392,23 +275,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
forgetEverything();
}
- // Setup broadcast receiver for updates
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
- null);
-
- // Setup receiver for user initialize (which happens once for a new user) and
- // if a user is removed.
- filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
- filter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
- filter, null, null);
-
- // Pull the latest state of UID->app name, version mapping when statsd starts.
- informAllUidsLocked(mContext);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
forgetEverything();
@@ -427,8 +293,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void forgetEverything() {
synchronized (sStatsdLock) {
sStatsd = null;
- mContext.unregisterReceiver(mAppUpdateReceiver);
- mContext.unregisterReceiver(mUserUpdateReceiver);
cancelAnomalyAlarm();
cancelPollingAlarms();
}
diff --git a/com/android/server/statusbar/StatusBarManagerInternal.java b/com/android/server/statusbar/StatusBarManagerInternal.java
index b07fe98d..08846784 100644
--- a/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -77,7 +77,6 @@ public interface StatusBarManagerInternal {
void setCurrentUser(int newUserId);
- boolean isGlobalActionsDisabled();
void setGlobalActionsListener(GlobalActionsListener listener);
void showGlobalActions();
diff --git a/com/android/server/statusbar/StatusBarManagerService.java b/com/android/server/statusbar/StatusBarManagerService.java
index c78a3406..bdfbe481 100644
--- a/com/android/server/statusbar/StatusBarManagerService.java
+++ b/com/android/server/statusbar/StatusBarManagerService.java
@@ -16,8 +16,6 @@
package com.android.server.statusbar;
-import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
-
import android.app.ActivityThread;
import android.app.StatusBarManager;
import android.content.ComponentName;
@@ -365,11 +363,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public boolean isGlobalActionsDisabled() {
- return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
- }
-
- @Override
public void setGlobalActionsListener(GlobalActionsListener listener) {
mGlobalActionListener = listener;
mGlobalActionListener.onStatusBarConnectedChanged(mBar != null);
diff --git a/com/android/server/tv/TvInputHardwareManager.java b/com/android/server/tv/TvInputHardwareManager.java
index c1607e94..6117da7b 100644
--- a/com/android/server/tv/TvInputHardwareManager.java
+++ b/com/android/server/tv/TvInputHardwareManager.java
@@ -1022,6 +1022,20 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
}
+ @Override
+ public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ }
+ if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
+ return false;
+ }
+ // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
+ return false;
+ }
+
private boolean startCapture(Surface surface, TvStreamConfig config) {
synchronized (mImplLock) {
if (mReleased) {
diff --git a/com/android/server/usb/UsbAlsaManager.java b/com/android/server/usb/UsbAlsaManager.java
index d359b704..acc27bee 100644
--- a/com/android/server/usb/UsbAlsaManager.java
+++ b/com/android/server/usb/UsbAlsaManager.java
@@ -132,9 +132,7 @@ public final class UsbAlsaManager {
mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
// initial scan
- if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning ASLA cards file.");
- }
+ mCardsParser.scan();
}
public void systemReady() {
@@ -316,7 +314,7 @@ public final class UsbAlsaManager {
return null;
}
- if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) {
+ if (!mDevicesParser.scan()) {
Slog.e(TAG, "Error parsing ALSA devices file.");
return null;
}
@@ -532,9 +530,6 @@ public final class UsbAlsaManager {
//
// called by UsbService.dump
public void dump(IndentingPrintWriter pw) {
- pw.println("Parsers Scan Status:");
- pw.println(" Cards Parser: " + mCardsParser.getScanStatus());
- pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
pw.println("USB Audio Devices:");
for (UsbDevice device : mAudioDevices.keySet()) {
pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
diff --git a/com/android/server/usb/UsbHostManager.java b/com/android/server/usb/UsbHostManager.java
index 095fdc63..c657a1b4 100644
--- a/com/android/server/usb/UsbHostManager.java
+++ b/com/android/server/usb/UsbHostManager.java
@@ -376,8 +376,6 @@ public class UsbHostManager {
}
}
}
-
- mUsbAlsaManager.dump(pw);
}
private native void monitorUsbHostBus();
diff --git a/com/android/server/wifi/SelfRecovery.java b/com/android/server/wifi/SelfRecovery.java
index e39e0d5b..21a3e0ac 100644
--- a/com/android/server/wifi/SelfRecovery.java
+++ b/com/android/server/wifi/SelfRecovery.java
@@ -72,7 +72,7 @@ public class SelfRecovery {
Log.e(TAG, "Invalid trigger reason. Ignoring...");
return;
}
- Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
+ Log.wtf(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
if (reason == REASON_WIFICOND_CRASH || reason == REASON_HAL_CRASH) {
trimPastRestartTimes();
// Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW
diff --git a/com/android/server/wifi/WifiNative.java b/com/android/server/wifi/WifiNative.java
index 35dec2e7..0b1719db 100644
--- a/com/android/server/wifi/WifiNative.java
+++ b/com/android/server/wifi/WifiNative.java
@@ -16,7 +16,6 @@
package com.android.server.wifi;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.apf.ApfCapabilities;
import android.net.wifi.IApInterface;
@@ -110,12 +109,12 @@ public class WifiNative {
* @return Pair of <Integer, IClientInterface> to indicate the status and the associated wificond
* client interface binder handler (will be null on failure).
*/
- public Pair<Integer, IClientInterface> setupForClientMode(@NonNull String ifaceName) {
+ public Pair<Integer, IClientInterface> setupForClientMode() {
if (!startHalIfNecessary(true)) {
Log.e(mTAG, "Failed to start HAL for client mode");
return Pair.create(SETUP_FAILURE_HAL, null);
}
- IClientInterface iClientInterface = mWificondControl.setupDriverForClientMode(ifaceName);
+ IClientInterface iClientInterface = mWificondControl.setupDriverForClientMode();
if (iClientInterface == null) {
return Pair.create(SETUP_FAILURE_WIFICOND, null);
}
@@ -131,12 +130,12 @@ public class WifiNative {
* @return Pair of <Integer, IApInterface> to indicate the status and the associated wificond
* AP interface binder handler (will be null on failure).
*/
- public Pair<Integer, IApInterface> setupForSoftApMode(@NonNull String ifaceName) {
+ public Pair<Integer, IApInterface> setupForSoftApMode() {
if (!startHalIfNecessary(false)) {
Log.e(mTAG, "Failed to start HAL for AP mode");
return Pair.create(SETUP_FAILURE_HAL, null);
}
- IApInterface iApInterface = mWificondControl.setupDriverForSoftApMode(ifaceName);
+ IApInterface iApInterface = mWificondControl.setupDriverForSoftApMode();
if (iApInterface == null) {
return Pair.create(SETUP_FAILURE_WIFICOND, null);
}
diff --git a/com/android/server/wifi/WifiStateMachine.java b/com/android/server/wifi/WifiStateMachine.java
index 0c2cc32f..1f6cada7 100644
--- a/com/android/server/wifi/WifiStateMachine.java
+++ b/com/android/server/wifi/WifiStateMachine.java
@@ -4165,7 +4165,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
switch (message.what) {
case CMD_START_SUPPLICANT:
Pair<Integer, IClientInterface> statusAndInterface =
- mWifiNative.setupForClientMode(mInterfaceName);
+ mWifiNative.setupForClientMode();
if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
mClientInterface = statusAndInterface.second;
} else {
@@ -6954,8 +6954,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
mMode = config.getTargetMode();
IApInterface apInterface = null;
- Pair<Integer, IApInterface> statusAndInterface =
- mWifiNative.setupForSoftApMode(mInterfaceName);
+ Pair<Integer, IApInterface> statusAndInterface = mWifiNative.setupForSoftApMode();
if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
apInterface = statusAndInterface.second;
} else {
diff --git a/com/android/server/wifi/WifiStateMachinePrime.java b/com/android/server/wifi/WifiStateMachinePrime.java
index cd1948f1..20068849 100644
--- a/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/com/android/server/wifi/WifiStateMachinePrime.java
@@ -290,8 +290,7 @@ public class WifiStateMachinePrime {
}
try {
- mApInterface = mWificond.createApInterface(
- mWifiInjector.getWifiNative().getInterfaceName());
+ mApInterface = mWificond.createApInterface();
} catch (RemoteException e1) { }
if (mApInterface == null) {
diff --git a/com/android/server/wifi/WificondControl.java b/com/android/server/wifi/WificondControl.java
index df4e785b..b6104a8c 100644
--- a/com/android/server/wifi/WificondControl.java
+++ b/com/android/server/wifi/WificondControl.java
@@ -16,7 +16,6 @@
package com.android.server.wifi;
-import android.annotation.NonNull;
import android.net.wifi.IApInterface;
import android.net.wifi.IClientInterface;
import android.net.wifi.IPnoScanEvent;
@@ -134,7 +133,7 @@ public class WificondControl {
* @return An IClientInterface as wificond client interface binder handler.
* Returns null on failure.
*/
- public IClientInterface setupDriverForClientMode(@NonNull String ifaceName) {
+ public IClientInterface setupDriverForClientMode() {
Log.d(TAG, "Setting up driver for client mode");
mWificond = mWifiInjector.makeWificond();
if (mWificond == null) {
@@ -144,7 +143,7 @@ public class WificondControl {
IClientInterface clientInterface = null;
try {
- clientInterface = mWificond.createClientInterface(ifaceName);
+ clientInterface = mWificond.createClientInterface();
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IClientInterface due to remote exception");
return null;
@@ -182,7 +181,7 @@ public class WificondControl {
* @return An IApInterface as wificond Ap interface binder handler.
* Returns null on failure.
*/
- public IApInterface setupDriverForSoftApMode(@NonNull String ifaceName) {
+ public IApInterface setupDriverForSoftApMode() {
Log.d(TAG, "Setting up driver for soft ap mode");
mWificond = mWifiInjector.makeWificond();
if (mWificond == null) {
@@ -192,7 +191,7 @@ public class WificondControl {
IApInterface apInterface = null;
try {
- apInterface = mWificond.createApInterface(ifaceName);
+ apInterface = mWificond.createApInterface();
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IApInterface due to remote exception");
return null;
diff --git a/com/android/server/wifi/scanner/WificondScannerImpl.java b/com/android/server/wifi/scanner/WificondScannerImpl.java
index 10fc8e3e..fb878e67 100644
--- a/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -167,12 +167,12 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
+ ",eventHandler=" + eventHandler);
return false;
}
+ if (mPendingSingleScanSettings != null
+ || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
+ Log.w(TAG, "A single scan is already running");
+ return false;
+ }
synchronized (mSettingsLock) {
- if (mPendingSingleScanSettings != null
- || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
- Log.w(TAG, "A single scan is already running");
- return false;
- }
mPendingSingleScanSettings = settings;
mPendingSingleScanEventHandler = eventHandler;
processPendingScans();
@@ -518,10 +518,8 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
}
private boolean isHwPnoScanRequired() {
- synchronized (mSettingsLock) {
- if (mPnoSettings == null) return false;
- return isHwPnoScanRequired(mPnoSettings.isConnected);
- }
+ if (mPnoSettings == null) return false;
+ return isHwPnoScanRequired(mPnoSettings.isConnected);
}
@Override
diff --git a/com/android/server/wifi/util/InformationElementUtil.java b/com/android/server/wifi/util/InformationElementUtil.java
index 14912b5f..c8f9ca34 100644
--- a/com/android/server/wifi/util/InformationElementUtil.java
+++ b/com/android/server/wifi/util/InformationElementUtil.java
@@ -247,10 +247,6 @@ public class InformationElementUtil {
"Bad Interworking element length: " + ie.bytes.length);
}
- if (ie.bytes.length == 3 || ie.bytes.length == 9) {
- int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2);
- }
-
if (ie.bytes.length == 7 || ie.bytes.length == 9) {
hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
}
diff --git a/com/android/server/wm/AppWindowToken.java b/com/android/server/wm/AppWindowToken.java
index 5d034935..a1eeff84 100644
--- a/com/android/server/wm/AppWindowToken.java
+++ b/com/android/server/wm/AppWindowToken.java
@@ -823,7 +823,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// For freeform windows, we can't freeze the bounds at the moment because this would make
// the resizing unresponsive.
- if (task == null || task.inFreeformWindowingMode()) {
+ if (task == null || task.inFreeformWorkspace()) {
return false;
}
@@ -1310,7 +1310,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// Notify the pinned stack upon all windows drawn. If there was an animation in
// progress then this signal will resume that animation.
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
if (pinnedStack != null) {
pinnedStack.onAllWindowsDrawn();
}
diff --git a/com/android/server/wm/BlackFrame.java b/com/android/server/wm/BlackFrame.java
index d206554c..5c29a0aa 100644
--- a/com/android/server/wm/BlackFrame.java
+++ b/com/android/server/wm/BlackFrame.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.graphics.PixelFormat.OPAQUE;
import static android.view.SurfaceControl.FX_SURFACE_DIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -50,8 +51,14 @@ public class BlackFrame {
int w = r-l;
int h = b-t;
- surface = new SurfaceControl(session, "BlackSurface",
- w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
+ if (DEBUG_SURFACE_TRACE) {
+ surface = new WindowSurfaceController.SurfaceTrace(session, "BlackSurface("
+ + l + ", " + t + ")",
+ w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
+ } else {
+ surface = new SurfaceControl(session, "BlackSurface",
+ w, h, OPAQUE, FX_SURFACE_DIM | SurfaceControl.HIDDEN);
+ }
surface.setAlpha(1);
surface.setLayerStack(layerStack);
diff --git a/com/android/server/wm/CircularDisplayMask.java b/com/android/server/wm/CircularDisplayMask.java
index 85f468b5..ae415413 100644
--- a/com/android/server/wm/CircularDisplayMask.java
+++ b/com/android/server/wm/CircularDisplayMask.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -66,9 +67,14 @@ class CircularDisplayMask {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x,
- mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-
+ if (DEBUG_SURFACE_TRACE) {
+ ctrl = new WindowSurfaceController.SurfaceTrace(session, "CircularDisplayMask",
+ mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } else {
+ ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x,
+ mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ }
ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
diff --git a/com/android/server/wm/ConfigurationContainer.java b/com/android/server/wm/ConfigurationContainer.java
index 5bfea989..9e028d38 100644
--- a/com/android/server/wm/ConfigurationContainer.java
+++ b/com/android/server/wm/ConfigurationContainer.java
@@ -21,9 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -184,11 +182,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
- public boolean inSplitScreenPrimaryWindowingMode() {
- return mFullConfiguration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- }
-
/**
* Returns true if this container can be put in either
* {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
@@ -199,14 +192,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
}
- public boolean inPinnedWindowingMode() {
- return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
- }
-
- public boolean inFreeformWindowingMode() {
- return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
- }
-
/** Returns the activity type associated with the the configuration container. */
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
diff --git a/com/android/server/wm/DimLayer.java b/com/android/server/wm/DimLayer.java
index 48181d30..708973d5 100644
--- a/com/android/server/wm/DimLayer.java
+++ b/com/android/server/wm/DimLayer.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -104,10 +105,16 @@ public class DimLayer {
private void constructSurface(WindowManagerService service) {
service.openSurfaceTransaction();
try {
- mDimSurface = new SurfaceControl(service.mFxSession, mName,
+ if (DEBUG_SURFACE_TRACE) {
+ mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
+ "DimSurface",
16, 16, PixelFormat.OPAQUE,
SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-
+ } else {
+ mDimSurface = new SurfaceControl(service.mFxSession, mName,
+ 16, 16, PixelFormat.OPAQUE,
+ SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
+ }
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
" DIM " + mDimSurface + ": CREATE");
mDimSurface.setLayerStack(mDisplayId);
diff --git a/com/android/server/wm/DisplayContent.java b/com/android/server/wm/DisplayContent.java
index 03fdc968..0e68a8f6 100644
--- a/com/android/server/wm/DisplayContent.java
+++ b/com/android/server/wm/DisplayContent.java
@@ -16,9 +16,10 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -117,7 +118,7 @@ import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
import android.annotation.NonNull;
-import android.content.pm.PackageManager;
+import android.app.ActivityManager.StackId;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -296,6 +297,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** Window tokens that are in the process of exiting, but still on screen for animations. */
final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
+ /** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
+ * (except a future lockscreen TaskStack) moves to the top. */
+ private TaskStack mHomeStack = null;
+
/** Detect user tapping outside of current focused task bounds .*/
TaskTapPointerEventListener mTapDetector;
@@ -974,8 +979,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
// In the presence of the PINNED stack or System Alert
- // windows we unfortunately can not seamlessly rotate.
- if (hasPinnedStack()) {
+ // windows we unforuntately can not seamlessly rotate.
+ if (getStackById(PINNED_STACK_ID) != null) {
mayRotateSeamlessly = false;
}
for (int i = 0; i < mService.mSessions.size(); i++) {
@@ -1446,31 +1451,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
TaskStack getHomeStack() {
- return mTaskStackContainers.getHomeStack();
- }
-
- /**
- * @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise.
- */
- TaskStack getSplitScreenPrimaryStackStack() {
- TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStackStack();
- return (stack != null && stack.isVisible()) ? stack : null;
- }
-
- /**
- * Like {@link #getSplitScreenPrimaryStackStack}, but also returns the stack if it's currently
- * not visible.
- */
- TaskStack getSplitScreenPrimaryStackStackIgnoringVisibility() {
- return mTaskStackContainers.getSplitScreenPrimaryStackStack();
- }
-
- TaskStack getPinnedStack() {
- return mTaskStackContainers.getPinnedStack();
+ if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
+ Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
+ }
+ return mHomeStack;
}
- private boolean hasPinnedStack() {
- return mTaskStackContainers.getPinnedStack() != null;
+ TaskStack getStackById(int stackId) {
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
+ if (stack.mStackId == stackId) {
+ return stack;
+ }
+ }
+ return null;
}
/**
@@ -1486,16 +1480,29 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* activity type. Null is no compatible stack on the display.
*/
TaskStack getStack(int windowingMode, int activityType) {
- return mTaskStackContainers.getStack(windowingMode, activityType);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
+ return null;
}
@VisibleForTesting
- TaskStack getTopStack() {
- return mTaskStackContainers.getTopStack();
+ int getStackCount() {
+ return mTaskStackContainers.size();
}
- void onStackWindowingModeChanged(TaskStack stack) {
- mTaskStackContainers.onStackWindowingModeChanged(stack);
+ @VisibleForTesting
+ int getStackPosition(int windowingMode, int activityType) {
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return i;
+ }
+ }
+ return -1;
}
@Override
@@ -1516,8 +1523,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* bounds were updated.
*/
void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
if (stack.updateBoundsAfterConfigChange()) {
changedStackList.add(stack.mStackId);
}
@@ -1526,7 +1533,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// If there was no pinned stack, we still need to notify the controller of the display info
// update as a result of the config change. We do this here to consolidate the flow between
// changes when there is and is not a stack.
- if (!hasPinnedStack()) {
+ if (getStack(WINDOWING_MODE_PINNED) == null) {
mPinnedStackControllerLocked.onDisplayInfoChanged();
}
}
@@ -1625,8 +1632,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- mTaskStackContainers.getChildAt(i).updateDisplayInfo(null);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ mTaskStackContainers.get(i).updateDisplayInfo(null);
}
}
@@ -1747,14 +1754,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
out.set(mContentRect);
}
- TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
+ TaskStack addStackToDisplay(int stackId, boolean onTop, StackWindowController controller) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
- final TaskStack stack = new TaskStack(mService, stackId, controller);
- mTaskStackContainers.addStackToDisplay(stack, onTop);
+ TaskStack stack = getStackById(stackId);
+ if (stack != null) {
+ // It's already attached to the display...clear mDeferRemoval, set controller, and move
+ // stack to appropriate z-order on display as needed.
+ stack.mDeferRemoval = false;
+ stack.setController(controller);
+ // We're not moving the display to front when we're adding stacks, only when
+ // requested to change the position of stack explicitly.
+ mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
+ false /* includingParents */);
+ } else {
+ stack = new TaskStack(mService, stackId, controller);
+ mTaskStackContainers.addStackToDisplay(stack, onTop);
+ }
- if (stack.inSplitScreenPrimaryWindowingMode()) {
+ if (stackId == DOCKED_STACK_ID) {
mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
return stack;
@@ -1771,7 +1790,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
+ " to its current displayId=" + mDisplayId);
}
- prevDc.mTaskStackContainers.removeChild(stack);
+ prevDc.mTaskStackContainers.removeStackFromDisplay(stack);
mTaskStackContainers.addStackToDisplay(stack, onTop);
}
@@ -1805,8 +1824,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
int taskIdFromPoint(int x, int y) {
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
final int taskId = stack.taskIdFromPoint(x, y);
if (taskId != -1) {
return taskId;
@@ -1822,8 +1841,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
Task findTaskForResizePoint(int x, int y) {
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
mTmpTaskForResizePointSearchResult.reset();
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
if (!stack.getWindowConfiguration().canResizeTask()) {
return null;
}
@@ -1845,8 +1864,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTouchExcludeRegion.set(mBaseDisplayRect);
final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
mTmpRect2.setEmpty();
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
stack.setTouchExcludeRegion(
focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
}
@@ -1877,7 +1896,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
// TODO(multi-display): Support docked stacks on secondary displays.
- if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStackStack() != null) {
+ if (mDisplayId == DEFAULT_DISPLAY && getDockedStackLocked() != null) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -1894,8 +1913,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
private void resetAnimationBackgroundAnimator() {
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- mTaskStackContainers.getChildAt(stackNdx).resetAnimationBackgroundAnimator();
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ mTaskStackContainers.get(stackNdx).resetAnimationBackgroundAnimator();
}
}
@@ -1966,8 +1985,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
float dividerAnimationTarget) {
boolean updated = false;
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
if (stack == null || !stack.isAdjustedForIme()) {
continue;
}
@@ -1995,8 +2014,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
boolean clearImeAdjustAnimation() {
boolean changed = false;
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
if (stack != null && stack.isAdjustedForIme()) {
stack.resetAdjustedForIme(true /* adjustBoundsNow */);
changed = true;
@@ -2006,8 +2025,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
void beginImeAdjustAnimation() {
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
if (stack.isVisible() && stack.isAdjustedForIme()) {
stack.beginImeAdjustAnimation();
}
@@ -2018,7 +2037,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final WindowState imeWin = mService.mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
- final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ final boolean dockVisible = isStackVisible(DOCKED_STACK_ID);
final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
@@ -2036,8 +2055,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// - If IME is not visible, divider is not moved and is normal width.
if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
&& stack.inSplitScreenWindowingMode()) {
@@ -2049,8 +2068,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDividerControllerLocked.setAdjustedForIme(
imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
} else {
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
stack.resetAdjustedForIme(!dockVisible);
}
mDividerControllerLocked.setAdjustedForIme(
@@ -2081,8 +2100,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
void prepareFreezingTaskBounds() {
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
stack.prepareFreezingTaskBounds();
}
}
@@ -2141,22 +2160,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final long token = proto.start(fieldId);
super.writeToProto(proto, WINDOW_CONTAINER);
proto.write(ID, mDisplayId);
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
stack.writeToProto(proto, STACKS);
}
mDividerControllerLocked.writeToProto(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
mPinnedStackControllerLocked.writeToProto(proto, PINNED_STACK_CONTROLLER);
- for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
+ for (int i = mAboveAppWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mAboveAppWindowsContainers.get(i);
windowToken.writeToProto(proto, ABOVE_APP_WINDOWS);
}
- for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
+ for (int i = mBelowAppWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mBelowAppWindowsContainers.get(i);
windowToken.writeToProto(proto, BELOW_APP_WINDOWS);
}
- for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
+ for (int i = mImeWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mImeWindowsContainers.get(i);
windowToken.writeToProto(proto, IME_WINDOWS);
}
proto.write(DPI, mBaseDisplayDensity);
@@ -2202,8 +2221,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
stack.dump(prefix + " ", pw);
}
@@ -2222,22 +2241,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
mDimLayerController.dump(prefix, pw);
pw.println();
-
- // Dump stack references
- final TaskStack homeStack = getHomeStack();
- if (homeStack != null) {
- pw.println(prefix + "homeStack=" + homeStack.getName());
- }
- final TaskStack pinnedStack = getPinnedStack();
- if (pinnedStack != null) {
- pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
- }
- final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStackStack();
- if (splitScreenPrimaryStack != null) {
- pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
- }
-
- pw.println();
mDividerControllerLocked.dump(prefix, pw);
pw.println();
mPinnedStackControllerLocked.dump(prefix, pw);
@@ -2257,10 +2260,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return "Display " + mDisplayId + " name=\"" + mDisplayInfo.name + "\"";
}
- /** Returns true if the stack in the windowing mode is visible. */
- boolean isStackVisible(int windowingMode) {
- final TaskStack stack = getStack(windowingMode);
- return stack != null && stack.isVisible();
+ /** Checks if stack with provided id is visible on this display. */
+ boolean isStackVisible(int stackId) {
+ final TaskStack stack = getStackById(stackId);
+ return (stack != null && stack.isVisible());
+ }
+
+ /**
+ * @return The docked stack, but only if it is visible, and {@code null} otherwise.
+ */
+ TaskStack getDockedStackLocked() {
+ final TaskStack stack = getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ return (stack != null && stack.isVisible()) ? stack : null;
+ }
+
+ /**
+ * Like {@link #getDockedStackLocked}, but also returns the docked stack if it's currently not
+ * visible.
+ */
+ TaskStack getDockedStackIgnoringVisibility() {
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
/** Find the visible, touch-deliverable window under the given point */
@@ -3339,6 +3358,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> {
+ int size() {
+ return mChildren.size();
+ }
+
+ E get(int index) {
+ return mChildren.get(index);
+ }
+
@Override
boolean fillsParent() {
return true;
@@ -3356,108 +3383,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
- // Cached reference to some special stacks we tend to get a lot so we don't need to loop
- // through the list to find them.
- private TaskStack mHomeStack = null;
- private TaskStack mPinnedStack = null;
- private TaskStack mSplitScreenPrimaryStack = null;
-
- /**
- * Returns the topmost stack on the display that is compatible with the input windowing mode
- * and activity type. Null is no compatible stack on the display.
- */
- TaskStack getStack(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_HOME) {
- return mHomeStack;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- return mPinnedStack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return mSplitScreenPrimaryStack;
- }
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- final TaskStack stack = mTaskStackContainers.getChildAt(i);
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
- }
- return null;
- }
-
- @VisibleForTesting
- TaskStack getTopStack() {
- return mTaskStackContainers.getChildCount() > 0
- ? mTaskStackContainers.getChildAt(mTaskStackContainers.getChildCount() - 1) : null;
- }
-
- TaskStack getHomeStack() {
- if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
- Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
- }
- return mHomeStack;
- }
-
- TaskStack getPinnedStack() {
- return mPinnedStack;
- }
-
- TaskStack getSplitScreenPrimaryStackStack() {
- return mSplitScreenPrimaryStack;
- }
-
/**
* Adds the stack to this container.
- * @see DisplayContent#createStack(int, boolean, StackWindowController)
+ * @see WindowManagerService#addStackToDisplay(int, int, boolean)
*/
void addStackToDisplay(TaskStack stack, boolean onTop) {
- addStackReferenceIfNeeded(stack);
- addChild(stack, onTop);
- stack.onDisplayChanged(DisplayContent.this);
- }
-
- void onStackWindowingModeChanged(TaskStack stack) {
- removeStackReferenceIfNeeded(stack);
- addStackReferenceIfNeeded(stack);
- if (stack == mPinnedStack && getTopStack() != stack) {
- // Looks like this stack changed windowing mode to pinned. Move it to the top.
- positionChildAt(POSITION_TOP, stack, false /* includingParents */);
- }
- }
-
- private void addStackReferenceIfNeeded(TaskStack stack) {
if (stack.isActivityTypeHome()) {
if (mHomeStack != null) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
- + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+ throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first.");
}
mHomeStack = stack;
}
- final int windowingMode = stack.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- if (mPinnedStack != null) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
- + mPinnedStack + " already exist on display=" + this
- + " stack=" + stack);
- }
- mPinnedStack = stack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- if (mSplitScreenPrimaryStack != null) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded:"
- + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
- + " already exist on display=" + this + " stack=" + stack);
- }
- mSplitScreenPrimaryStack = stack;
- }
+ addChild(stack, onTop);
+ stack.onDisplayChanged(DisplayContent.this);
}
- private void removeStackReferenceIfNeeded(TaskStack stack) {
- if (stack == mHomeStack) {
- mHomeStack = null;
- } else if (stack == mPinnedStack) {
- mPinnedStack = null;
- } else if (stack == mSplitScreenPrimaryStack) {
- mSplitScreenPrimaryStack = null;
- }
+ /** Removes the stack from its container and prepare for changing the parent. */
+ void removeStackFromDisplay(TaskStack stack) {
+ removeChild(stack);
+ stack.onRemovedFromDisplay();
}
private void addChild(TaskStack stack, boolean toTop) {
@@ -3467,11 +3411,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
setLayoutNeeded();
}
- @Override
- protected void removeChild(TaskStack stack) {
- super.removeChild(stack);
- removeStackReferenceIfNeeded(stack);
- }
@Override
boolean isOnTop() {
@@ -3514,7 +3453,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
: requestedPosition >= topChildPosition;
int targetPosition = requestedPosition;
- if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED && hasPinnedStack()) {
+ if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED
+ && getStack(WINDOWING_MODE_PINNED) != null) {
// The pinned stack is always the top most stack (always-on-top) when it is present.
TaskStack topStack = mChildren.get(topChildPosition);
if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
@@ -3616,8 +3556,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@Override
int getOrientation() {
- if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
- || isStackVisible(WINDOWING_MODE_FREEFORM)) {
+ if (isStackVisible(DOCKED_STACK_ID) || isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
// Apps and their containers are not allowed to specify an orientation while the
// docked or freeform stack is visible...except for the home stack/task if the
// docked stack is minimized and it actually set something.
@@ -3632,16 +3571,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
final int orientation = super.getOrientation();
- boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- if (isCar) {
- // In a car, you cannot physically rotate the screen, so it doesn't make sense to
- // allow anything but the default orientation.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
- return SCREEN_ORIENTATION_UNSPECIFIED;
- }
-
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
diff --git a/com/android/server/wm/DockedStackDividerController.java b/com/android/server/wm/DockedStackDividerController.java
index 52526e2f..6f441b98 100644
--- a/com/android/server/wm/DockedStackDividerController.java
+++ b/com/android/server/wm/DockedStackDividerController.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -319,7 +322,7 @@ public class DockedStackDividerController implements DimLayerUser {
if (mWindow == null) {
return;
}
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ TaskStack stack = mDisplayContent.getDockedStackIgnoringVisibility();
// If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
final boolean visible = stack != null;
@@ -359,7 +362,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
void positionDockedStackedDivider(Rect frame) {
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStack();
+ TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
@@ -456,7 +459,7 @@ public class DockedStackDividerController implements DimLayerUser {
long animDuration = 0;
if (animate) {
final TaskStack stack =
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final long transitionDuration = isAnimationMaximizing()
? mService.mAppTransition.getLastClipRevealTransitionDuration()
: DEFAULT_APP_TRANSITION_DURATION;
@@ -510,8 +513,7 @@ public class DockedStackDividerController implements DimLayerUser {
void registerDockedStackListener(IDockedStackListener listener) {
mDockedStackListeners.register(listener);
notifyDockedDividerVisibilityChanged(wasVisible());
- notifyDockedStackExistsChanged(
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null);
+ notifyDockedStackExistsChanged(mDisplayContent.getDockedStackIgnoringVisibility() != null);
notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
isHomeStackResizable());
notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
@@ -530,7 +532,7 @@ public class DockedStackDividerController implements DimLayerUser {
final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
? mDisplayContent.getStack(targetWindowingMode)
: null;
- final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackStack();
+ final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
stack.getDimBounds(mTmpRect);
@@ -586,7 +588,7 @@ public class DockedStackDividerController implements DimLayerUser {
private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
final AppWindowToken token = apps.valueAt(i);
- if (token.getTask() != null && token.inSplitScreenPrimaryWindowingMode()) {
+ if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
return true;
}
}
@@ -598,7 +600,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
private void checkMinimizeChanged(boolean animate) {
- if (mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() == null) {
+ if (mDisplayContent.getDockedStackIgnoringVisibility() == null) {
return;
}
final TaskStack homeStack = mDisplayContent.getHomeStack();
@@ -760,7 +762,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
private boolean setMinimizedDockedStack(boolean minimized) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack = mDisplayContent.getDockedStackIgnoringVisibility();
notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
}
@@ -811,7 +813,8 @@ public class DockedStackDividerController implements DimLayerUser {
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack =
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
diff --git a/com/android/server/wm/EmulatorDisplayOverlay.java b/com/android/server/wm/EmulatorDisplayOverlay.java
index 19bd8e9d..3186d3dc 100644
--- a/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -56,8 +57,14 @@ class EmulatorDisplayOverlay {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl(session, "EmulatorDisplayOverlay", mScreenSize.x,
- mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ if (DEBUG_SURFACE_TRACE) {
+ ctrl = new WindowSurfaceController.SurfaceTrace(session, "EmulatorDisplayOverlay",
+ mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT,
+ SurfaceControl.HIDDEN);
+ } else {
+ ctrl = new SurfaceControl(session, "EmulatorDisplayOverlay", mScreenSize.x,
+ mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ }
ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
diff --git a/com/android/server/wm/InputMonitor.java b/com/android/server/wm/InputMonitor.java
index 238cb9f1..5057f632 100644
--- a/com/android/server/wm/InputMonitor.java
+++ b/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
@@ -649,7 +650,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
final boolean hasFocus = w == mInputFocus;
final boolean isVisible = w.isVisibleLw();
- if (w.inPinnedWindowingMode()) {
+ if (w.getStackId() == PINNED_STACK_ID) {
if (mAddPipInputConsumerHandle
&& (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
// Update the bounds of the Pip input consumer to match the Pinned stack
diff --git a/com/android/server/wm/PinnedStackController.java b/com/android/server/wm/PinnedStackController.java
index 365366ad..ef31598f 100644
--- a/com/android/server/wm/PinnedStackController.java
+++ b/com/android/server/wm/PinnedStackController.java
@@ -417,7 +417,8 @@ class PinnedStackController {
false /* useCurrentMinEdgeSize */);
}
final Rect animatingBounds = mTmpAnimatingBoundsRect;
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
if (pinnedStack != null) {
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
} else {
diff --git a/com/android/server/wm/RootWindowContainer.java b/com/android/server/wm/RootWindowContainer.java
index fd574709..7832f5de 100644
--- a/com/android/server/wm/RootWindowContainer.java
+++ b/com/android/server/wm/RootWindowContainer.java
@@ -411,6 +411,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
}
}
+ TaskStack getStackById(int stackId) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final DisplayContent dc = mChildren.get(i);
+ final TaskStack stack = dc.getStackById(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
TaskStack getStack(int windowingMode, int activityType) {
for (int i = mChildren.size() - 1; i >= 0; i--) {
final DisplayContent dc = mChildren.get(i);
diff --git a/com/android/server/wm/ScreenRotationAnimation.java b/com/android/server/wm/ScreenRotationAnimation.java
index 8e99be83..d5b6d246 100644
--- a/com/android/server/wm/ScreenRotationAnimation.java
+++ b/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
@@ -23,6 +24,7 @@ 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.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
+import static com.android.server.wm.WindowSurfaceController.SurfaceTrace;
import static com.android.server.wm.proto.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.proto.ScreenRotationAnimationProto.STARTED;
@@ -274,10 +276,17 @@ class ScreenRotationAnimation {
flags |= SurfaceControl.SECURE;
}
- mSurfaceControl = new SurfaceControl(session, "ScreenshotSurface",
- mWidth, mHeight,
- PixelFormat.OPAQUE, flags);
-
+ if (DEBUG_SURFACE_TRACE) {
+ mSurfaceControl = new SurfaceTrace(session, "ScreenshotSurface",
+ mWidth, mHeight,
+ PixelFormat.OPAQUE, flags);
+ Slog.w(TAG, "ScreenRotationAnimation ctor: displayOffset="
+ + mOriginalDisplayRect.toShortString());
+ } else {
+ mSurfaceControl = new SurfaceControl(session, "ScreenshotSurface",
+ mWidth, mHeight,
+ PixelFormat.OPAQUE, flags);
+ }
// capture a screenshot into the surface we just created
Surface sur = new Surface();
sur.copyFrom(mSurfaceControl);
diff --git a/com/android/server/wm/StackWindowController.java b/com/android/server/wm/StackWindowController.java
index 1fda832d..c0a4cb72 100644
--- a/com/android/server/wm/StackWindowController.java
+++ b/com/android/server/wm/StackWindowController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -43,7 +45,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
public class StackWindowController
extends WindowContainerController<TaskStack, StackWindowListener> {
- private final int mStackId;
+ final int mStackId;
private final H mHandler;
@@ -72,7 +74,7 @@ public class StackWindowController
+ " to unknown displayId=" + displayId);
}
- dc.createStack(stackId, onTop, this);
+ dc.addStackToDisplay(stackId, onTop, this);
getRawBounds(outBounds);
}
}
@@ -278,9 +280,8 @@ public class StackWindowController
if (stack.getWindowConfiguration().tasksAreFloating()) {
// Floating tasks should not be resized to the screen's bounds.
- if (stack.inPinnedWindowingMode()
- && bounds.width() == mTmpDisplayBounds.width()
- && bounds.height() == mTmpDisplayBounds.height()) {
+ if (mStackId == PINNED_STACK_ID && bounds.width() == mTmpDisplayBounds.width() &&
+ bounds.height() == mTmpDisplayBounds.height()) {
// If the bounds we are animating is the same as the fullscreen stack
// dimensions, then apply the same inset calculations that we normally do for
// the fullscreen stack, without intersecting it with the display bounds
diff --git a/com/android/server/wm/Task.java b/com/android/server/wm/Task.java
index 891d637a..7e8d1308 100644
--- a/com/android/server/wm/Task.java
+++ b/com/android/server/wm/Task.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -211,7 +213,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
// then we want to preserve our insets so that there will not
// be a jump in the area covered by system decorations. We rely
// on the pinned animation to later unset this value.
- if (stack.inPinnedWindowingMode()) {
+ if (stack.mStackId == PINNED_STACK_ID) {
mPreserveNonFloatingState = true;
} else {
mPreserveNonFloatingState = false;
@@ -419,7 +421,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return mFillsParent
|| !inSplitScreenSecondaryWindowingMode()
|| displayContent == null
- || displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null;
+ || displayContent.getDockedStackIgnoringVisibility() != null;
}
/** Original bounds of the task if applicable, otherwise fullscreen rect. */
@@ -490,7 +492,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
final boolean dockedResizing = displayContent != null
&& displayContent.mDividerControllerLocked.isResizing();
if (useCurrentBounds()) {
- if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
+ if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
return;
}
@@ -596,6 +598,14 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
}
+ boolean inFreeformWorkspace() {
+ return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
+ boolean inPinnedWorkspace() {
+ return mStack != null && mStack.mStackId == PINNED_STACK_ID;
+ }
+
/**
* When we are in a floating stack (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen stack
diff --git a/com/android/server/wm/TaskPositioner.java b/com/android/server/wm/TaskPositioner.java
index 87de1514..c58212cd 100644
--- a/com/android/server/wm/TaskPositioner.java
+++ b/com/android/server/wm/TaskPositioner.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIG
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -648,7 +649,7 @@ class TaskPositioner implements DimLayer.DimLayerUser {
* shouldn't be shown.
*/
private int getDimSide(int x) {
- if (!mTask.mStack.inFreeformWindowingMode()
+ if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
|| !mTask.mStack.fillsParent()
|| mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
return CTRL_NONE;
diff --git a/com/android/server/wm/TaskSnapshotController.java b/com/android/server/wm/TaskSnapshotController.java
index 54ef0651..bff24f6e 100644
--- a/com/android/server/wm/TaskSnapshotController.java
+++ b/com/android/server/wm/TaskSnapshotController.java
@@ -294,9 +294,7 @@ class TaskSnapshotController {
decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
node.end(c);
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
- if (hwBitmap == null) {
- return null;
- }
+
return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
topChild.getConfiguration().orientation, mainWindow.mStableInsets,
ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */);
diff --git a/com/android/server/wm/TaskStack.java b/com/android/server/wm/TaskStack.java
index d170b6f2..65278837 100644
--- a/com/android/server/wm/TaskStack.java
+++ b/com/android/server/wm/TaskStack.java
@@ -18,6 +18,8 @@ package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -294,7 +296,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
if (mFillsParent
|| !inSplitScreenSecondaryWindowingMode()
|| mDisplayContent == null
- || mDisplayContent.getSplitScreenPrimaryStackStack() != null) {
+ || mDisplayContent.getDockedStackLocked() != null) {
return true;
}
return false;
@@ -407,7 +409,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return false;
}
- if (inPinnedWindowingMode()) {
+ if (mStackId == PINNED_STACK_ID) {
getAnimationOrCurrentBounds(mTmpRect2);
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
mTmpRect2, mTmpRect3);
@@ -441,19 +443,21 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
- if (inSplitScreenPrimaryWindowingMode()) {
- repositionDockedStackAfterRotation(mTmpRect2);
- snapDockedStackAfterRotation(mTmpRect2);
- final int newDockSide = getDockSide(mTmpRect2);
-
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ switch (mStackId) {
+ case DOCKED_STACK_ID:
+ repositionDockedStackAfterRotation(mTmpRect2);
+ snapDockedStackAfterRotation(mTmpRect2);
+ final int newDockSide = getDockSide(mTmpRect2);
+
+ // Update the dock create mode and clear the dock create bounds, these
+ // might change after a rotation and the original values will be invalid.
+ mService.setDockedStackCreateStateLocked(
+ (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ null);
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+ break;
}
mBoundsAfterRotation.set(mTmpRect2);
@@ -673,16 +677,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
}
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- final int prevWindowingMode = getWindowingMode();
- super.onConfigurationChanged(newParentConfig);
- if (mDisplayContent != null && prevWindowingMode != getWindowingMode()) {
- mDisplayContent.onStackWindowingModeChanged(this);
- }
- }
-
- @Override
void onDisplayChanged(DisplayContent dc) {
if (mDisplayContent != null) {
throw new IllegalStateException("onDisplayChanged: Already attached");
@@ -693,8 +687,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
"animation background stackId=" + mStackId);
Rect bounds = null;
- final TaskStack dockedStack = dc.getSplitScreenPrimaryStackStackIgnoringVisibility();
- if (inSplitScreenPrimaryWindowingMode()
+ final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
+ if (mStackId == DOCKED_STACK_ID
|| (dockedStack != null && inSplitScreenSecondaryWindowingMode()
&& !dockedStack.fillsParent())) {
// The existence of a docked stack affects the size of other static stack created since
@@ -709,10 +703,10 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
+ getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
- } else if (inPinnedWindowingMode()) {
+ } else if (mStackId == PINNED_STACK_ID) {
// Update the bounds based on any changes to the display info
getAnimationOrCurrentBounds(mTmpRect2);
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
@@ -772,8 +766,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return;
}
- final TaskStack dockedStack =
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack dockedStack = mDisplayContent.getDockedStackIgnoringVisibility();
if (dockedStack == null) {
// Not sure why you are calling this method when there is no docked stack...
throw new IllegalStateException(
@@ -798,7 +791,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
mDisplayContent.getLogicalDisplayRect(mTmpRect);
dockedStack.getRawBounds(mTmpRect2);
final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(mTmpRect, outStackBounds, mTmpRect2,
+ getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
@@ -807,15 +800,16 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
* Outputs the bounds a stack should be given the presence of a docked stack on the display.
* @param displayRect The bounds of the display the docked stack is on.
* @param outBounds Output bounds that should be used for the stack.
+ * @param stackId Id of stack we are calculating the bounds for.
* @param dockedBounds Bounds of the docked stack.
* @param dockDividerWidth We need to know the width of the divider make to the output bounds
* close to the side of the dock.
* @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
private void getStackDockedModeBounds(
- Rect displayRect, Rect outBounds, Rect dockedBounds, int dockDividerWidth,
+ Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
boolean dockOnTopOrLeft) {
- final boolean dockedStack = inSplitScreenPrimaryWindowingMode();
+ final boolean dockedStack = stackId == DOCKED_STACK_ID;
final boolean splitHorizontally = displayRect.width() > displayRect.height();
outBounds.set(displayRect);
@@ -872,7 +866,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
void resetDockedStackToMiddle() {
- if (inSplitScreenPrimaryWindowingMode()) {
+ if (mStackId != DOCKED_STACK_ID) {
throw new IllegalStateException("Not a docked stack=" + this);
}
@@ -900,12 +894,17 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
@Override
- void onParentSet() {
- if (getParent() != null || mDisplayContent == null) {
- return;
- }
+ void removeImmediately() {
+ super.removeImmediately();
+
+ onRemovedFromDisplay();
+ }
- // Looks like the stack was removed from the display. Go ahead and clean things up.
+ /**
+ * Removes the stack it from its current parent, so it can be either destroyed completely or
+ * re-parented.
+ */
+ void onRemovedFromDisplay() {
mDisplayContent.mDimLayerController.removeDimLayerUser(this);
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
@@ -914,7 +913,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
mAnimationBackgroundSurface = null;
}
- if (inSplitScreenPrimaryWindowingMode()) {
+ if (mStackId == DOCKED_STACK_ID) {
mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
@@ -1036,8 +1035,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
boolean shouldIgnoreInput() {
- return isAdjustedForMinimizedDockedStack() ||
- (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
+ return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID &&
+ isMinimizedDockAndHomeStackResizable();
}
/**
@@ -1472,7 +1471,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
postExclude.set(mTmpRect);
}
- final boolean isFreeformed = task.inFreeformWindowingMode();
+ final boolean isFreeformed = task.inFreeformWorkspace();
if (task != focusedTask || isFreeformed) {
if (isFreeformed) {
// If the task is freeformed, enlarge the area to account for outside
@@ -1530,7 +1529,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
}
- if (inPinnedWindowingMode()) {
+ if (mStackId == PINNED_STACK_ID) {
try {
mService.mActivityManager.notifyPinnedStackAnimationStarted();
} catch (RemoteException e) {
@@ -1562,7 +1561,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
mService.requestTraversal();
}
- if (inPinnedWindowingMode()) {
+ if (mStackId == PINNED_STACK_ID) {
// Update to the final bounds if requested. This is done here instead of in the bounds
// animator to allow us to coordinate this after we notify the PiP mode changed
@@ -1596,7 +1595,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
* bounds and we have a deferred PiP mode changed callback set with the animation.
*/
public boolean deferScheduleMultiWindowModeChanged() {
- if (inPinnedWindowingMode()) {
+ if (mStackId == PINNED_STACK_ID) {
return (mBoundsAnimatingRequested || mBoundsAnimating);
}
return false;
diff --git a/com/android/server/wm/WallpaperController.java b/com/android/server/wm/WallpaperController.java
index 629cc868..7213c951 100644
--- a/com/android/server/wm/WallpaperController.java
+++ b/com/android/server/wm/WallpaperController.java
@@ -18,7 +18,7 @@ package com.android.server.wm;
import com.android.internal.util.ToBooleanFunction;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -447,7 +447,7 @@ class WallpaperController {
private void findWallpaperTarget(DisplayContent dc) {
mFindResults.reset();
- if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
+ if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
// additional window to make it visible.
mFindResults.setUseTopWallpaperAsTarget(true);
diff --git a/com/android/server/wm/WindowContainer.java b/com/android/server/wm/WindowContainer.java
index 1b0825e5..40923c82 100644
--- a/com/android/server/wm/WindowContainer.java
+++ b/com/android/server/wm/WindowContainer.java
@@ -73,12 +73,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
- protected int getChildCount() {
+ final protected int getChildCount() {
return mChildren.size();
}
@Override
- protected E getChildAt(int index) {
+ final protected E getChildAt(int index) {
return mChildren.get(index);
}
diff --git a/com/android/server/wm/WindowManagerDebugConfig.java b/com/android/server/wm/WindowManagerDebugConfig.java
index 9d9805ab..6d5673e2 100644
--- a/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/com/android/server/wm/WindowManagerDebugConfig.java
@@ -60,6 +60,7 @@ public class WindowManagerDebugConfig {
static final boolean DEBUG_SCREENSHOT = false;
static final boolean DEBUG_BOOT = false;
static final boolean DEBUG_LAYOUT_REPEATS = false;
+ static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
static final boolean DEBUG_TASK_POSITIONING = false;
diff --git a/com/android/server/wm/WindowManagerService.java b/com/android/server/wm/WindowManagerService.java
index b133bd45..1fb21887 100644
--- a/com/android/server/wm/WindowManagerService.java
+++ b/com/android/server/wm/WindowManagerService.java
@@ -2384,7 +2384,7 @@ public class WindowManagerService extends IWindowManager.Stub
final Rect insets = new Rect();
final Rect stableInsets = new Rect();
Rect surfaceInsets = null;
- final boolean freeform = win != null && win.inFreeformWindowingMode();
+ final boolean freeform = win != null && win.inFreeformWorkspace();
if (win != null) {
// Containing frame will usually cover the whole screen, including dialog windows.
// For freeform workspace windows it will not cover the whole screen and it also
@@ -2794,7 +2794,7 @@ public class WindowManagerService extends IWindowManager.Stub
for (final WindowState win : mWindowMap.values()) {
final Task task = win.getTask();
if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1
- && task.inFreeformWindowingMode()) {
+ && task.inFreeformWorkspace()) {
final AppWindowToken appToken = win.mAppToken;
if (appToken != null && appToken.mAppAnimator != null) {
appToken.mAppAnimator.startProlongAnimation(scaleUp ?
@@ -3391,8 +3391,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Notify whether the docked stack exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final TaskStack stack =
- displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack = displayContent.getDockedStackIgnoringVisibility();
displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
stack != null && stack.hasTaskForUser(newUserId));
@@ -6899,6 +6898,11 @@ public class WindowManagerService extends IWindowManager.Stub
dumpSessionsLocked(pw, true);
}
return;
+ } else if ("surfaces".equals(cmd)) {
+ synchronized(mWindowMap) {
+ WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, null);
+ }
+ return;
} else if ("displays".equals(cmd) || "d".equals(cmd)) {
synchronized(mWindowMap) {
mRoot.dumpDisplayContents(pw);
@@ -6963,6 +6967,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
+ WindowSurfaceController.SurfaceTrace.dumpAllSurfaces(pw, dumpAll ?
+ "-------------------------------------------------------------------------------"
+ : null);
+ pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
@@ -7124,7 +7132,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getDockedStackSide() {
synchronized (mWindowMap) {
final TaskStack dockedStack = getDefaultDisplayContentLocked()
- .getSplitScreenPrimaryStackStackIgnoringVisibility();
+ .getDockedStackIgnoringVisibility();
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
}
}
@@ -7596,10 +7604,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean isStackVisible(int windowingMode) {
+ public boolean isStackVisible(int stackId) {
synchronized (mWindowMap) {
final DisplayContent dc = getDefaultDisplayContentLocked();
- return dc.isStackVisible(windowingMode);
+ return dc.isStackVisible(stackId);
}
}
diff --git a/com/android/server/wm/WindowState.java b/com/android/server/wm/WindowState.java
index e1715284..4ff0f391 100644
--- a/com/android/server/wm/WindowState.java
+++ b/com/android/server/wm/WindowState.java
@@ -16,7 +16,10 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -80,6 +83,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -811,12 +815,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final WindowState imeWin = mService.mInputMethodWindow;
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) {
- if (inFreeformWindowingMode()
+ final int stackId = getStackId();
+ if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& mContainingFrame.bottom > contentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
- } else if (!inPinnedWindowingMode()
+ } else if (stackId != PINNED_STACK_ID
&& mContainingFrame.bottom > parentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
@@ -893,7 +898,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// For pinned workspace the frame isn't limited in any particular
// way since SystemUI controls the bounds. For freeform however
// we want to keep things inside the content frame.
- final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
+ final Rect limitFrame = task.inPinnedWorkspace() ? mFrame : mContentFrame;
// Keep the frame out of the blocked system area, limit it in size to the content area
// and make sure that there is always a minimum visible so that the user can drag it
// into a usable area..
@@ -1205,7 +1210,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// application when it has finished drawing.
if (getOrientationChanging() || dragResizingChanged
|| isResizedWhileNotDragResizing()) {
- if (DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+ ", mDrawState=DRAW_PENDING in " + this
+ ", surfaceController " + winAnimator.mSurfaceController);
@@ -1657,9 +1662,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
//
// Anyway we don't need to synchronize position and content updates for these
// windows since they aren't at the base layer and could be moved around anyway.
- if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION
- && !mWinAnimator.isForceScaled() && !isGoneForLayoutLw()
- && !getTask().inPinnedWindowingMode()) {
+ if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION &&
+ !mWinAnimator.isForceScaled() && !isGoneForLayoutLw() &&
+ !getTask().inPinnedWorkspace()) {
setResizedWhileNotDragResizing(true);
}
}
@@ -2191,6 +2196,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ // TODO: Strange usage of word workspace here and above.
+ boolean inPinnedWorkspace() {
+ final Task task = getTask();
+ return task != null && task.inPinnedWorkspace();
+ }
+
void applyAdjustForImeIfNeeded() {
final Task task = getTask();
if (task != null && task.mStack != null && task.mStack.isAdjustedForIme()) {
@@ -2224,7 +2235,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
getVisibleBounds(mTmpRect);
}
- if (inFreeformWindowingMode()) {
+ if (inFreeformWorkspace()) {
// For freeform windows we the touch region to include the whole surface for the
// shadows.
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
@@ -2360,8 +2371,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// just in case they have the divider at an unstable position. Better
// also reset drag resizing state, because the owner can't do it
// anymore.
- final TaskStack stack =
- dc.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack = dc.getDockedStackIgnoringVisibility();
if (stack != null) {
stack.resetDockedStackToMiddle();
}
@@ -2928,7 +2938,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mTmpRect;
}
- private int getStackId() {
+ @Override
+ public int getStackId() {
final TaskStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
@@ -2972,6 +2983,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ boolean inFreeformWorkspace() {
+ final Task task = getTask();
+ return task != null && task.inFreeformWorkspace();
+ }
+
@Override
public boolean isInMultiWindowMode() {
final Task task = getTask();
@@ -3089,7 +3105,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// background.
return (getDisplayContent().mDividerControllerLocked.isResizing()
|| mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWindowingMode() && !isGoneForLayoutLw();
+ !task.inFreeformWorkspace() && !isGoneForLayoutLw();
}
@@ -3679,7 +3695,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
- if (DEBUG_ANIM) Slog.v(TAG,
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) Slog.v(TAG,
"performShowLocked: mDrawState=HAS_DRAWN in " + this);
mWinAnimator.mDrawState = HAS_DRAWN;
mService.scheduleAnimationLocked();
@@ -3740,7 +3756,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
windowInfo.focused = isFocused();
Task task = getTask();
- windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
+ windowInfo.inPictureInPicture = (task != null) && task.inPinnedWorkspace();
if (mIsChildWindow) {
windowInfo.parentToken = getParentWindow().mClient.asBinder();
@@ -4203,7 +4219,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// If a freeform window is animating from a position where it would be cutoff, it would be
// cutoff during the animation. We don't want that, so for the duration of the animation
// we ignore the decor cropping and depend on layering to position windows correctly.
- final boolean cropToDecor = !(inFreeformWindowingMode() && isAnimatingLw());
+ final boolean cropToDecor = !(inFreeformWorkspace() && isAnimatingLw());
if (cropToDecor) {
// Intersect with the decor rect, offsetted by window position.
systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
@@ -4287,7 +4303,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// scale for the animation using the source hint rect
// (see WindowStateAnimator#setSurfaceBoundariesLocked()).
if (isDragResizeChanged() || isResizedWhileNotDragResizing()
- || (surfaceInsetsChanging() && !inPinnedWindowingMode())) {
+ || (surfaceInsetsChanging() && !inPinnedWorkspace())) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
setDragResizing();
diff --git a/com/android/server/wm/WindowStateAnimator.java b/com/android/server/wm/WindowStateAnimator.java
index 52669031..1b7e5278 100644
--- a/com/android/server/wm/WindowStateAnimator.java
+++ b/com/android/server/wm/WindowStateAnimator.java
@@ -31,6 +31,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEAT
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
@@ -508,7 +509,7 @@ class WindowStateAnimator {
boolean layoutNeeded = false;
if (mDrawState == DRAW_PENDING) {
- if (DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in "
+ mSurfaceController);
if (DEBUG_STARTING_WINDOW && startingWindow) {
@@ -531,7 +532,7 @@ class WindowStateAnimator {
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
- if (DEBUG_ANIM) {
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
}
mDrawState = READY_TO_SHOW;
@@ -1032,7 +1033,7 @@ class WindowStateAnimator {
//Slog.i(TAG_WM, "Not applying alpha transform");
}
- if ((DEBUG_ANIM || WindowManagerService.localLOGV)
+ if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV)
&& (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
+ " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
@@ -1111,7 +1112,7 @@ class WindowStateAnimator {
*/
private boolean useFinalClipRect() {
return (isAnimationSet() && resolveStackClip() == STACK_CLIP_AFTER_ANIM)
- || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWindowingMode();
+ || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWorkspace();
}
/**
@@ -1176,7 +1177,7 @@ class WindowStateAnimator {
return false;
}
- if (w.inPinnedWindowingMode()) {
+ if (w.inPinnedWorkspace()) {
return false;
}
diff --git a/com/android/server/wm/WindowSurfaceController.java b/com/android/server/wm/WindowSurfaceController.java
index d56df55d..2e1e3f76 100644
--- a/com/android/server/wm/WindowSurfaceController.java
+++ b/com/android/server/wm/WindowSurfaceController.java
@@ -22,6 +22,7 @@ import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -564,4 +565,262 @@ class WindowSurfaceController {
public String toString() {
return mSurfaceControl.toString();
}
+
+ static class SurfaceTrace extends SurfaceControl {
+ private final static String SURFACE_TAG = TAG_WITH_CLASS_NAME ? "SurfaceTrace" : TAG_WM;
+ private final static boolean LOG_SURFACE_TRACE = DEBUG_SURFACE_TRACE;
+ final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
+
+ private float mSurfaceTraceAlpha = 0;
+ private int mLayer;
+ private final PointF mPosition = new PointF();
+ private final Point mSize = new Point();
+ private final Rect mWindowCrop = new Rect();
+ private final Rect mFinalCrop = new Rect();
+ private boolean mShown = false;
+ private int mLayerStack;
+ private boolean mIsOpaque;
+ private float mDsdx, mDtdx, mDsdy, mDtdy;
+ private final String mName;
+
+ public SurfaceTrace(SurfaceSession s, String name, int w, int h, int format, int flags,
+ int windowType, int ownerUid)
+ throws OutOfResourcesException {
+ super(s, name, w, h, format, flags, windowType, ownerUid);
+ mName = name != null ? name : "Not named";
+ mSize.set(w, h);
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.add(0, this);
+ }
+ }
+
+ public SurfaceTrace(SurfaceSession s,
+ String name, int w, int h, int format, int flags) {
+ super(s, name, w, h, format, flags);
+ mName = name != null ? name : "Not named";
+ mSize.set(w, h);
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.add(0, this);
+ }
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ if (mSurfaceTraceAlpha != alpha) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setAlpha(" + alpha + "): OLD:" + this +
+ ". Called by " + Debug.getCallers(3));
+ mSurfaceTraceAlpha = alpha;
+ }
+ super.setAlpha(alpha);
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ if (zorder != mLayer) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayer(" + zorder + "): OLD:" + this
+ + ". Called by " + Debug.getCallers(3));
+ mLayer = zorder;
+ }
+ super.setLayer(zorder);
+
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ int i;
+ for (i = sSurfaces.size() - 1; i >= 0; i--) {
+ SurfaceTrace s = sSurfaces.get(i);
+ if (s.mLayer < zorder) {
+ break;
+ }
+ }
+ sSurfaces.add(i + 1, this);
+ }
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ if (x != mPosition.x || y != mPosition.y) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPosition(" + x + "," + y + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mPosition.set(x, y);
+ }
+ super.setPosition(x, y);
+ }
+
+ @Override
+ public void setGeometryAppliesWithResize() {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setGeometryAppliesWithResize(): OLD: "
+ + this + ". Called by" + Debug.getCallers(3));
+ super.setGeometryAppliesWithResize();
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ if (w != mSize.x || h != mSize.y) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mSize.set(w, h);
+ }
+ super.setSize(w, h);
+ }
+
+ @Override
+ public void setWindowCrop(Rect crop) {
+ if (crop != null) {
+ if (!crop.equals(mWindowCrop)) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setWindowCrop("
+ + crop.toShortString() + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mWindowCrop.set(crop);
+ }
+ }
+ super.setWindowCrop(crop);
+ }
+
+ @Override
+ public void setFinalCrop(Rect crop) {
+ if (crop != null) {
+ if (!crop.equals(mFinalCrop)) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop("
+ + crop.toShortString() + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mFinalCrop.set(crop);
+ }
+ }
+ super.setFinalCrop(crop);
+ }
+
+ @Override
+ public void setLayerStack(int layerStack) {
+ if (layerStack != mLayerStack) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mLayerStack = layerStack;
+ }
+ super.setLayerStack(layerStack);
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ if (isOpaque != mIsOpaque) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setOpaque(" + isOpaque + "): OLD:"
+ + this + ". Called by " + Debug.getCallers(3));
+ mIsOpaque = isOpaque;
+ }
+ super.setOpaque(isOpaque);
+ }
+
+ @Override
+ public void setSecure(boolean isSecure) {
+ super.setSecure(isSecure);
+ }
+
+ @Override
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + ","
+ + dsdy + "," + dtdy + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mDsdx = dsdx;
+ mDtdx = dtdx;
+ mDsdy = dsdy;
+ mDtdy = dtdy;
+ }
+ super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ @Override
+ public void hide() {
+ if (mShown) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "hide: OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mShown = false;
+ }
+ super.hide();
+ }
+
+ @Override
+ public void show() {
+ if (!mShown) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "show: OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mShown = true;
+ }
+ super.show();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ }
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "release: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.remove(this);
+ }
+ }
+
+ @Override
+ public void setTransparentRegionHint(Region region) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setTransparentRegionHint(" + region
+ + "): OLD: " + this + " . Called by " + Debug.getCallers(3));
+ super.setTransparentRegionHint(region);
+ }
+
+ static void dumpAllSurfaces(PrintWriter pw, String header) {
+ synchronized (sSurfaces) {
+ final int N = sSurfaces.size();
+ if (N <= 0) {
+ return;
+ }
+ if (header != null) {
+ pw.println(header);
+ }
+ pw.println("WINDOW MANAGER SURFACES (dumpsys window surfaces)");
+ for (int i = 0; i < N; i++) {
+ SurfaceTrace s = sSurfaces.get(i);
+ pw.print(" Surface #"); pw.print(i); pw.print(": #");
+ pw.print(Integer.toHexString(System.identityHashCode(s)));
+ pw.print(" "); pw.println(s.mName);
+ pw.print(" mLayerStack="); pw.print(s.mLayerStack);
+ pw.print(" mLayer="); pw.println(s.mLayer);
+ pw.print(" mShown="); pw.print(s.mShown); pw.print(" mAlpha=");
+ pw.print(s.mSurfaceTraceAlpha); pw.print(" mIsOpaque=");
+ pw.println(s.mIsOpaque);
+ pw.print(" mPosition="); pw.print(s.mPosition.x); pw.print(",");
+ pw.print(s.mPosition.y);
+ pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
+ pw.println(s.mSize.y);
+ pw.print(" mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
+ pw.print(" mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println();
+ pw.print(" Transform: ("); pw.print(s.mDsdx); pw.print(", ");
+ pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
+ pw.print(", "); pw.print(s.mDtdy); pw.println(")");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
+ + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
+ + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
+ + " " + mSize.x + "x" + mSize.y
+ + " crop=" + mWindowCrop.toShortString()
+ + " opaque=" + mIsOpaque
+ + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
+ }
+ }
}
diff --git a/com/android/server/wm/WindowSurfacePlacer.java b/com/android/server/wm/WindowSurfacePlacer.java
index fa33fe8f..af1fa2fe 100644
--- a/com/android/server/wm/WindowSurfacePlacer.java
+++ b/com/android/server/wm/WindowSurfacePlacer.java
@@ -449,9 +449,6 @@ class WindowSurfacePlacer {
// animating?
wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
wtoken.updateReportedVisibilityLocked();
- // setAllAppWinAnimators so the windows get onExitAnimationDone once the animation is
- // done.
- wtoken.setAllAppWinAnimators();
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
diff --git a/com/android/settingslib/CustomEditTextPreference.java b/com/android/settingslib/CustomEditTextPreference.java
index 90124f1f..253ca11b 100644
--- a/com/android/settingslib/CustomEditTextPreference.java
+++ b/com/android/settingslib/CustomEditTextPreference.java
@@ -20,7 +20,6 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.support.annotation.CallSuper;
import android.support.v14.preference.EditTextPreferenceDialogFragment;
import android.support.v7.preference.EditTextPreference;
import android.util.AttributeSet;
@@ -31,8 +30,7 @@ public class CustomEditTextPreference extends EditTextPreference {
private CustomPreferenceDialogFragment mFragment;
- public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@@ -71,12 +69,7 @@ public class CustomEditTextPreference extends EditTextPreference {
protected void onClick(DialogInterface dialog, int which) {
}
- @CallSuper
protected void onBindDialogView(View view) {
- final EditText editText = view.findViewById(android.R.id.edit);
- if (editText != null) {
- editText.requestFocus();
- }
}
private void setFragment(CustomPreferenceDialogFragment fragment) {
diff --git a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
index f79be7ea..7998b2ef 100644
--- a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
+++ b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -33,27 +33,21 @@ public abstract class AbstractLogdSizePreferenceController extends
+ "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
- @VisibleForTesting
- static final String LOW_RAM_CONFIG_PROPERTY_KEY = "ro.config.low_ram";
private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size";
@VisibleForTesting
static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size";
static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag";
// Tricky, isLoggable only checks for first character, assumes silence
static final String SELECT_LOGD_TAG_SILENCE = "Settings";
- @VisibleForTesting
- static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
+ private static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log";
private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size";
@VisibleForTesting
static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144";
private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536";
// 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with.
- @VisibleForTesting
- static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
+ private static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768";
- @VisibleForTesting
- static final String DEFAULT_SNET_TAG = "I";
private ListPreference mLogdSize;
@@ -160,7 +154,7 @@ public abstract class AbstractLogdSizePreferenceController extends
if ((snetValue == null) || (snetValue.length() == 0)) {
snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY);
if ((snetValue == null) || (snetValue.length() == 0)) {
- SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, DEFAULT_SNET_TAG);
+ SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, "I");
}
}
// Silence all log sources, security logs notwithstanding
diff --git a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
index 77b2d86c..67553adc 100644
--- a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
+++ b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -68,7 +68,7 @@ public abstract class AbstractLogpersistPreferenceController extends
public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
- if (isAvailable() && lifecycle != null) {
+ if (isAvailable()) {
lifecycle.addObserver(this);
}
}
diff --git a/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
deleted file mode 100644
index ba358f83..00000000
--- a/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.annotation.SuppressLint;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-
-import com.android.settingslib.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-/**
- * Preference controller for bluetooth address
- */
-public abstract class AbstractBluetoothAddressPreferenceController
- extends AbstractConnectivityPreferenceController {
-
- @VisibleForTesting
- static final String KEY_BT_ADDRESS = "bt_address";
-
- private static final String[] CONNECTIVITY_INTENTS = {
- BluetoothAdapter.ACTION_STATE_CHANGED
- };
-
- private Preference mBtAddress;
-
- public AbstractBluetoothAddressPreferenceController(Context context, Lifecycle lifecycle) {
- super(context, lifecycle);
- }
-
- @Override
- public boolean isAvailable() {
- return BluetoothAdapter.getDefaultAdapter() != null;
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_BT_ADDRESS;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mBtAddress = screen.findPreference(KEY_BT_ADDRESS);
- updateConnectivity();
- }
-
- @Override
- protected String[] getConnectivityIntents() {
- return CONNECTIVITY_INTENTS;
- }
-
- @SuppressLint("HardwareIds")
- @Override
- protected void updateConnectivity() {
- BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
- if (bluetooth != null && mBtAddress != null) {
- String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
- if (!TextUtils.isEmpty(address)) {
- // Convert the address to lowercase for consistency with the wifi MAC address.
- mBtAddress.setSummary(address.toLowerCase());
- } else {
- mBtAddress.setSummary(R.string.status_unavailable);
- }
- }
- }
-}
diff --git a/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
deleted file mode 100644
index c6552f77..00000000
--- a/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.os.Message;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Base class for preference controllers which listen to connectivity broadcasts
- */
-public abstract class AbstractConnectivityPreferenceController
- extends AbstractPreferenceController implements LifecycleObserver, OnStart, OnStop {
-
- private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (ArrayUtils.contains(getConnectivityIntents(), action)) {
- getHandler().sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY);
- }
- }
- };
-
- private static final int EVENT_UPDATE_CONNECTIVITY = 600;
-
- private Handler mHandler;
-
- public AbstractConnectivityPreferenceController(Context context, Lifecycle lifecycle) {
- super(context);
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
- }
-
- @Override
- public void onStop() {
- mContext.unregisterReceiver(mConnectivityReceiver);
- }
-
- @Override
- public void onStart() {
- final IntentFilter connectivityIntentFilter = new IntentFilter();
- final String[] intents = getConnectivityIntents();
- for (String intent : intents) {
- connectivityIntentFilter.addAction(intent);
- }
-
- mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter,
- android.Manifest.permission.CHANGE_NETWORK_STATE, null);
- }
-
- protected abstract String[] getConnectivityIntents();
-
- protected abstract void updateConnectivity();
-
- private Handler getHandler() {
- if (mHandler == null) {
- mHandler = new ConnectivityEventHandler(this);
- }
- return mHandler;
- }
-
- private static class ConnectivityEventHandler extends Handler {
- private WeakReference<AbstractConnectivityPreferenceController> mPreferenceController;
-
- public ConnectivityEventHandler(AbstractConnectivityPreferenceController activity) {
- mPreferenceController = new WeakReference<>(activity);
- }
-
- @Override
- public void handleMessage(Message msg) {
- AbstractConnectivityPreferenceController preferenceController
- = mPreferenceController.get();
- if (preferenceController == null) {
- return;
- }
-
- switch (msg.what) {
- case EVENT_UPDATE_CONNECTIVITY:
- preferenceController.updateConnectivity();
- break;
- default:
- throw new IllegalStateException("Unknown message " + msg.what);
- }
- }
- }
-}
diff --git a/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
deleted file mode 100644
index bb8404b0..00000000
--- a/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
-import android.os.PersistableBundle;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-
-import com.android.settingslib.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-/**
- * Preference controller for IMS status
- */
-public abstract class AbstractImsStatusPreferenceController
- extends AbstractConnectivityPreferenceController {
-
- @VisibleForTesting
- static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state";
-
- private static final String[] CONNECTIVITY_INTENTS = {
- BluetoothAdapter.ACTION_STATE_CHANGED,
- ConnectivityManager.CONNECTIVITY_ACTION,
- WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
- WifiManager.NETWORK_STATE_CHANGED_ACTION,
- };
-
- private Preference mImsStatus;
-
- public AbstractImsStatusPreferenceController(Context context,
- Lifecycle lifecycle) {
- super(context, lifecycle);
- }
-
- @Override
- public boolean isAvailable() {
- CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- PersistableBundle config = null;
- if (configManager != null) {
- config = configManager.getConfigForSubId(subId);
- }
- return config != null && config.getBoolean(
- CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_IMS_REGISTRATION_STATE;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mImsStatus = screen.findPreference(KEY_IMS_REGISTRATION_STATE);
- updateConnectivity();
- }
-
- @Override
- protected String[] getConnectivityIntents() {
- return CONNECTIVITY_INTENTS;
- }
-
- @Override
- protected void updateConnectivity() {
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- if (mImsStatus != null) {
- TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ?
- R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered);
- }
- }
-}
diff --git a/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
deleted file mode 100644
index ded30226..00000000
--- a/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.wifi.WifiManager;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settingslib.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import java.net.InetAddress;
-import java.util.Iterator;
-
-/**
- * Preference controller for IP address
- */
-public abstract class AbstractIpAddressPreferenceController
- extends AbstractConnectivityPreferenceController {
-
- @VisibleForTesting
- static final String KEY_IP_ADDRESS = "wifi_ip_address";
-
- private static final String[] CONNECTIVITY_INTENTS = {
- ConnectivityManager.CONNECTIVITY_ACTION,
- WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
- WifiManager.NETWORK_STATE_CHANGED_ACTION,
- };
-
- private Preference mIpAddress;
- private final ConnectivityManager mCM;
-
- public AbstractIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
- super(context, lifecycle);
- mCM = context.getSystemService(ConnectivityManager.class);
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_IP_ADDRESS;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mIpAddress = screen.findPreference(KEY_IP_ADDRESS);
- updateConnectivity();
- }
-
- @Override
- protected String[] getConnectivityIntents() {
- return CONNECTIVITY_INTENTS;
- }
-
- @Override
- protected void updateConnectivity() {
- String ipAddress = getDefaultIpAddresses(mCM);
- if (ipAddress != null) {
- mIpAddress.setSummary(ipAddress);
- } else {
- mIpAddress.setSummary(R.string.status_unavailable);
- }
- }
-
- /**
- * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
- * addresses.
- * @param cm ConnectivityManager
- * @return the formatted and newline-separated IP addresses, or null if none.
- */
- private static String getDefaultIpAddresses(ConnectivityManager cm) {
- LinkProperties prop = cm.getActiveLinkProperties();
- return formatIpAddresses(prop);
- }
-
- private static String formatIpAddresses(LinkProperties prop) {
- if (prop == null) return null;
- Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
- // If there are no entries, return null
- if (!iter.hasNext()) return null;
- // Concatenate all available addresses, newline separated
- StringBuilder addresses = new StringBuilder();
- while (iter.hasNext()) {
- addresses.append(iter.next().getHostAddress());
- if (iter.hasNext()) addresses.append("\n");
- }
- return addresses.toString();
- }
-}
diff --git a/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
deleted file mode 100644
index ac61ade1..00000000
--- a/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.format.DateUtils;
-
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Preference controller for uptime
- */
-public abstract class AbstractUptimePreferenceController extends AbstractPreferenceController
- implements LifecycleObserver, OnStart, OnStop {
-
- @VisibleForTesting
- static final String KEY_UPTIME = "up_time";
- private static final int EVENT_UPDATE_STATS = 500;
-
- private Preference mUptime;
- private Handler mHandler;
-
- public AbstractUptimePreferenceController(Context context, Lifecycle lifecycle) {
- super(context);
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
- }
-
- @Override
- public void onStart() {
- getHandler().sendEmptyMessage(EVENT_UPDATE_STATS);
- }
-
- @Override
- public void onStop() {
- getHandler().removeMessages(EVENT_UPDATE_STATS);
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_UPTIME;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mUptime = screen.findPreference(KEY_UPTIME);
- updateTimes();
- }
-
- private Handler getHandler() {
- if (mHandler == null) {
- mHandler = new MyHandler(this);
- }
- return mHandler;
- }
-
- private void updateTimes() {
- mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
- }
-
- private static class MyHandler extends Handler {
- private WeakReference<AbstractUptimePreferenceController> mStatus;
-
- public MyHandler(AbstractUptimePreferenceController activity) {
- mStatus = new WeakReference<>(activity);
- }
-
- @Override
- public void handleMessage(Message msg) {
- AbstractUptimePreferenceController status = mStatus.get();
- if (status == null) {
- return;
- }
-
- switch (msg.what) {
- case EVENT_UPDATE_STATS:
- status.updateTimes();
- sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
- break;
-
- default:
- throw new IllegalStateException("Unknown message " + msg.what);
- }
- }
- }
-}
diff --git a/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
deleted file mode 100644
index d57b64f0..00000000
--- a/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-
-import com.android.settingslib.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-/**
- * Preference controller for WIFI MAC address
- */
-public abstract class AbstractWifiMacAddressPreferenceController
- extends AbstractConnectivityPreferenceController {
-
- @VisibleForTesting
- static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
-
- private static final String[] CONNECTIVITY_INTENTS = {
- ConnectivityManager.CONNECTIVITY_ACTION,
- WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
- WifiManager.NETWORK_STATE_CHANGED_ACTION,
- };
-
- private Preference mWifiMacAddress;
- private final WifiManager mWifiManager;
-
- public AbstractWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) {
- super(context, lifecycle);
- mWifiManager = context.getSystemService(WifiManager.class);
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_WIFI_MAC_ADDRESS;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
- updateConnectivity();
- }
-
- @Override
- protected String[] getConnectivityIntents() {
- return CONNECTIVITY_INTENTS;
- }
-
- @SuppressLint("HardwareIds")
- @Override
- protected void updateConnectivity() {
- WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
- if (!TextUtils.isEmpty(macAddress)) {
- mWifiMacAddress.setSummary(macAddress);
- } else {
- mWifiMacAddress.setSummary(R.string.status_unavailable);
- }
- }
-}
diff --git a/com/android/settingslib/suggestions/SuggestionParser.java b/com/android/settingslib/suggestions/SuggestionParser.java
index 9c347631..56b84415 100644
--- a/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/com/android/settingslib/suggestions/SuggestionParser.java
@@ -261,7 +261,7 @@ public class SuggestionParser {
if (requiredAccountType == null) {
return true;
}
- AccountManager accountManager = mContext.getSystemService(AccountManager.class);
+ AccountManager accountManager = AccountManager.get(mContext);
Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
boolean satisfiesRequiredAccount = accounts.length > 0;
if (!satisfiesRequiredAccount) {
diff --git a/com/android/settingslib/wifi/WifiTracker.java b/com/android/settingslib/wifi/WifiTracker.java
index c56e1da1..12455d85 100644
--- a/com/android/settingslib/wifi/WifiTracker.java
+++ b/com/android/settingslib/wifi/WifiTracker.java
@@ -291,6 +291,15 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
/**
+ * Force a scan for wifi networks to happen now.
+ */
+ public void forceScan() {
+ if (mWifiManager.isWifiEnabled() && mScanner != null) {
+ mScanner.forceScan();
+ }
+ }
+
+ /**
* Temporarily stop scanning for wifi networks.
*/
public void pauseScanning() {
@@ -780,6 +789,14 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
}
+ public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
+ boolean includeScans) {
+ WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
+ tracker.forceUpdate();
+ tracker.copyAndNotifyListeners(false /*notifyListeners*/);
+ return tracker.getAccessPoints();
+ }
+
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -969,6 +986,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
}
+ void forceScan() {
+ removeMessages(MSG_SCAN);
+ sendEmptyMessage(MSG_SCAN);
+ }
+
void pause() {
mRetry = 0;
removeMessages(MSG_SCAN);
diff --git a/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/com/android/setupwizardlib/test/util/DrawingTestActivity.java
index 154339a7..3d11e128 100644
--- a/com/android/setupwizardlib/test/util/DrawingTestActivity.java
+++ b/com/android/setupwizardlib/test/util/DrawingTestActivity.java
@@ -16,15 +16,14 @@
package com.android.setupwizardlib.test.util;
-import android.support.v7.app.AppCompatActivity;
+import android.app.Activity;
/**
* Activity to test view and drawable drawing behaviors. This is used to make sure that the drawing
- * behavior tested is the same as it would when inflated as part of an {@link AppCompatActivity},
- * including custom layout inflaters and theme values that the support library injects to the
- * activity.
+ * behavior tested is the same as it would when inflated as part of an activity, including any
+ * injected layout inflater factories and custom themes etc.
*
* @see DrawingTestHelper
*/
-public class DrawingTestActivity extends AppCompatActivity {
+public class DrawingTestActivity extends Activity {
}
diff --git a/com/android/setupwizardlib/util/WizardManagerHelper.java b/com/android/setupwizardlib/util/WizardManagerHelper.java
index 896c0137..a93694c1 100644
--- a/com/android/setupwizardlib/util/WizardManagerHelper.java
+++ b/com/android/setupwizardlib/util/WizardManagerHelper.java
@@ -45,8 +45,6 @@ public class WizardManagerHelper {
static final String EXTRA_IS_FIRST_RUN = "firstRun";
@VisibleForTesting
static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
- @VisibleForTesting
- static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
public static final String EXTRA_THEME = "theme";
public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
@@ -215,18 +213,6 @@ public class WizardManagerHelper {
}
/**
- * Checks whether an intent is running in "pre-deferred" setup wizard flow.
- *
- * @param originalIntent The original intent that was used to start the step, usually via
- * {@link android.app.Activity#getIntent()}.
- * @return true if the intent passed in was running in "pre-deferred" setup wizard.
- */
- public static boolean isPreDeferredSetupWizard(Intent originalIntent) {
- return originalIntent != null
- && originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false);
- }
-
- /**
* Checks the intent whether the extra indicates that the light theme should be used or not. If
* the theme is not specified in the intent, or the theme specified is unknown, the value def
* will be returned.
diff --git a/com/android/setupwizardlib/util/WizardManagerHelperTest.java b/com/android/setupwizardlib/util/WizardManagerHelperTest.java
index 6477b518..4c460c8c 100644
--- a/com/android/setupwizardlib/util/WizardManagerHelperTest.java
+++ b/com/android/setupwizardlib/util/WizardManagerHelperTest.java
@@ -88,14 +88,6 @@ public class WizardManagerHelperTest {
}
@Test
- public void testIsPreDeferredSetupTrue() {
- final Intent intent = new Intent();
- intent.putExtra("preDeferredSetup", true);
- assertTrue("Is pre-deferred setup wizard should be true",
- WizardManagerHelper.isPreDeferredSetupWizard(intent));
- }
-
- @Test
public void testIsSetupWizardFalse() {
final Intent intent = new Intent();
intent.putExtra("firstRun", false);
diff --git a/com/android/setupwizardlib/view/NavigationBarButton.java b/com/android/setupwizardlib/view/NavigationBarButton.java
index 5172c476..45d3737c 100644
--- a/com/android/setupwizardlib/view/NavigationBarButton.java
+++ b/com/android/setupwizardlib/view/NavigationBarButton.java
@@ -17,143 +17,16 @@
package com.android.setupwizardlib.view;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.widget.Button;
-/**
- * Button for navigation bar, which includes tinting of its compound drawables to be used for dark
- * and light themes.
- */
public class NavigationBarButton extends Button {
public NavigationBarButton(Context context) {
super(context);
- init();
}
public NavigationBarButton(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
- }
-
- private void init() {
- // Unfortunately, drawableStart and drawableEnd set through XML does not call the setter,
- // so manually getting it and wrapping it in the compat drawable.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- Drawable[] drawables = getCompoundDrawablesRelative();
- for (int i = 0; i < drawables.length; i++) {
- if (drawables[i] != null) {
- drawables[i] = TintedDrawable.wrap(drawables[i]);
- }
- }
- setCompoundDrawablesRelativeWithIntrinsicBounds(drawables[0], drawables[1],
- drawables[2], drawables[3]);
- }
- }
-
- @Override
- public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
- if (left != null) left = TintedDrawable.wrap(left);
- if (top != null) top = TintedDrawable.wrap(top);
- if (right != null) right = TintedDrawable.wrap(right);
- if (bottom != null) bottom = TintedDrawable.wrap(bottom);
- super.setCompoundDrawables(left, top, right, bottom);
- tintDrawables();
- }
-
- @Override
- public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end,
- Drawable bottom) {
- if (start != null) start = TintedDrawable.wrap(start);
- if (top != null) top = TintedDrawable.wrap(top);
- if (end != null) end = TintedDrawable.wrap(end);
- if (bottom != null) bottom = TintedDrawable.wrap(bottom);
- super.setCompoundDrawablesRelative(start, top, end, bottom);
- tintDrawables();
- }
-
- @Override
- public void setTextColor(ColorStateList colors) {
- super.setTextColor(colors);
- tintDrawables();
- }
-
- private void tintDrawables() {
- final ColorStateList textColors = getTextColors();
- if (textColors != null) {
- for (Drawable drawable : getAllCompoundDrawables()) {
- if (drawable instanceof TintedDrawable) {
- ((TintedDrawable) drawable).setTintListCompat(textColors);
- }
- }
- invalidate();
- }
- }
-
- private Drawable[] getAllCompoundDrawables() {
- Drawable[] drawables = new Drawable[6];
- Drawable[] compoundDrawables = getCompoundDrawables();
- drawables[0] = compoundDrawables[0]; // left
- drawables[1] = compoundDrawables[1]; // top
- drawables[2] = compoundDrawables[2]; // right
- drawables[3] = compoundDrawables[3]; // bottom
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative();
- drawables[4] = compoundDrawablesRelative[0]; // start
- drawables[5] = compoundDrawablesRelative[2]; // end
- }
- return drawables;
- }
-
- // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0
- // or above
- private static class TintedDrawable extends LayerDrawable {
-
- public static TintedDrawable wrap(Drawable drawable) {
- if (drawable instanceof TintedDrawable) {
- return (TintedDrawable) drawable;
- }
- return new TintedDrawable(drawable.mutate());
- }
-
- private ColorStateList mTintList = null;
-
- TintedDrawable(Drawable wrapped) {
- super(new Drawable[] { wrapped });
- }
-
- @Override
- public boolean isStateful() {
- return true;
- }
-
- @Override
- public boolean setState(@NonNull int[] stateSet) {
- boolean needsInvalidate = super.setState(stateSet);
- boolean needsInvalidateForState = updateState();
- return needsInvalidate || needsInvalidateForState;
- }
-
- public void setTintListCompat(ColorStateList colors) {
- mTintList = colors;
- if (updateState()) {
- invalidateSelf();
- }
- }
-
- private boolean updateState() {
- if (mTintList != null) {
- final int color = mTintList.getColorForState(getState(), 0);
- setColorFilter(color, PorterDuff.Mode.SRC_IN);
- return true; // Needs invalidate
- }
- return false;
- }
}
}
diff --git a/com/android/setupwizardlib/view/RichTextView.java b/com/android/setupwizardlib/view/RichTextView.java
index e6bc9da0..5a78561f 100644
--- a/com/android/setupwizardlib/view/RichTextView.java
+++ b/com/android/setupwizardlib/view/RichTextView.java
@@ -17,11 +17,6 @@
package com.android.setupwizardlib.view;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.AppCompatTextView;
import android.text.Annotation;
import android.text.SpannableString;
import android.text.Spanned;
@@ -30,18 +25,22 @@ import android.text.style.ClickableSpan;
import android.text.style.TextAppearanceSpan;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.MotionEvent;
+import android.widget.TextView;
import com.android.setupwizardlib.span.LinkSpan;
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
import com.android.setupwizardlib.span.SpanHelper;
-import com.android.setupwizardlib.util.LinkAccessibilityHelper;
/**
* An extension of TextView that automatically replaces the annotation tags as specified in
* {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)}
+ *
+ * <p>Note: The accessibility interaction for ClickableSpans (and therefore LinkSpans) are built
+ * into platform in O, although the interaction paradigm is different. (See b/17726921). In this
+ * platform version, the links are exposed in the Local Context Menu of TalkBack instead of
+ * accessible directly through swiping.
*/
-public class RichTextView extends AppCompatTextView implements OnLinkClickListener {
+public class RichTextView extends TextView implements OnLinkClickListener {
/* static section */
@@ -89,22 +88,14 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
/* non-static section */
- private LinkAccessibilityHelper mAccessibilityHelper;
private OnLinkClickListener mOnLinkClickListener;
public RichTextView(Context context) {
super(context);
- init();
}
public RichTextView(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
- }
-
- private void init() {
- mAccessibilityHelper = new LinkAccessibilityHelper(this);
- ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
}
@Override
@@ -141,32 +132,6 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
return false;
}
- @Override
- protected boolean dispatchHoverEvent(MotionEvent event) {
- if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
-
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
- // b/26765507 causes drawableStart and drawableEnd to not get the right state on M. As a
- // workaround, set the state on those drawables directly.
- final int[] state = getDrawableState();
- for (Drawable drawable : getCompoundDrawablesRelative()) {
- if (drawable != null) {
- if (drawable.setState(state)) {
- invalidateDrawable(drawable);
- }
- }
- }
- }
- }
-
public void setOnLinkClickListener(OnLinkClickListener listener) {
mOnLinkClickListener = listener;
}
diff --git a/com/android/systemui/Dependency.java b/com/android/systemui/Dependency.java
index d8a47c5e..2937a250 100644
--- a/com/android/systemui/Dependency.java
+++ b/com/android/systemui/Dependency.java
@@ -22,8 +22,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.util.ArrayMap;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.NightDisplayController;
@@ -306,8 +304,6 @@ public class Dependency extends SystemUI {
mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
- mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
-
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/com/android/systemui/ImageWallpaper.java b/com/android/systemui/ImageWallpaper.java
index 593bb508..907a79e7 100644
--- a/com/android/systemui/ImageWallpaper.java
+++ b/com/android/systemui/ImageWallpaper.java
@@ -16,6 +16,11 @@
package com.android.systemui;
+import static android.opengl.GLES20.*;
+
+import static javax.microedition.khronos.egl.EGL10.*;
+
+import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.ComponentCallbacks2;
import android.graphics.Bitmap;
@@ -23,18 +28,31 @@ import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
+import android.opengl.GLUtils;
import android.os.AsyncTask;
+import android.os.SystemProperties;
import android.os.Trace;
+import android.renderscript.Matrix4f;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
/**
* Default built-in wallpaper that simply shows a static image.
@@ -46,13 +64,24 @@ public class ImageWallpaper extends WallpaperService {
private static final boolean DEBUG = false;
private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
- private WallpaperManager mWallpaperManager;
- private DrawableEngine mEngine;
+ static final boolean FIXED_SIZED_SURFACE = true;
+ static final boolean USE_OPENGL = true;
+
+ WallpaperManager mWallpaperManager;
+
+ DrawableEngine mEngine;
+
+ boolean mIsHwAccelerated;
@Override
public void onCreate() {
super.onCreate();
- mWallpaperManager = getSystemService(WallpaperManager.class);
+ mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
+
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (FIXED_SIZED_SURFACE && USE_OPENGL) {
+ mIsHwAccelerated = ActivityManager.isHighEndGfx();
+ }
}
@Override
@@ -69,12 +98,15 @@ public class ImageWallpaper extends WallpaperService {
}
class DrawableEngine extends Engine {
+ static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ static final int EGL_OPENGL_ES2_BIT = 4;
+
Bitmap mBackground;
int mBackgroundWidth = -1, mBackgroundHeight = -1;
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
int mLastRotation = -1;
- float mXOffset = 0f;
- float mYOffset = 0f;
+ float mXOffset = 0.5f;
+ float mYOffset = 0.5f;
float mScale = 1f;
private Display mDefaultDisplay;
@@ -85,6 +117,34 @@ public class ImageWallpaper extends WallpaperService {
int mLastXTranslation;
int mLastYTranslation;
+ private EGL10 mEgl;
+ private EGLDisplay mEglDisplay;
+ private EGLConfig mEglConfig;
+ private EGLContext mEglContext;
+ private EGLSurface mEglSurface;
+
+ private static final String sSimpleVS =
+ "attribute vec4 position;\n" +
+ "attribute vec2 texCoords;\n" +
+ "varying vec2 outTexCoords;\n" +
+ "uniform mat4 projection;\n" +
+ "\nvoid main(void) {\n" +
+ " outTexCoords = texCoords;\n" +
+ " gl_Position = projection * position;\n" +
+ "}\n\n";
+ private static final String sSimpleFS =
+ "precision mediump float;\n\n" +
+ "varying vec2 outTexCoords;\n" +
+ "uniform sampler2D texture;\n" +
+ "\nvoid main(void) {\n" +
+ " gl_FragColor = texture2D(texture, outTexCoords);\n" +
+ "}\n\n";
+
+ private static final int FLOAT_SIZE_BYTES = 4;
+ private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+ private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+ private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+
private int mRotationAtLastSurfaceSizeUpdate = -1;
private int mDisplayWidthAtLastSurfaceSizeUpdate = -1;
private int mDisplayHeightAtLastSurfaceSizeUpdate = -1;
@@ -94,14 +154,13 @@ public class ImageWallpaper extends WallpaperService {
private AsyncTask<Void, Void, Bitmap> mLoader;
private boolean mNeedsDrawAfterLoadingWallpaper;
private boolean mSurfaceValid;
- private boolean mSurfaceRedrawNeeded;
- DrawableEngine() {
+ public DrawableEngine() {
super();
setFixedSizeAllowed(true);
}
- void trimMemory(int level) {
+ public void trimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
&& level <= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
&& mBackground != null) {
@@ -120,7 +179,6 @@ public class ImageWallpaper extends WallpaperService {
super.onCreate(surfaceHolder);
- //noinspection ConstantConditions
mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
setOffsetNotificationsEnabled(false);
@@ -141,7 +199,7 @@ public class ImageWallpaper extends WallpaperService {
// Load background image dimensions, if we haven't saved them yet
if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
// Need to load the image to get dimensions
- loadWallpaper(forDraw);
+ loadWallpaper(forDraw, false /* needsReset */);
if (DEBUG) {
Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
}
@@ -152,13 +210,16 @@ public class ImageWallpaper extends WallpaperService {
int surfaceWidth = Math.max(displayInfo.logicalWidth, mBackgroundWidth);
int surfaceHeight = Math.max(displayInfo.logicalHeight, mBackgroundHeight);
- // Used a fixed size surface, because we are special. We can do
- // this because we know the current design of window animations doesn't
- // cause this to break.
- surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
- mLastRequestedWidth = surfaceWidth;
- mLastRequestedHeight = surfaceHeight;
-
+ if (FIXED_SIZED_SURFACE) {
+ // Used a fixed size surface, because we are special. We can do
+ // this because we know the current design of window animations doesn't
+ // cause this to break.
+ surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
+ mLastRequestedWidth = surfaceWidth;
+ mLastRequestedHeight = surfaceHeight;
+ } else {
+ surfaceHolder.setSizeFromLayout();
+ }
return hasWallpaper;
}
@@ -239,13 +300,6 @@ public class ImageWallpaper extends WallpaperService {
Log.d(TAG, "onSurfaceRedrawNeeded");
}
super.onSurfaceRedrawNeeded(holder);
- // At the end of this method we should have drawn into the surface.
- // This means that the bitmap should be loaded synchronously if
- // it was already unloaded.
- if (mBackground == null) {
- updateBitmap(mWallpaperManager.getBitmap(true /* hardware */));
- }
- mSurfaceRedrawNeeded = true;
drawFrame();
}
@@ -282,8 +336,7 @@ public class ImageWallpaper extends WallpaperService {
boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
|| dh != mLastSurfaceHeight;
- boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation
- || mSurfaceRedrawNeeded;
+ boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
if (!redrawNeeded && !mOffsetsChanged) {
if (DEBUG) {
Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
@@ -292,24 +345,40 @@ public class ImageWallpaper extends WallpaperService {
return;
}
mLastRotation = newRotation;
- mSurfaceRedrawNeeded = false;
// Load bitmap if it is not yet loaded
if (mBackground == null) {
- loadWallpaper(true);
+ if (DEBUG) {
+ Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
+ mBackground + ", " +
+ ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
+ ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
+ dw + ", " + dh);
+ }
+ loadWallpaper(true /* needDraw */, true /* needReset */);
if (DEBUG) {
Log.d(TAG, "Reloading, resuming draw later");
}
return;
}
- // Left align the scaled image
+ // Center the scaled image
mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(),
dh / (float) mBackground.getHeight()));
- final int availw = (int) (mBackground.getWidth() * mScale) - dw;
- final int availh = (int) (mBackground.getHeight() * mScale) - dh;
- int xPixels = (int) (availw * mXOffset);
- int yPixels = (int) (availh * mYOffset);
+ final int availw = dw - (int) (mBackground.getWidth() * mScale);
+ final int availh = dh - (int) (mBackground.getHeight() * mScale);
+ int xPixels = availw / 2;
+ int yPixels = availh / 2;
+
+ // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
+ // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
+ // will remain unchanged
+ final int availwUnscaled = dw - mBackground.getWidth();
+ final int availhUnscaled = dh - mBackground.getHeight();
+ if (availwUnscaled < 0)
+ xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
+ if (availhUnscaled < 0)
+ yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);
mOffsetsChanged = false;
if (surfaceDimensionsChanged) {
@@ -330,7 +399,21 @@ public class ImageWallpaper extends WallpaperService {
Log.d(TAG, "Redrawing wallpaper");
}
- drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+ if (mIsHwAccelerated) {
+ if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
+ drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+ }
+ } else {
+ drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+ if (FIXED_SIZED_SURFACE) {
+ // If the surface is fixed-size, we should only need to
+ // draw it once and then we'll let the window manager
+ // position it appropriately. As such, we no longer needed
+ // the loaded bitmap. Yay!
+ // hw-accelerated renderer retains bitmap for faster rotation
+ unloadWallpaper(false /* forgetSize */);
+ }
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
@@ -345,20 +428,28 @@ public class ImageWallpaper extends WallpaperService {
*
* If {@param needsReset} is set also clears the cache in WallpaperManager first.
*/
- private void loadWallpaper(boolean needsDraw) {
+ private void loadWallpaper(boolean needsDraw, boolean needsReset) {
mNeedsDrawAfterLoadingWallpaper |= needsDraw;
if (mLoader != null) {
- if (DEBUG) {
- Log.d(TAG, "Skipping loadWallpaper, already in flight ");
+ if (needsReset) {
+ mLoader.cancel(false /* interrupt */);
+ mLoader = null;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping loadWallpaper, already in flight ");
+ }
+ return;
}
- return;
}
mLoader = new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Void... params) {
Throwable exception;
try {
- return mWallpaperManager.getBitmap(true /* hardware */);
+ if (needsReset) {
+ mWallpaperManager.forgetLoadedWallpaper();
+ }
+ return mWallpaperManager.getBitmap();
} catch (RuntimeException | OutOfMemoryError e) {
exception = e;
}
@@ -367,33 +458,48 @@ public class ImageWallpaper extends WallpaperService {
return null;
}
- // Note that if we do fail at this, and the default wallpaper can't
- // be loaded, we will go into a cycle. Don't do a build where the
- // default wallpaper can't be loaded.
- Log.w(TAG, "Unable to load wallpaper!", exception);
- try {
- mWallpaperManager.clear();
- } catch (IOException ex) {
- // now we're really screwed.
- Log.w(TAG, "Unable reset to default wallpaper!", ex);
- }
-
- if (isCancelled()) {
- return null;
- }
-
- try {
- return mWallpaperManager.getBitmap(true /* hardware */);
- } catch (RuntimeException | OutOfMemoryError e) {
- Log.w(TAG, "Unable to load default wallpaper!", e);
+ if (exception != null) {
+ // Note that if we do fail at this, and the default wallpaper can't
+ // be loaded, we will go into a cycle. Don't do a build where the
+ // default wallpaper can't be loaded.
+ Log.w(TAG, "Unable to load wallpaper!", exception);
+ try {
+ mWallpaperManager.clear();
+ } catch (IOException ex) {
+ // now we're really screwed.
+ Log.w(TAG, "Unable reset to default wallpaper!", ex);
+ }
+
+ if (isCancelled()) {
+ return null;
+ }
+
+ try {
+ return mWallpaperManager.getBitmap();
+ } catch (RuntimeException | OutOfMemoryError e) {
+ Log.w(TAG, "Unable to load default wallpaper!", e);
+ }
}
return null;
}
@Override
protected void onPostExecute(Bitmap b) {
- updateBitmap(b);
+ mBackground = null;
+ mBackgroundWidth = -1;
+ mBackgroundHeight = -1;
+
+ if (b != null) {
+ mBackground = b;
+ mBackgroundWidth = mBackground.getWidth();
+ mBackgroundHeight = mBackground.getHeight();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Wallpaper loaded: " + mBackground);
+ }
+ updateSurfaceSize(getSurfaceHolder(), getDefaultDisplayInfo(),
+ false /* forDraw */);
if (mNeedsDrawAfterLoadingWallpaper) {
drawFrame();
}
@@ -404,24 +510,6 @@ public class ImageWallpaper extends WallpaperService {
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- private void updateBitmap(Bitmap bitmap) {
- mBackground = null;
- mBackgroundWidth = -1;
- mBackgroundHeight = -1;
-
- if (bitmap != null) {
- mBackground = bitmap;
- mBackgroundWidth = mBackground.getWidth();
- mBackgroundHeight = mBackground.getHeight();
- }
-
- if (DEBUG) {
- Log.d(TAG, "Wallpaper loaded: " + mBackground);
- }
- updateSurfaceSize(getSurfaceHolder(), getDefaultDisplayInfo(),
- false /* forDraw */);
- }
-
private void unloadWallpaper(boolean forgetSize) {
if (mLoader != null) {
mLoader.cancel(false);
@@ -476,7 +564,7 @@ public class ImageWallpaper extends WallpaperService {
}
private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) {
- Canvas c = sh.lockHardwareCanvas();
+ Canvas c = sh.lockCanvas();
if (c != null) {
try {
if (DEBUG) {
@@ -502,5 +590,278 @@ public class ImageWallpaper extends WallpaperService {
}
}
}
+
+ private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
+ if (!initGL(sh)) return false;
+
+ final float right = left + mBackground.getWidth() * mScale;
+ final float bottom = top + mBackground.getHeight() * mScale;
+
+ final Rect frame = sh.getSurfaceFrame();
+ final Matrix4f ortho = new Matrix4f();
+ ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
+
+ final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
+
+ final int texture = loadTexture(mBackground);
+ final int program = buildProgram(sSimpleVS, sSimpleFS);
+
+ final int attribPosition = glGetAttribLocation(program, "position");
+ final int attribTexCoords = glGetAttribLocation(program, "texCoords");
+ final int uniformTexture = glGetUniformLocation(program, "texture");
+ final int uniformProjection = glGetUniformLocation(program, "projection");
+
+ checkGlError();
+
+ glViewport(0, 0, frame.width(), frame.height());
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glUseProgram(program);
+ glEnableVertexAttribArray(attribPosition);
+ glEnableVertexAttribArray(attribTexCoords);
+ glUniform1i(uniformTexture, 0);
+ glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
+
+ checkGlError();
+
+ if (w > 0 || h > 0) {
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ // drawQuad
+ triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+ glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+
+ triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+ glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+ checkEglError();
+
+ finishGL(texture, program);
+
+ return status;
+ }
+
+ private FloatBuffer createMesh(int left, int top, float right, float bottom) {
+ final float[] verticesData = {
+ // X, Y, Z, U, V
+ left, bottom, 0.0f, 0.0f, 1.0f,
+ right, bottom, 0.0f, 1.0f, 1.0f,
+ left, top, 0.0f, 0.0f, 0.0f,
+ right, top, 0.0f, 1.0f, 0.0f,
+ };
+
+ final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
+ final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
+ ByteOrder.nativeOrder()).asFloatBuffer();
+ triangleVertices.put(verticesData).position(0);
+ return triangleVertices;
+ }
+
+ private int loadTexture(Bitmap bitmap) {
+ int[] textures = new int[1];
+
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(1, textures, 0);
+ checkGlError();
+
+ int texture = textures[0];
+ glBindTexture(GL_TEXTURE_2D, texture);
+ checkGlError();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
+ checkGlError();
+
+ return texture;
+ }
+
+ private int buildProgram(String vertex, String fragment) {
+ int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+ if (vertexShader == 0) return 0;
+
+ int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+ if (fragmentShader == 0) return 0;
+
+ int program = glCreateProgram();
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ checkGlError();
+
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+
+ int[] status = new int[1];
+ glGetProgramiv(program, GL_LINK_STATUS, status, 0);
+ if (status[0] != GL_TRUE) {
+ String error = glGetProgramInfoLog(program);
+ Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ return program;
+ }
+
+ private int buildShader(String source, int type) {
+ int shader = glCreateShader(type);
+
+ glShaderSource(shader, source);
+ checkGlError();
+
+ glCompileShader(shader);
+ checkGlError();
+
+ int[] status = new int[1];
+ glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
+ if (status[0] != GL_TRUE) {
+ String error = glGetShaderInfoLog(shader);
+ Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+ }
+
+ private void checkEglError() {
+ int error = mEgl.eglGetError();
+ if (error != EGL_SUCCESS) {
+ Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
+ }
+ }
+
+ private void checkGlError() {
+ int error = glGetError();
+ if (error != GL_NO_ERROR) {
+ Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
+ }
+ }
+
+ private void finishGL(int texture, int program) {
+ int[] textures = new int[1];
+ textures[0] = texture;
+ glDeleteTextures(1, textures, 0);
+ glDeleteProgram(program);
+ mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ mEgl.eglTerminate(mEglDisplay);
+ }
+
+ private boolean initGL(SurfaceHolder surfaceHolder) {
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ int[] version = new int[2];
+ if (!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ mEglConfig = chooseEglConfig();
+ if (mEglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+
+ mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+ if (mEglContext == EGL_NO_CONTEXT) {
+ throw new RuntimeException("createContext failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ int attribs[] = {
+ EGL_WIDTH, 1,
+ EGL_HEIGHT, 1,
+ EGL_NONE
+ };
+ EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+ mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
+
+ int[] maxSize = new int[1];
+ Rect frame = surfaceHolder.getSurfaceFrame();
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
+
+ mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
+
+ if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ mEgl.eglTerminate(mEglDisplay);
+ Log.e(GL_LOG_TAG, "requested texture size " +
+ frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
+ maxSize[0] + "x" + maxSize[0]);
+ return false;
+ }
+
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
+ if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
+ Log.e(GL_LOG_TAG, "createWindowSurface returned " +
+ GLUtils.getEGLErrorString(error) + ".");
+ return false;
+ }
+ throw new RuntimeException("createWindowSurface failed " +
+ GLUtils.getEGLErrorString(error));
+ }
+
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ }
+
+ return true;
+ }
+
+
+ EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+ int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
+ }
+
+ private EGLConfig chooseEglConfig() {
+ int[] configsCount = new int[1];
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] configSpec = getConfig();
+ if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+ throw new IllegalArgumentException("eglChooseConfig failed " +
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
+ } else if (configsCount[0] > 0) {
+ return configs[0];
+ }
+ return null;
+ }
+
+ private int[] getConfig() {
+ return new int[] {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_NONE
+ };
+ }
}
}
diff --git a/com/android/systemui/SwipeHelper.java b/com/android/systemui/SwipeHelper.java
index 592dda07..4b377153 100644
--- a/com/android/systemui/SwipeHelper.java
+++ b/com/android/systemui/SwipeHelper.java
@@ -83,6 +83,7 @@ public class SwipeHelper implements Gefingerpoken {
private boolean mMenuRowIntercepting;
private boolean mLongPressSent;
+ private LongPressListener mLongPressListener;
private Runnable mWatchLongPress;
private final long mLongPressTimeout;
@@ -114,6 +115,10 @@ public class SwipeHelper implements Gefingerpoken {
mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
}
+ public void setLongPressListener(LongPressListener listener) {
+ mLongPressListener = listener;
+ }
+
public void setDensityScale(float densityScale) {
mDensityScale = densityScale;
}
@@ -252,7 +257,7 @@ public class SwipeHelper implements Gefingerpoken {
}
}
- public void cancelLongPress() {
+ public void removeLongPressCallback() {
if (mWatchLongPress != null) {
mHandler.removeCallbacks(mWatchLongPress);
mWatchLongPress = null;
@@ -283,27 +288,33 @@ public class SwipeHelper implements Gefingerpoken {
mInitialTouchPos = getPos(ev);
mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
mTranslation = getTranslation(mCurrView);
- if (mWatchLongPress == null) {
- mWatchLongPress = new Runnable() {
- @Override
- public void run() {
- if (mCurrView != null && !mLongPressSent) {
- mLongPressSent = true;
- mCurrView.getLocationOnScreen(mTmpPos);
- final int x = (int) ev.getRawX() - mTmpPos[0];
- final int y = (int) ev.getRawY() - mTmpPos[1];
- if (mCurrView instanceof ExpandableNotificationRow) {
+ if (mLongPressListener != null) {
+ if (mWatchLongPress == null) {
+ mWatchLongPress = new Runnable() {
+ @Override
+ public void run() {
+ if (mCurrView != null && !mLongPressSent) {
+ mLongPressSent = true;
mCurrView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- ExpandableNotificationRow currRow =
- (ExpandableNotificationRow) mCurrView;
- currRow.doLongClickCallback(x, y);
+ mCurrView.getLocationOnScreen(mTmpPos);
+ final int x = (int) ev.getRawX() - mTmpPos[0];
+ final int y = (int) ev.getRawY() - mTmpPos[1];
+ MenuItem menuItem = null;
+ if (mCurrView instanceof ExpandableNotificationRow) {
+ menuItem = ((ExpandableNotificationRow) mCurrView)
+ .getProvider().getLongpressMenuItem(mContext);
+ }
+ if (menuItem != null) {
+ mLongPressListener.onLongPress(mCurrView, x, y,
+ menuItem);
+ }
}
}
- }
- };
+ };
+ }
+ mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
}
- mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
}
break;
@@ -320,7 +331,7 @@ public class SwipeHelper implements Gefingerpoken {
mDragging = true;
mInitialTouchPos = getPos(ev);
mTranslation = getTranslation(mCurrView);
- cancelLongPress();
+ removeLongPressCallback();
}
}
break;
@@ -332,7 +343,7 @@ public class SwipeHelper implements Gefingerpoken {
mCurrView = null;
mLongPressSent = false;
mMenuRowIntercepting = false;
- cancelLongPress();
+ removeLongPressCallback();
if (captured) return true;
break;
}
@@ -575,7 +586,7 @@ public class SwipeHelper implements Gefingerpoken {
// We are not doing anything, make sure the long press callback
// is not still ticking like a bomb waiting to go off.
- cancelLongPress();
+ removeLongPressCallback();
return false;
}
}
@@ -723,4 +734,15 @@ public class SwipeHelper implements Gefingerpoken {
*/
float getFalsingThresholdFactor();
}
+
+ /**
+ * Equivalent to View.OnLongClickListener with coordinates
+ */
+ public interface LongPressListener {
+ /**
+ * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
+ * @return whether the longpress was handled
+ */
+ boolean onLongPress(View v, int x, int y, MenuItem item);
+ }
}
diff --git a/com/android/systemui/doze/DozeUi.java b/com/android/systemui/doze/DozeUi.java
index 851b78cf..dc626fb4 100644
--- a/com/android/systemui/doze/DozeUi.java
+++ b/com/android/systemui/doze/DozeUi.java
@@ -84,6 +84,9 @@ public class DozeUi implements DozeMachine.Part {
case DOZE_REQUEST_PULSE:
pulseWhileDozing(mMachine.getPulseReason());
break;
+ case DOZE_PULSE_DONE:
+ mHost.abortPulsing();
+ break;
case INITIALIZED:
mHost.startDozing();
break;
diff --git a/com/android/systemui/globalactions/GlobalActionsComponent.java b/com/android/systemui/globalactions/GlobalActionsComponent.java
index f06cda0f..09a08f09 100644
--- a/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -31,7 +31,6 @@ import android.os.ServiceManager;
public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
- private GlobalActions mPlugin;
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
@@ -42,19 +41,10 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class)
.withPlugin(GlobalActions.class)
.withDefault(() -> new GlobalActionsImpl(mContext))
- .withCallback(this::onExtensionCallback)
.build();
- mPlugin = mExtension.get();
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
}
- private void onExtensionCallback(GlobalActions newPlugin) {
- if (mPlugin != null) {
- mPlugin.destroy();
- }
- mPlugin = newPlugin;
- }
-
@Override
public void handleShowShutdownUi(boolean isReboot, String reason) {
mExtension.get().showShutdownUi(isReboot, reason);
diff --git a/com/android/systemui/globalactions/GlobalActionsDialog.java b/com/android/systemui/globalactions/GlobalActionsDialog.java
index d82f9cd4..189badfc 100644
--- a/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -16,6 +16,27 @@ package com.android.systemui.globalactions;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import com.android.internal.R;
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
+import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.WallpaperManager;
@@ -48,6 +69,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
@@ -64,23 +86,7 @@ import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
-import com.android.systemui.Interpolators;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
@@ -90,8 +96,7 @@ import java.util.List;
* may show depending on whether the keyguard is showing, and whether the device
* is provisioned.
*/
-class GlobalActionsDialog implements DialogInterface.OnDismissListener,
- DialogInterface.OnClickListener {
+class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -190,14 +195,6 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- /**
- * Dismiss the global actions dialog, if it's currently shown
- */
- public void dismissDialog() {
- mHandler.removeMessages(MESSAGE_DISMISS);
- mHandler.sendEmptyMessage(MESSAGE_DISMISS);
- }
-
private void awakenIfNecessary() {
if (mDreamManager != null) {
try {
diff --git a/com/android/systemui/globalactions/GlobalActionsImpl.java b/com/android/systemui/globalactions/GlobalActionsImpl.java
index 35634374..2cf230c8 100644
--- a/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -14,12 +14,12 @@
package com.android.systemui.globalactions;
-import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
-
import android.app.Dialog;
import android.app.KeyguardManager;
+import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Point;
import android.view.ViewGroup;
import android.view.Window;
@@ -32,14 +32,12 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
+public class GlobalActionsImpl implements GlobalActions {
private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
@@ -47,23 +45,15 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
private final KeyguardMonitor mKeyguardMonitor;
private final DeviceProvisionedController mDeviceProvisionedController;
private GlobalActionsDialog mGlobalActions;
- private boolean mDisabled;
public GlobalActionsImpl(Context context) {
mContext = context;
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
- }
-
- @Override
- public void destroy() {
- SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
}
@Override
public void showGlobalActions(GlobalActionsManager manager) {
- if (mDisabled) return;
if (mGlobalActions == null) {
mGlobalActions = new GlobalActionsDialog(mContext, manager);
}
@@ -117,14 +107,4 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
d.show();
}
-
- @Override
- public void disable(int state1, int state2, boolean animate) {
- final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
- if (disabled == mDisabled) return;
- mDisabled = disabled;
- if (disabled && mGlobalActions != null) {
- mGlobalActions.dismissDialog();
- }
- }
}
diff --git a/com/android/systemui/keyguard/WorkLockActivityController.java b/com/android/systemui/keyguard/WorkLockActivityController.java
index 4c3d5bad..f198229a 100644
--- a/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityManager;
@@ -28,8 +29,9 @@ import android.os.RemoteException;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
public class WorkLockActivityController {
private final Context mContext;
@@ -96,7 +98,7 @@ public class WorkLockActivityController {
}
}
- private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() {
+ private final TaskStackListener mLockListener = new TaskStackListener() {
@Override
public void onTaskProfileLocked(int taskId, int userId) {
startWorkChallengeInTask(taskId, userId);
diff --git a/com/android/systemui/pip/phone/InputConsumerController.java b/com/android/systemui/pip/phone/InputConsumerController.java
index e6d6c558..abc56672 100644
--- a/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/com/android/systemui/pip/phone/InputConsumerController.java
@@ -28,6 +28,8 @@ import android.view.InputEvent;
import android.view.IWindowManager;
import android.view.MotionEvent;
+import com.android.systemui.recents.misc.Utilities;
+
import java.io.PrintWriter;
/**
diff --git a/com/android/systemui/pip/phone/PipManager.java b/com/android/systemui/pip/phone/PipManager.java
index 7e87666a..f8996aae 100644
--- a/com/android/systemui/pip/phone/PipManager.java
+++ b/com/android/systemui/pip/phone/PipManager.java
@@ -41,7 +41,8 @@ import com.android.systemui.pip.BasePipManager;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.component.ExpandPipEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+import com.android.systemui.statusbar.CommandQueue;
import java.io.PrintWriter;
@@ -69,7 +70,7 @@ public class PipManager implements BasePipManager {
/**
* Handler for system task stack changes.
*/
- TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
mTouchHandler.onActivityPinned();
diff --git a/com/android/systemui/pip/tv/PipManager.java b/com/android/systemui/pip/tv/PipManager.java
index 312b9908..e0445c16 100644
--- a/com/android/systemui/pip/tv/PipManager.java
+++ b/com/android/systemui/pip/tv/PipManager.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -46,7 +47,7 @@ import android.view.WindowManagerGlobal;
import com.android.systemui.R;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -620,7 +621,7 @@ public class PipManager implements BasePipManager {
return false;
}
- private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ private TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
diff --git a/com/android/systemui/plugins/GlobalActions.java b/com/android/systemui/plugins/GlobalActions.java
index 95ff13b4..1f633da4 100644
--- a/com/android/systemui/plugins/GlobalActions.java
+++ b/com/android/systemui/plugins/GlobalActions.java
@@ -29,9 +29,6 @@ public interface GlobalActions extends Plugin {
default void showShutdownUi(boolean isReboot, String reason) {
}
- default void destroy() {
- }
-
@ProvidesInterface(version = GlobalActionsManager.VERSION)
public interface GlobalActionsManager {
int VERSION = 1;
diff --git a/com/android/systemui/power/PowerUI.java b/com/android/systemui/power/PowerUI.java
index c1a36239..f3782685 100644
--- a/com/android/systemui/power/PowerUI.java
+++ b/com/android/systemui/power/PowerUI.java
@@ -28,14 +28,8 @@ import android.database.ContentObserver;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.HardwarePropertiesManager;
-import android.os.IBinder;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
@@ -81,7 +75,6 @@ public class PowerUI extends SystemUI {
private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
private int mNumTemps;
private long mNextLogTime;
- private IThermalService mThermalService;
// We create a method reference here so that we are guaranteed that we can remove a callback
// by using the same instance (method references are not guaranteed to be the same object
@@ -270,7 +263,7 @@ public class PowerUI extends SystemUI {
resources.getInteger(R.integer.config_warningTemperature));
if (mThresholdTemp < 0f) {
- // Get the shutdown temperature, adjust for warning tolerance.
+ // Get the throttling temperature. No need to check if we're not throttling.
float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
@@ -283,25 +276,6 @@ public class PowerUI extends SystemUI {
resources.getInteger(R.integer.config_warningTemperatureTolerance);
}
- if (mThermalService == null) {
- // Enable push notifications of throttling from vendor thermal
- // management subsystem via thermalservice, in addition to our
- // usual polling, to react to temperature jumps more quickly.
- IBinder b = ServiceManager.getService("thermalservice");
-
- if (b != null) {
- mThermalService = IThermalService.Stub.asInterface(b);
- try {
- mThermalService.registerThermalEventListener(
- new ThermalEventListener());
- } catch (RemoteException e) {
- // Should never happen.
- }
- } else {
- Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
- }
- }
-
setNextLogTime();
// This initialization method may be called on a configuration change. Only one set of
@@ -440,15 +414,5 @@ public class PowerUI extends SystemUI {
void dump(PrintWriter pw);
void userSwitched();
}
-
- // Thermal event received from vendor thermal management subsystem
- private final class ThermalEventListener extends IThermalEventListener.Stub {
- @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
- // Trigger an update of the temperature warning. Only one
- // callback can be enabled at a time, so remove any existing
- // callback; updateTemperatureWarning will schedule another one.
- mHandler.removeCallbacks(mUpdateTempCallback);
- updateTemperatureWarning();
- }
- }
}
+
diff --git a/com/android/systemui/recents/Recents.java b/com/android/systemui/recents/Recents.java
index ce1438a1..283ac0c4 100644
--- a/com/android/systemui/recents/Recents.java
+++ b/com/android/systemui/recents/Recents.java
@@ -29,13 +29,14 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
@@ -52,7 +53,6 @@ import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
@@ -62,7 +62,7 @@ import com.android.systemui.recents.events.component.SetWaitingForTransitionStar
import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -81,15 +81,23 @@ public class Recents extends SystemUI
implements RecentsComponent, CommandQueue.Callbacks {
private final static String TAG = "Recents";
+ private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = 1;
public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
+ public final static int RECENTS_GROW_TARGET_INVALID = -1;
public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
static {
RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
}
+ // Purely for experimentation
+ private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg";
+ private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW";
+ private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
+ private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
+
private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
@@ -99,6 +107,11 @@ public class Recents extends SystemUI
private static RecentsTaskLoader sTaskLoader;
private static RecentsConfiguration sConfiguration;
+ // For experiments only, allows another package to handle recents if it is defined in the system
+ // properties. This is limited to show/toggle/hide, and does not tie into the ActivityManager,
+ // and does not reside in the home stack.
+ private String mOverrideRecentsPackageName;
+
private Handler mHandler;
private RecentsImpl mImpl;
private int mDraggingInRecentsCurrentUser;
@@ -191,23 +204,21 @@ public class Recents extends SystemUI
@Override
public void start() {
- final Resources res = mContext.getResources();
- final int defaultTaskBarBackgroundColor =
- mContext.getColor(R.color.recents_task_bar_default_background_color);
- final int defaultTaskViewBackgroundColor =
- mContext.getColor(R.color.recents_task_view_default_background_color);
- sDebugFlags = new RecentsDebugFlags();
+ sDebugFlags = new RecentsDebugFlags(mContext);
sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
sConfiguration = new RecentsConfiguration(mContext);
- sTaskLoader = new RecentsTaskLoader(mContext,
- // TODO: Once we start building the AAR, move these into the loader
- res.getInteger(R.integer.config_recents_max_thumbnail_count),
- res.getInteger(R.integer.config_recents_max_icon_count),
- res.getInteger(R.integer.recents_svelte_level));
- sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
+ sTaskLoader = new RecentsTaskLoader(mContext);
mHandler = new Handler();
mImpl = new RecentsImpl(mContext);
+ // Check if there is a recents override package
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY);
+ if (!cnStr.isEmpty()) {
+ mOverrideRecentsPackageName = cnStr;
+ }
+ }
+
// Register with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
@@ -246,8 +257,16 @@ public class Recents extends SystemUI
return;
}
- sSystemServicesProxy.sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+ if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) {
+ return;
+ }
+ try {
+ ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_RECENT_APPS);
+ } catch (RemoteException e) {
+ }
+
int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
@@ -282,6 +301,10 @@ public class Recents extends SystemUI
return;
}
+ if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
@@ -313,7 +336,12 @@ public class Recents extends SystemUI
return;
}
+ if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) {
+ return;
+ }
+
int growTarget = getComponent(Divider.class).getView().growsRecents();
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.toggleRecents(growTarget);
@@ -606,23 +634,6 @@ public class Recents extends SystemUI
}
}
- public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- int processUser = ssp.getProcessUser();
- if (!ssp.isSystemUser(processUser)) {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
/**
* Handle screen pinning request.
*/
@@ -809,6 +820,21 @@ public class Recents extends SystemUI
(Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
}
+ /**
+ * Attempts to proxy the following action to the override recents package.
+ * @return whether the proxying was successful
+ */
+ private boolean proxyToOverridePackage(String action) {
+ if (mOverrideRecentsPackageName != null) {
+ Intent intent = new Intent(action);
+ intent.setPackage(mOverrideRecentsPackageName);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Recents");
diff --git a/com/android/systemui/recents/RecentsActivity.java b/com/android/systemui/recents/RecentsActivity.java
index b75a1428..86b77900 100644
--- a/com/android/systemui/recents/RecentsActivity.java
+++ b/com/android/systemui/recents/RecentsActivity.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.TaskStackBuilder;
import android.app.WallpaperManager;
@@ -28,10 +29,10 @@ import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@@ -41,7 +42,6 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.LatencyTracker;
@@ -53,6 +53,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
@@ -60,10 +61,10 @@ import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationC
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
@@ -78,24 +79,28 @@ import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
+import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/**
* The main Recents activity that is started from RecentsComponent.
@@ -109,23 +114,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
- private PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- }
-
- @Override
- public boolean onPackageChanged(String packageName, int uid, String[] components) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- return true;
- }
-
- @Override
- public void onPackageModified(String packageName) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- }
- };
+ private RecentsPackageMonitor mPackageMonitor;
private Handler mHandler = new Handler();
private long mLastTabKeyEventTime;
private boolean mFinishedOnStartup;
@@ -144,6 +133,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// The trigger to automatically launch the current task
private int mFocusTimerDuration;
+ private DozeTrigger mIterateTrigger;
private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent();
// Theme and colors
@@ -198,6 +188,41 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
// When switching users, dismiss Recents to Home similar to screen off
finish();
+ } else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
+ // If the time shifts but the currentTime >= lastStackActiveTime, then that boundary
+ // is still valid. Otherwise, we need to reset the lastStackactiveTime to the
+ // currentTime and remove the old tasks in between which would not be previously
+ // visible, but currently would be in the new currentTime
+ int currentUser = SystemServicesProxy.getInstance(RecentsActivity.this)
+ .getCurrentUser();
+ long oldLastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(),
+ Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1, currentUser);
+ if (oldLastStackActiveTime != -1) {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime < oldLastStackActiveTime) {
+ // We are only removing tasks that are between the new current time
+ // and the old last stack active time, they were not visible and in the
+ // TaskStack so we don't need to remove any associated TaskViews but we do
+ // need to load the task id's from the system
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(ctx);
+ loader.preloadRawTasks(loadPlan, false /* includeFrontMostExcludedTask */);
+ List<ActivityManager.RecentTaskInfo> tasks = loadPlan.getRawTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ ActivityManager.RecentTaskInfo task = tasks.get(i);
+ if (currentTime <= task.lastActiveTime && task.lastActiveTime <
+ oldLastStackActiveTime) {
+ Recents.getSystemServices().removeTask(task.persistentId);
+ }
+ }
+ Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
+ currentTime, currentUser);
+
+ // Clear the last PiP task time, it's an edge case and we'd rather it
+ // not relaunch the PiP task if the user double taps
+ RecentsImpl.clearLastPipTime();
+ }
+ }
}
}
};
@@ -315,8 +340,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
// Initialize the package monitor
- mPackageMonitor.register(this, Looper.getMainLooper(), UserHandle.ALL,
- true /* externalStorage */);
+ mPackageMonitor = new RecentsPackageMonitor();
+ mPackageMonitor.register(this);
// Select theme based on wallpaper colors
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
@@ -338,6 +363,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
+ mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
+ mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
+ @Override
+ public void run() {
+ dismissRecentsToFocusedTask(MetricsEvent.OVERVIEW_SELECT_TIMEOUT);
+ }
+ });
// Set the window background
mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode());
@@ -351,6 +383,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// Register the broadcast receiver to handle messages when the screen is turned off
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
registerReceiver(mSystemBroadcastReceiver, filter);
@@ -427,21 +460,22 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
if (loadPlan == null) {
- loadPlan = new RecentsTaskLoadPlan(this);
+ loadPlan = loader.createLoadPlan(this);
}
// Start loading tasks according to the load plan
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!loadPlan.hasTasks()) {
- loader.preloadTasks(loadPlan, launchState.launchedToTaskId);
+ loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
+ !launchState.launchedFromHome && !launchState.launchedViaDockGesture);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(loadPlan, loadOpts);
+ loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
mRecentsView.onReload(stack, mIsVisible);
@@ -506,6 +540,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
super.onPause();
mIgnoreAltTabRelease = false;
+ mIterateTrigger.stopDozing();
}
@Override
@@ -608,7 +643,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
if (backward) {
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
} else {
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
}
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
@@ -666,10 +702,38 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
}
+ public final void onBusEvent(IterateRecentsEvent event) {
+ final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+
+ // Start dozing after the recents button is clicked
+ int timerIndicatorDuration = 0;
+ if (debugFlags.isFastToggleRecentsEnabled()) {
+ timerIndicatorDuration = getResources().getInteger(
+ R.integer.recents_subsequent_auto_advance_duration);
+
+ mIterateTrigger.setDozeDuration(timerIndicatorDuration);
+ if (!mIterateTrigger.isDozing()) {
+ mIterateTrigger.startDozing();
+ } else {
+ mIterateTrigger.poke();
+ }
+ }
+
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
+
+ MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
+ }
+
public final void onBusEvent(RecentsActivityStartingEvent event) {
mRecentsStartRequested = true;
}
+ public final void onBusEvent(UserInteractionEvent event) {
+ // Stop the fast-toggle dozer
+ mIterateTrigger.stopDozing();
+ }
+
public final void onBusEvent(HideRecentsEvent event) {
if (event.triggeredFromAltTab) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
@@ -687,11 +751,15 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
+ EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
mRecentsView.invalidate();
}
public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
+ if (mRecentsView.isLastTaskLaunchedFreeform()) {
+ EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(false));
+ }
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
mRecentsView.invalidate();
}
@@ -791,6 +859,11 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
MetricsLogger.count(this, "overview_screen_pinned", 1);
}
+ public final void onBusEvent(DebugFlagsChangedEvent event) {
+ // Just finish recents so that we can reload the flags anew on the next instantiation
+ finish();
+ }
+
public final void onBusEvent(StackViewScrolledEvent event) {
// Once the user has scrolled while holding alt-tab, then we should ignore the release of
// the key
@@ -815,13 +888,14 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(this);
- loader.preloadTasks(loadPlan, -1 /* runningTaskId */);
+ RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+ loader.preloadTasks(loadPlan, -1 /* runningTaskId */,
+ false /* includeFrontMostExcludedTask */);
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(loadPlan, loadOpts);
+ loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
int numStackTasks = stack.getStackTaskCount();
@@ -850,11 +924,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
return true;
}
- public void onPackageChanged(String packageName, int userId) {
- Recents.getTaskLoader().onPackageChanged(packageName);
- EventBus.getDefault().send(new PackagesChangedEvent(packageName, userId));
- }
-
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
@@ -862,9 +931,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
Recents.getTaskLoader().dump(prefix, writer);
String id = Integer.toHexString(System.identityHashCode(this));
+ long lastStackActiveTime = Settings.Secure.getLongForUser(getContentResolver(),
+ Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, -1,
+ SystemServicesProxy.getInstance(this).getCurrentUser());
writer.print(prefix); writer.print(TAG);
writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+ writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime);
writer.print(" currentTime="); writer.print(System.currentTimeMillis());
writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println();
diff --git a/com/android/systemui/recents/RecentsActivityLaunchState.java b/com/android/systemui/recents/RecentsActivityLaunchState.java
index d2326ce2..5b8ed94d 100644
--- a/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -33,6 +33,7 @@ public class RecentsActivityLaunchState {
public boolean launchedFromPipApp;
// Set if the next activity that quick-switch will launch is the PiP activity
public boolean launchedWithNextPipApp;
+ public boolean launchedFromBlacklistedApp;
public boolean launchedFromHome;
public boolean launchedViaDragGesture;
public boolean launchedViaDockGesture;
@@ -43,6 +44,7 @@ public class RecentsActivityLaunchState {
public void reset() {
launchedFromHome = false;
launchedFromApp = false;
+ launchedFromBlacklistedApp = false;
launchedFromPipApp = false;
launchedWithNextPipApp = false;
launchedToTaskId = -1;
@@ -58,6 +60,18 @@ public class RecentsActivityLaunchState {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchedFromApp) {
+ if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
+ // If fast toggling, focus the front most task so that the next tap will launch the
+ // task
+ return numTasks - 1;
+ }
+
+ if (launchState.launchedFromBlacklistedApp) {
+ // If we are launching from a blacklisted app, focus the front most task so that the
+ // next tap will launch the task
+ return numTasks - 1;
+ }
+
if (useGridLayout) {
// If coming from another app to the grid layout, focus the front most task
return numTasks - 1;
@@ -66,6 +80,12 @@ public class RecentsActivityLaunchState {
// If coming from another app, focus the next task
return Math.max(0, numTasks - 2);
} else {
+ if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
+ // If fast toggling, defer focusing until the next tap (which will automatically
+ // focus the front most task)
+ return -1;
+ }
+
// If coming from home, focus the front most task
return numTasks - 1;
}
diff --git a/com/android/systemui/recents/RecentsConfiguration.java b/com/android/systemui/recents/RecentsConfiguration.java
index 68df1d5b..5dc6f31c 100644
--- a/com/android/systemui/recents/RecentsConfiguration.java
+++ b/com/android/systemui/recents/RecentsConfiguration.java
@@ -20,31 +20,31 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.SystemProperties;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.views.DockState;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStack;
/**
* Represents the dock regions for each orientation.
*/
class DockRegion {
- public static DockState[] PHONE_LANDSCAPE = {
+ public static TaskStack.DockState[] PHONE_LANDSCAPE = {
// We only allow docking to the left in landscape for now on small devices
- DockState.LEFT
+ TaskStack.DockState.LEFT
};
- public static DockState[] PHONE_PORTRAIT = {
+ public static TaskStack.DockState[] PHONE_PORTRAIT = {
// We only allow docking to the top for now on small devices
- DockState.TOP
+ TaskStack.DockState.TOP
};
- public static DockState[] TABLET_LANDSCAPE = {
- DockState.LEFT,
- DockState.RIGHT
+ public static TaskStack.DockState[] TABLET_LANDSCAPE = {
+ TaskStack.DockState.LEFT,
+ TaskStack.DockState.RIGHT
};
- public static DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
+ public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
}
/**
@@ -56,6 +56,18 @@ public class RecentsConfiguration {
private static final int LARGE_SCREEN_MIN_DP = 600;
private static final int XLARGE_SCREEN_MIN_DP = 720;
+ /** Levels of svelte in increasing severity/austerity. */
+ // No svelting.
+ public static final int SVELTE_NONE = 0;
+ // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
+ // caching thumbnails as you scroll.
+ public static final int SVELTE_LIMIT_CACHE = 1;
+ // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
+ // evict all thumbnails when hidden.
+ public static final int SVELTE_DISABLE_CACHE = 2;
+ // Disable all thumbnail loading.
+ public static final int SVELTE_DISABLE_LOADING = 3;
+
// Launch states
public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
@@ -113,7 +125,7 @@ public class RecentsConfiguration {
* Returns the preferred dock states for the current orientation.
* @return a list of dock states for device and its orientation
*/
- public DockState[] getDockStatesForCurrentOrientation() {
+ public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
RecentsConfiguration config = Recents.getConfiguration();
diff --git a/com/android/systemui/recents/RecentsDebugFlags.java b/com/android/systemui/recents/RecentsDebugFlags.java
index 19185939..0262a098 100644
--- a/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/com/android/systemui/recents/RecentsDebugFlags.java
@@ -16,14 +16,75 @@
package com.android.systemui.recents;
-public class RecentsDebugFlags {
+import android.content.Context;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.tuner.TunerService;
+
+/**
+ * Tunable debug flags
+ */
+public class RecentsDebugFlags implements TunerService.Tunable {
public static class Static {
// Enables debug drawing for the transition thumbnail
public static final boolean EnableTransitionThumbnailDebugMode = false;
-
+ // This disables the bitmap and icon caches
+ public static final boolean DisableBackgroundCache = false;
+ // Enables the task affiliations
+ public static final boolean EnableAffiliatedTaskGroups = false;
+ // Enables the button above the stack
+ public static final boolean EnableStackActionButton = true;
+ // Overrides the Tuner flags and enables the timeout
+ private static final boolean EnableFastToggleTimeout = false;
+ // Overrides the Tuner flags and enables the paging via the Recents button
+ private static final boolean EnablePaging = false;
// Disables enter and exit transitions for other tasks for low ram devices
public static final boolean DisableRecentsLowRamEnterExitAnimation = false;
+ // Enables us to create mock recents tasks
+ public static final boolean EnableMockTasks = false;
+ // Defines the number of mock recents packages to create
+ public static final int MockTasksPackageCount = 3;
+ // Defines the number of mock recents tasks to create
+ public static final int MockTaskCount = 100;
+ // Enables the simulated task affiliations
+ public static final boolean EnableMockTaskGroups = false;
+ // Defines the number of mock task affiliations per group
+ public static final int MockTaskGroupsTaskCount = 12;
+ }
+
+ /**
+ * We read the prefs once when we start the activity, then update them as the tuner changes
+ * the flags.
+ */
+ public RecentsDebugFlags(Context context) {
+ // Register all our flags, this will also call onTuningChanged() for each key, which will
+ // initialize the current state of each flag
+ }
+
+ /**
+ * @return whether we are enabling fast toggling.
+ */
+ public boolean isFastToggleRecentsEnabled() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport() || ssp.isTouchExplorationEnabled()) {
+ return false;
+ }
+ return Static.EnableFastToggleTimeout;
+ }
+
+ /**
+ * @return whether we are enabling paging.
+ */
+ public boolean isPagingEnabled() {
+ return Static.EnablePaging;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ EventBus.getDefault().send(new DebugFlagsChangedEvent());
}
}
diff --git a/com/android/systemui/recents/RecentsImpl.java b/com/android/systemui/recents/RecentsImpl.java
index 868ed64b..3e2a5f3f 100644
--- a/com/android/systemui/recents/RecentsImpl.java
+++ b/com/android/systemui/recents/RecentsImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.View.MeasureSpec;
import android.app.ActivityManager;
@@ -55,6 +56,7 @@ import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
@@ -70,18 +72,20 @@ import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.Task.TaskKey;
+import com.android.systemui.recents.model.TaskGrouping;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.model.ThumbnailData;
import com.android.systemui.recents.views.RecentsTransitionHelper;
import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewScroller;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
@@ -113,10 +117,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
/**
- * An implementation of TaskStackChangeListener, that allows us to listen for changes to the system
+ * An implementation of TaskStackListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
*/
- class TaskStackListenerImpl extends TaskStackChangeListener {
+ class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChangedBackground() {
@@ -127,7 +131,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
- if (config.svelteLevel == RecentsTaskLoader.SVELTE_NONE) {
+ if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
Rect windowRect = getWindowRect(null /* windowRectOverride */);
if (windowRect.isEmpty()) {
return;
@@ -137,8 +141,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
TaskStack stack = plan.getTaskStack();
RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
@@ -164,7 +168,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
launchOpts.onlyLoadPausedActivities = true;
launchOpts.loadThumbnails = true;
}
- loader.loadTasks(plan, launchOpts);
+ loader.loadTasks(mContext, plan, launchOpts);
}
}
@@ -203,7 +207,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
}
EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId,
- new ThumbnailData(snapshot)));
+ ThumbnailData.createFromTaskSnapshot(snapshot)));
}
}
@@ -278,13 +282,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// When we start, preload the data associated with the previous recent tasks.
// We can use a new plan since the caches will be the same.
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
launchOpts.numVisibleTasks = loader.getIconCacheSize();
launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
launchOpts.onlyLoadForCache = true;
- loader.loadTasks(plan, launchOpts);
+ loader.loadTasks(mContext, plan, launchOpts);
}
public void onConfigurationChanged() {
@@ -405,17 +409,22 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
+ // Has the user tapped quickly?
+ boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
if (Recents.getConfiguration().isGridEnabled) {
- // Has the user tapped quickly?
- boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
if (isQuickTap) {
EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
} else {
EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
}
} else {
- // Launch the next focused task
- EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+ if (!debugFlags.isPagingEnabled() || isQuickTap) {
+ // Launch the next focused task
+ EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+ } else {
+ // Notify recents to move onto the next task
+ EventBus.getDefault().post(new IterateRecentsEvent());
+ }
}
} else {
// If the user has toggled it too quickly, then just eat up the event here (it's
@@ -464,15 +473,16 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
mHandler.post(() -> {
- if (!ssp.isRecentsActivityVisible(null)) {
+ MutableBoolean isHomeStackVisible = new MutableBoolean(true);
+ if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
if (runningTask == null) {
return;
}
RecentsTaskLoader loader = Recents.getTaskLoader();
- sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(sInstanceLoadPlan, runningTask.id, !isHomeStackVisible.value);
TaskStack stack = sInstanceLoadPlan.getTaskStack();
if (stack.getTaskCount() > 0) {
// Only preload the icon (but not the thumbnail since it may not have been taken
@@ -511,8 +521,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public void showNextTask() {
SystemServicesProxy ssp = Recents.getSystemServices();
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
TaskStack focusedStack = plan.getTaskStack();
// Return early if there are no tasks in the focused stack
@@ -566,8 +576,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public void showRelativeAffiliatedTask(boolean showNextTask) {
SystemServicesProxy ssp = Recents.getSystemServices();
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
TaskStack focusedStack = plan.getTaskStack();
// Return early if there are no tasks in the focused stack
@@ -585,38 +595,43 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
+ int numAffiliatedTasks = 0;
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
if (task.key.id == runningTask.id) {
+ TaskGrouping group = task.group;
+ Task.TaskKey toTaskKey;
if (showNextTask) {
- if ((i + 1) < taskCount) {
- toTask = tasks.get(i + 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_target,
- R.anim.recents_launch_next_affiliated_task_source);
- }
+ toTaskKey = group.getNextTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_target,
+ R.anim.recents_launch_next_affiliated_task_source);
} else {
- if ((i - 1) >= 0) {
- toTask = tasks.get(i - 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_target,
- R.anim.recents_launch_prev_affiliated_task_source);
- }
+ toTaskKey = group.getPrevTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_target,
+ R.anim.recents_launch_prev_affiliated_task_source);
+ }
+ if (toTaskKey != null) {
+ toTask = focusedStack.findTaskWithId(toTaskKey.id);
}
+ numAffiliatedTasks = group.getTaskCount();
break;
}
}
// Return early if there is no next task
if (toTask == null) {
- if (showNextTask) {
- ssp.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_bounce));
- } else {
- ssp.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_bounce));
+ if (numAffiliatedTasks > 1) {
+ if (showNextTask) {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_bounce));
+ } else {
+ ssp.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_bounce));
+ }
}
return;
}
@@ -738,7 +753,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
systemInsets.left, systemInsets.right, mTmpBounds);
stackLayout.reset();
- stackLayout.initialize(displayRect, windowRect, mTmpBounds);
+ stackLayout.initialize(displayRect, windowRect, mTmpBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
}
}
@@ -827,7 +843,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
launchOpts.runningTaskId = runningTaskId;
launchOpts.loadThumbnails = false;
launchOpts.onlyLoadForCache = true;
- Recents.getTaskLoader().loadTasks(sInstanceLoadPlan, launchOpts);
+ Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
}
/**
@@ -857,29 +873,61 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
Rect windowOverrideRect) {
final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
-
- // Update the destination rect
- Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
- windowOverrideRect);
-
- RectF toTaskRect = toTransform.rect;
- AppTransitionAnimationSpecsFuture future =
- new RecentsTransitionHelper(mContext).getAppTransitionFuture(
- () -> {
- Rect rect = new Rect();
- toTaskRect.round(rect);
- GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
- toTransform);
- return Lists.newArrayList(new AppTransitionAnimationSpec(
- toTask.key.id, thumbnail, rect));
- });
-
- // For low end ram devices, wait for transition flag is reset when Recents entrance
- // animation is complete instead of when the transition animation starts
- return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
- mHandler, future.getFuture(), isLowRamDevice ? null : mResetToggleFlagListener,
- false /* scaleUp */), future);
+ if (runningTask != null
+ && runningTask.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM) {
+ ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ ArrayList<Task> tasks = mDummyStackView.getStack().getStackTasks();
+ TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
+ TaskStackViewScroller stackScroller = mDummyStackView.getScroller();
+
+ mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */);
+ mDummyStackView.updateToInitialState();
+
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
+ stackScroller.getStackScroll(), mTmpTransform, null,
+ windowOverrideRect);
+ GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform);
+ Rect toTaskRect = new Rect();
+ mTmpTransform.rect.round(toTaskRect);
+ specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
+ }
+ }
+ AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
+ specs.toArray(specsArray);
+
+ // For low end ram devices, wait for transition flag is reset when Recents entrance
+ // animation is complete instead of when the transition animation starts
+ return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ specsArray, mHandler, isLowRamDevice ? null : mResetToggleFlagListener, this),
+ null);
+ } else {
+ // Update the destination rect
+ Task toTask = new Task();
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
+ windowOverrideRect);
+
+ RectF toTaskRect = toTransform.rect;
+ AppTransitionAnimationSpecsFuture future =
+ new RecentsTransitionHelper(mContext).getAppTransitionFuture(
+ () -> {
+ Rect rect = new Rect();
+ toTaskRect.round(rect);
+ GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
+ toTransform);
+ return Lists.newArrayList(new AppTransitionAnimationSpec(
+ toTask.key.id, thumbnail, rect));
+ });
+
+ // For low end ram devices, wait for transition flag is reset when Recents entrance
+ // animation is complete instead of when the transition animation starts
+ return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
+ mHandler, future.getFuture(), isLowRamDevice ? null : mResetToggleFlagListener,
+ false /* scaleUp */), future);
+ }
}
/**
@@ -894,7 +942,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
runningTaskOut.copyFrom(launchTask);
} else {
// If no task is specified or we can not find the task just use the front most one
- launchTask = stack.getStackFrontMostTask();
+ launchTask = stack.getStackFrontMostTask(true /* includeFreeform */);
runningTaskOut.copyFrom(launchTask);
}
@@ -947,8 +995,12 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
boolean isHomeStackVisible, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ boolean isBlacklisted = (runningTask != null)
+ ? ssp.isBlackListedActivity(runningTask.baseActivity.getClassName())
+ : false;
- int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
+ int runningTaskId = !mLaunchedWhileDocking && !isBlacklisted && (runningTask != null)
? runningTask.id
: -1;
@@ -957,10 +1009,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// the stacks might have changed.
if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
// Create a new load plan if preloadRecents() was never triggered
- sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, runningTaskId);
+ loader.preloadTasks(sInstanceLoadPlan, runningTaskId, !isHomeStackVisible);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -971,6 +1023,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Update the launch state that we need in updateHeaderBarLayout()
launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
+ launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted;
launchState.launchedFromPipApp = false;
launchState.launchedWithNextPipApp =
stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
@@ -1006,7 +1059,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
}
Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
- if (useThumbnailTransition) {
+ if (isBlacklisted) {
+ pair = new Pair<>(getUnknownTransitionActivityOptions(), null);
+ } else if (useThumbnailTransition) {
// Try starting with a thumbnail transition
pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
} else {
diff --git a/com/android/systemui/recents/RecentsSystemUser.java b/com/android/systemui/recents/RecentsSystemUser.java
index ff1f7dc5..12856260 100644
--- a/com/android/systemui/recents/RecentsSystemUser.java
+++ b/com/android/systemui/recents/RecentsSystemUser.java
@@ -27,7 +27,6 @@ import android.util.SparseArray;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
@@ -109,11 +108,6 @@ public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
}
@Override
- public void sendDockedFirstAnimationFrameEvent() throws RemoteException {
- EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
- }
-
- @Override
public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
EventBus.getDefault().post(new SetWaitingForTransitionStartEvent(
waitingForTransitionStart));
diff --git a/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
index fec34e3c..7604de1d 100644
--- a/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
+++ b/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
/**
* This is sent when we want to cancel the enter-recents window animation for the launch task.
diff --git a/com/android/systemui/shared/recents/model/TaskFilter.java b/com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java
index 9a1ff544..fe3bf263 100644
--- a/com/android/systemui/shared/recents/model/TaskFilter.java
+++ b/com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,17 +11,16 @@
* 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.shared.recents.model;
+package com.android.systemui.recents.events.activity;
-import android.util.SparseArray;
+import com.android.systemui.recents.events.EventBus;
/**
- * An interface for a task filter to query whether a particular task should show in a stack.
+ * This is sent when the SystemUI tuner changes a flag.
*/
-interface TaskFilter {
- /** Returns whether the filter accepts the specified task */
- boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
+public class DebugFlagsChangedEvent extends EventBus.Event {
+ // Simple event
}
diff --git a/com/android/systemui/recents/events/activity/IterateRecentsEvent.java b/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
new file mode 100644
index 00000000..f7b2706b
--- /dev/null
+++ b/com/android/systemui/recents/events/activity/IterateRecentsEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when the user taps on the Overview button to iterate to the next item in the
+ * Recents list.
+ */
+public class IterateRecentsEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
index 2409f39d..862a1eee 100644
--- a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
+++ b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
@@ -22,7 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.graphics.Rect;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskView;
/**
diff --git a/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
index e4972b1f..64eeafa1 100644
--- a/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
+++ b/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStack;
/**
* This is sent by the activity whenever the multi-window state has changed.
diff --git a/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
index 47670e03..3b68574c 100644
--- a/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
+++ b/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
@@ -17,20 +17,22 @@
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.RecentsActivity;
/**
- * This event is sent by {@link RecentsActivity} when a package on the the system changes.
+ * This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes.
* {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
* packages.
*/
public class PackagesChangedEvent extends EventBus.Event {
+ public final RecentsPackageMonitor monitor;
public final String packageName;
public final int userId;
- public PackagesChangedEvent(String packageName, int userId) {
+ public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) {
+ this.monitor = monitor;
this.packageName = packageName;
this.userId = userId;
}
diff --git a/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
index 51d02b5b..0d614e8c 100644
--- a/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ b/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStack;
/**
* This is sent by the activity whenever the task stach has changed.
diff --git a/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java b/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
index b52e83b8..4ed02708 100644
--- a/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
+++ b/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
/**
* This is sent when the data associated with a given {@link Task} should be deleted from the
diff --git a/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
index da19384a..40c30b88 100644
--- a/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
+++ b/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
/**
* This is sent when a user wants to show the application info for a {@link Task}.
diff --git a/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
index f0829280..e0ed7a9e 100644
--- a/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
+++ b/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.recents.model.ThumbnailData;
/**
* Sent when a task snapshot has changed.
diff --git a/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java b/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
index 881a64af..0628c501 100644
--- a/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
+++ b/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
@@ -17,8 +17,8 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.recents.views.TaskView;
/**
diff --git a/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java b/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java
new file mode 100644
index 00000000..b42da9c7
--- /dev/null
+++ b/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent to update the visibility of all visible freeform task views.
+ */
+public class UpdateFreeformTaskViewVisibilityEvent extends EventBus.Event {
+
+ public final boolean visible;
+
+ public UpdateFreeformTaskViewVisibilityEvent(boolean visible) {
+ this.visible = visible;
+ }
+}
diff --git a/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
index cf61b1ef..216be612 100644
--- a/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ b/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.DropTarget;
/**
diff --git a/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
index 297afc53..edd79959 100644
--- a/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
+++ b/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
@@ -17,8 +17,9 @@
package com.android.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.TaskView;
/**
diff --git a/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 73cbde99..73c282fe 100644
--- a/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.TaskView;
diff --git a/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
index 021be77b..e57fa2d8 100644
--- a/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
+++ b/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -19,7 +19,7 @@ package com.android.systemui.recents.events.ui.dragndrop;
import android.graphics.Point;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskView;
/**
diff --git a/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
index 64ba5748..7030729d 100644
--- a/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
+++ b/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.events.ui.dragndrop;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.RecentsViewTouchHandler;
import com.android.systemui.recents.views.TaskView;
diff --git a/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
index 171ab5e8..a1e4957a 100644
--- a/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
+++ b/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
@@ -22,5 +22,10 @@ import com.android.systemui.recents.events.EventBus;
* Focuses the next task view in the stack.
*/
public class FocusNextTaskViewEvent extends EventBus.Event {
- // Simple event
+
+ public final int timerIndicatorDuration;
+
+ public FocusNextTaskViewEvent(int timerIndicatorDuration) {
+ this.timerIndicatorDuration = timerIndicatorDuration;
+ }
}
diff --git a/java/lang/invoke/ArrayElementVarHandle.java b/com/android/systemui/recents/misc/NamedCounter.java
index e315ba77..ec3c39cc 100644
--- a/java/lang/invoke/ArrayElementVarHandle.java
+++ b/com/android/systemui/recents/misc/NamedCounter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,26 @@
* limitations under the License.
*/
-package java.lang.invoke;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
+package com.android.systemui.recents.misc;
/**
- * A VarHandle to access array elements.
- * @hide
+ * Used to generate successive incremented names.
*/
-final class ArrayElementVarHandle extends VarHandle {
- private ArrayElementVarHandle(Class<?> arrayClass) {
- super(arrayClass.getComponentType(), arrayClass, false /* isFinal */,
- arrayClass, int.class);
+public class NamedCounter {
+
+ int mCount;
+ String mPrefix = "";
+ String mSuffix = "";
+
+ public NamedCounter(String prefix, String suffix) {
+ mPrefix = prefix;
+ mSuffix = suffix;
}
- static ArrayElementVarHandle create(Class<?> arrayClass) {
- return new ArrayElementVarHandle(arrayClass);
+ /** Returns the next name. */
+ public String nextName() {
+ String name = mPrefix + mCount + mSuffix;
+ mCount++;
+ return name;
}
}
diff --git a/com/android/systemui/shared/recents/utilities/RectFEvaluator.java b/com/android/systemui/recents/misc/RectFEvaluator.java
index 51c1b5aa..72511de9 100644
--- a/com/android/systemui/shared/recents/utilities/RectFEvaluator.java
+++ b/com/android/systemui/recents/misc/RectFEvaluator.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.shared.recents.utilities;
+package com.android.systemui.recents.misc;
import android.animation.TypeEvaluator;
import android.graphics.RectF;
@@ -23,7 +23,7 @@ import android.graphics.RectF;
*/
public class RectFEvaluator implements TypeEvaluator<RectF> {
- private final RectF mRect = new RectF();
+ private RectF mRect = new RectF();
/**
* This function returns the result of linearly interpolating the start and
diff --git a/com/android/systemui/recents/misc/SystemServicesProxy.java b/com/android/systemui/recents/misc/SystemServicesProxy.java
index 87f24fdb..bddf9a59 100644
--- a/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -25,20 +25,27 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -48,18 +55,24 @@ import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IRemoteCallback;
-import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.MutableBoolean;
import android.view.Display;
@@ -76,12 +89,19 @@ import com.android.internal.os.BackgroundThread;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.pip.tv.PipMenuActivity;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.ThumbnailData;
import com.android.systemui.statusbar.policy.UserInfoController;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Random;
/**
* Acts as a shim around the real system services that we need to access data from, and provides
@@ -97,32 +117,42 @@ public class SystemServicesProxy {
sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
}
+ final static List<String> sRecentsBlacklist;
+ static {
+ sRecentsBlacklist = new ArrayList<>();
+ sRecentsBlacklist.add(PipMenuActivity.class.getName());
+ }
+
private static SystemServicesProxy sSystemServicesProxy;
AccessibilityManager mAccm;
ActivityManager mAm;
IActivityManager mIam;
PackageManager mPm;
+ IconDrawableFactory mDrawableFactory;
IPackageManager mIpm;
private final IDreamManager mDreamManager;
private final Context mContext;
AssistUtils mAssistUtils;
WindowManager mWm;
IWindowManager mIwm;
+ KeyguardManager mKgm;
UserManager mUm;
Display mDisplay;
String mRecentsPackage;
- private TaskStackChangeListeners mTaskStackChangeListeners;
+ ComponentName mAssistComponent;
private int mCurrentUserId;
boolean mIsSafeMode;
+ boolean mHasFreeformWorkspaceSupport;
+ Bitmap mDummyIcon;
int mDummyThumbnailWidth;
int mDummyThumbnailHeight;
Paint mBgProtectionPaint;
Canvas mBgProtectionCanvas;
- private final Handler mHandler = new Handler();
+ private final Handler mHandler = new H();
private final Runnable mGcRunnable = new Runnable() {
@Override
public void run() {
@@ -133,10 +163,144 @@ public class SystemServicesProxy {
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ /**
+ * An abstract class to track task stack changes.
+ * Classes should implement this instead of {@link android.app.ITaskStackListener}
+ * to reduce IPC calls from system services. These callbacks will be called on the main thread.
+ */
+ public abstract static class TaskStackListener {
+ /**
+ * NOTE: This call is made of the thread that the binder call comes in on.
+ */
+ public void onTaskStackChangedBackground() { }
+ public void onTaskStackChanged() { }
+ public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
+ public void onActivityUnpinned() { }
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
+ public void onPinnedStackAnimationStarted() { }
+ public void onPinnedStackAnimationEnded() { }
+ public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
+ public void onActivityDismissingDockedStack() { }
+ public void onActivityLaunchOnSecondaryDisplayFailed() { }
+ public void onTaskProfileLocked(int taskId, int userId) { }
+
+ /**
+ * Checks that the current user matches the user's SystemUI process. Since
+ * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
+ * TaskStackListener should make this call to verify that we don't act on events from other
+ * user's processes.
+ */
+ protected final boolean checkCurrentUserId(Context context, boolean debug) {
+ int processUserId = UserHandle.myUserId();
+ int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
+ if (processUserId != currentUserId) {
+ if (debug) {
+ Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
+ + " and the current user is uid=" + currentUserId);
+ }
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
+ * ActivityManagerService.
+ * This simply passes callbacks to listeners through {@link H}.
+ * */
+ private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
+
+ private final List<SystemServicesProxy.TaskStackListener> mTmpListeners = new ArrayList<>();
+
+ @Override
+ public void onTaskStackChanged() throws RemoteException {
+ // Call the task changed callback for the non-ui thread listeners first
+ synchronized (mTaskStackListeners) {
+ mTmpListeners.clear();
+ mTmpListeners.addAll(mTaskStackListeners);
+ }
+ for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
+ mTmpListeners.get(i).onTaskStackChangedBackground();
+ }
+
+ mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
+ mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
+ }
+
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
+ throws RemoteException {
+ mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
+ mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
+ new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
+ }
+
+ @Override
+ public void onActivityUnpinned() throws RemoteException {
+ mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
+ mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
+ }
+
+ @Override
+ public void onPinnedActivityRestartAttempt(boolean clearedTask)
+ throws RemoteException{
+ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onPinnedStackAnimationStarted() throws RemoteException {
+ mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
+ mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
+ }
+
+ @Override
+ public void onPinnedStackAnimationEnded() throws RemoteException {
+ mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ }
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId, int reason)
+ throws RemoteException {
+ mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onActivityDismissingDockedStack() throws RemoteException {
+ mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+ }
+
+ @Override
+ public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
+ mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED);
+ }
+
+ @Override
+ public void onTaskProfileLocked(int taskId, int userId) {
+ mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+ }
+
+ @Override
+ public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+ throws RemoteException {
+ mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+ }
+ };
+
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
(String name, Drawable picture, String userAccount) ->
mCurrentUserId = mAm.getCurrentUser();
+ /**
+ * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
+ */
+ private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
+
/** Private constructor */
private SystemServicesProxy(Context context) {
mContext = context.getApplicationContext();
@@ -144,18 +308,23 @@ public class SystemServicesProxy {
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIam = ActivityManager.getService();
mPm = context.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
mIpm = AppGlobals.getPackageManager();
mAssistUtils = new AssistUtils(context);
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mIwm = WindowManagerGlobal.getWindowManagerService();
+ mKgm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
mUm = UserManager.get(context);
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
+ mHasFreeformWorkspaceSupport =
+ mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
+ Settings.Global.getInt(context.getContentResolver(),
+ DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
mIsSafeMode = mPm.isSafeMode();
mCurrentUserId = mAm.getCurrentUser();
- mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -170,11 +339,23 @@ public class SystemServicesProxy {
mBgProtectionPaint.setColor(0xFFffffff);
mBgProtectionCanvas = new Canvas();
+ // Resolve the assist intent
+ mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
+
// Since SystemServicesProxy can be accessed from a per-SysUI process component, create a
// per-process listener to keep track of the current user id to reduce the number of binder
// calls to fetch it.
UserInfoController userInfoController = Dependency.get(UserInfoController.class);
userInfoController.addCallback(mOnUserInfoChangedListener);
+
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ // Create a dummy icon
+ mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ mDummyIcon.eraseColor(0xFF999999);
+ }
+
+ Collections.addAll(sRecentsBlacklist,
+ res.getStringArray(R.array.recents_blacklist_array));
}
/**
@@ -196,6 +377,110 @@ public class SystemServicesProxy {
}
/**
+ * @return whether the provided {@param className} is blacklisted
+ */
+ public boolean isBlackListedActivity(String className) {
+ return sRecentsBlacklist.contains(className);
+ }
+
+ /**
+ * Returns a list of the recents tasks.
+ *
+ * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
+ * will be visible, otherwise no excluded tasks will be
+ * visible.
+ */
+ public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
+ boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
+ if (mAm == null) return null;
+
+ // If we are mocking, then create some recent tasks
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ ArrayList<ActivityManager.RecentTaskInfo> tasks =
+ new ArrayList<ActivityManager.RecentTaskInfo>();
+ int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
+ for (int i = 0; i < count; i++) {
+ // Create a dummy component name
+ int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
+ ComponentName cn = new ComponentName("com.android.test" + packageIndex,
+ "com.android.test" + i + ".Activity");
+ String description = "" + i + " - " +
+ Long.toString(Math.abs(new Random().nextLong()), 36);
+ // Create the recent task info
+ ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
+ rti.id = rti.persistentId = rti.affiliatedTaskId = i;
+ rti.baseIntent = new Intent();
+ rti.baseIntent.setComponent(cn);
+ rti.description = description;
+ rti.firstActiveTime = rti.lastActiveTime = i;
+ if (i % 2 == 0) {
+ rti.taskDescription = new ActivityManager.TaskDescription(description,
+ Bitmap.createBitmap(mDummyIcon), null,
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
+ 0, 0);
+ } else {
+ rti.taskDescription = new ActivityManager.TaskDescription();
+ }
+ tasks.add(rti);
+ }
+ return tasks;
+ }
+
+ // Remove home/recents/excluded tasks
+ int minNumTasksToQuery = 10;
+ int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
+ int flags = ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS |
+ ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
+ ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE |
+ ActivityManager.RECENT_INCLUDE_PROFILES;
+ if (includeFrontMostExcludedTask) {
+ flags |= ActivityManager.RECENT_WITH_EXCLUDED;
+ }
+ List<ActivityManager.RecentTaskInfo> tasks = null;
+ try {
+ tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get recent tasks", e);
+ }
+
+ // Break early if we can't get a valid set of tasks
+ if (tasks == null) {
+ return new ArrayList<>();
+ }
+
+ boolean isFirstValidTask = true;
+ Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
+ while (iter.hasNext()) {
+ ActivityManager.RecentTaskInfo t = iter.next();
+
+ // NOTE: The order of these checks happens in the expected order of the traversal of the
+ // tasks
+
+ // Remove the task if it or it's package are blacklsited
+ if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
+ sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
+ iter.remove();
+ continue;
+ }
+
+ // Remove the task if it is marked as excluded, unless it is the first most task and we
+ // are requested to include it
+ boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+ isExcluded |= quietProfileIds.contains(t.userId);
+ if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
+ iter.remove();
+ }
+
+ isFirstValidTask = false;
+ }
+
+ return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
+ }
+
+ /**
* Returns the top running task.
*/
public ActivityManager.RunningTaskInfo getRunningTask() {
@@ -287,6 +572,13 @@ public class SystemServicesProxy {
}
/**
+ * Returns whether this device has freeform workspaces.
+ */
+ public boolean hasFreeformWorkspaceSupport() {
+ return mHasFreeformWorkspaceSupport;
+ }
+
+ /**
* Returns whether this device is in the safe mode.
*/
public boolean isInSafeMode() {
@@ -354,7 +646,7 @@ public class SystemServicesProxy {
*/
public boolean hasSoftNavigationBar() {
try {
- return mIwm.hasNavigationBar();
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -397,6 +689,43 @@ public class SystemServicesProxy {
}
}
+ /** Returns the top task thumbnail for the given task id */
+ public ThumbnailData getTaskThumbnail(int taskId, boolean reduced) {
+ if (mAm == null) return null;
+
+ // If we are mocking, then just return a dummy thumbnail
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ ThumbnailData thumbnailData = new ThumbnailData();
+ thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
+ mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
+ thumbnailData.thumbnail.eraseColor(0xff333333);
+ return thumbnailData;
+ }
+
+ return getThumbnail(taskId, reduced);
+ }
+
+ /**
+ * Returns a task thumbnail from the activity manager
+ */
+ public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) {
+ if (mAm == null) {
+ return new ThumbnailData();
+ }
+
+ ActivityManager.TaskSnapshot snapshot = null;
+ try {
+ snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to retrieve snapshot", e);
+ }
+ if (snapshot != null) {
+ return ThumbnailData.createFromTaskSnapshot(snapshot);
+ } else {
+ return new ThumbnailData();
+ }
+ }
+
/** Set the task's windowing mode. */
public void setTaskWindowingMode(int taskId, int windowingMode) {
if (mIam == null) return;
@@ -411,14 +740,11 @@ public class SystemServicesProxy {
/** Removes the task */
public void removeTask(final int taskId) {
if (mAm == null) return;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return;
// Remove the task.
mUiOffloadThread.submit(() -> {
- try {
- mIam.removeTask(taskId);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ mAm.removeTask(taskId);
});
}
@@ -434,6 +760,145 @@ public class SystemServicesProxy {
});
}
+ /**
+ * Returns the activity info for a given component name.
+ *
+ * @param cn The component name of the activity.
+ * @param userId The userId of the user that this is for.
+ */
+ public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
+ if (mIpm == null) return null;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
+
+ try {
+ return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Returns the activity info for a given component name.
+ *
+ * @param cn The component name of the activity.
+ */
+ public ActivityInfo getActivityInfo(ComponentName cn) {
+ if (mPm == null) return null;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
+
+ try {
+ return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Returns the activity label, badging if necessary.
+ */
+ public String getBadgedActivityLabel(ActivityInfo info, int userId) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return "Recent Task: " + userId;
+ }
+
+ return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
+ }
+
+ /**
+ * Returns the application label, badging if necessary.
+ */
+ public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return "Recent Task App: " + userId;
+ }
+
+ return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
+ }
+
+ /**
+ * Returns the content description for a given task, badging it if necessary. The content
+ * description joins the app and activity labels.
+ */
+ public String getBadgedContentDescription(ActivityInfo info, int userId,
+ ActivityManager.TaskDescription td, Resources res) {
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return "Recent Task Content Description: " + userId;
+ }
+
+ String activityLabel;
+ if (td != null && td.getLabel() != null) {
+ activityLabel = td.getLabel();
+ } else {
+ activityLabel = info.loadLabel(mPm).toString();
+ }
+ String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
+ String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
+ return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
+ : res.getString(R.string.accessibility_recents_task_header,
+ badgedApplicationLabel, activityLabel);
+ }
+
+ /**
+ * Returns the activity icon for the ActivityInfo for a user, badging if
+ * necessary.
+ */
+ public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
+ }
+
+ /**
+ * Returns the application icon for the ApplicationInfo for a user, badging if
+ * necessary.
+ */
+ public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ return mDrawableFactory.getBadgedIcon(appInfo, userId);
+ }
+
+ /**
+ * Returns the task description icon, loading and badging it if it necessary.
+ */
+ public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
+ int userId, Resources res) {
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Bitmap tdIcon = taskDescription.getInMemoryIcon();
+ if (tdIcon == null) {
+ tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+ taskDescription.getIconFilename(), userId);
+ }
+ if (tdIcon != null) {
+ return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
+ }
+ return null;
+ }
+
public ActivityManager.TaskDescription getTaskDescription(int taskId) {
try {
return mIam.getTaskDescription(taskId);
@@ -443,6 +908,85 @@ public class SystemServicesProxy {
}
/**
+ * Returns the given icon for a user, badging if necessary.
+ */
+ private Drawable getBadgedIcon(Drawable icon, int userId) {
+ if (userId != UserHandle.myUserId()) {
+ icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
+ }
+ return icon;
+ }
+
+ /**
+ * Returns a banner used on TV for the specified Activity.
+ */
+ public Drawable getActivityBanner(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock banner
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable banner = info.loadBanner(mPm);
+ return banner;
+ }
+
+ /**
+ * Returns a logo used on TV for the specified Activity.
+ */
+ public Drawable getActivityLogo(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock logo
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable logo = info.loadLogo(mPm);
+ return logo;
+ }
+
+
+ /**
+ * Returns the given label for a user, badging if necessary.
+ */
+ private String getBadgedLabel(String label, int userId) {
+ if (userId != UserHandle.myUserId()) {
+ label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
+ }
+ return label;
+ }
+
+ /**
+ * Returns whether the provided {@param userId} is currently locked (and showing Keyguard).
+ */
+ public boolean isDeviceLocked(int userId) {
+ if (mKgm == null) {
+ return false;
+ }
+ return mKgm.isDeviceLocked(userId);
+ }
+
+ /** Returns the package name of the home activity. */
+ public String getHomeActivityPackageName() {
+ if (mPm == null) return null;
+ if (RecentsDebugFlags.Static.EnableMockTasks) return null;
+
+ ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
+ ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
+ if (defaultHomeActivity != null) {
+ return defaultHomeActivity.getPackageName();
+ } else if (homeActivities.size() == 1) {
+ ResolveInfo info = homeActivities.get(0);
+ if (info.activityInfo != null) {
+ return info.activityInfo.packageName;
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns whether the provided {@param userId} represents the system user.
*/
public boolean isSystemUser(int userId) {
@@ -547,7 +1091,7 @@ public class SystemServicesProxy {
ActivityManager.StackInfo stackInfo =
mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (stackInfo == null) {
- stackInfo = mIam.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
}
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
@@ -630,11 +1174,19 @@ public class SystemServicesProxy {
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
- public void registerTaskStackListener(TaskStackChangeListener listener) {
+ public void registerTaskStackListener(TaskStackListener listener) {
if (mIam == null) return;
- synchronized (mTaskStackChangeListeners) {
- mTaskStackChangeListeners.addListener(mIam, listener);
+ synchronized (mTaskStackListeners) {
+ mTaskStackListeners.add(listener);
+ if (mTaskStackListeners.size() == 1) {
+ // Register mTaskStackListener to IActivityManager only once if needed.
+ try {
+ mIam.registerTaskStackListener(mTaskStackListener);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ }
+ }
}
}
@@ -643,7 +1195,7 @@ public class SystemServicesProxy {
return;
}
try {
- mIwm.endProlongedAnimations();
+ WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
} catch (Exception e) {
e.printStackTrace();
}
@@ -653,7 +1205,7 @@ public class SystemServicesProxy {
if (mWm == null) return;
try {
- mIwm.registerDockedStackListener(listener);
+ WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
} catch (Exception e) {
e.printStackTrace();
}
@@ -680,7 +1232,8 @@ public class SystemServicesProxy {
if (mWm == null) return;
try {
- mIwm.getStableInsets(Display.DEFAULT_DISPLAY, outStableInsets);
+ WindowManagerGlobal.getWindowManagerService().getStableInsets(Display.DEFAULT_DISPLAY,
+ outStableInsets);
} catch (Exception e) {
e.printStackTrace();
}
@@ -690,7 +1243,9 @@ public class SystemServicesProxy {
IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
boolean scaleUp) {
try {
- mIwm.overridePendingAppTransitionMultiThumbFuture(future, animStartedListener, scaleUp);
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
+ scaleUp);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override transition: " + e);
}
@@ -699,27 +1254,23 @@ public class SystemServicesProxy {
/**
* Updates the visibility of recents.
*/
- public void setRecentsVisibility(final boolean visible) {
- mUiOffloadThread.submit(() -> {
- try {
- mIwm.setRecentsVisibility(visible);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
- });
+ public void setRecentsVisibility(boolean visible) {
+ try {
+ mIwm.setRecentsVisibility(visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
+ }
}
/**
* Updates the visibility of the picture-in-picture.
*/
- public void setPipVisibility(final boolean visible) {
- mUiOffloadThread.submit(() -> {
- try {
- mIwm.setPipVisibility(visible);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
- });
+ public void setPipVisibility(boolean visible) {
+ try {
+ mIwm.setPipVisibility(visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
+ }
}
public boolean isDreaming() {
@@ -741,7 +1292,126 @@ public class SystemServicesProxy {
});
}
+ public void updateOverviewLastStackActiveTimeAsync(long newLastStackActiveTime,
+ int currentUserId) {
+ mUiOffloadThread.submit(() -> {
+ Settings.Secure.putLongForUser(mContext.getContentResolver(),
+ Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, newLastStackActiveTime, currentUserId);
+ });
+ }
+
public interface StartActivityFromRecentsResultListener {
void onStartActivityResult(boolean succeeded);
}
+
+ private class PinnedActivityInfo {
+ final String mPackageName;
+ final int mUserId;
+ final int mTaskId;
+ final int mStackId;
+
+ PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mTaskId = taskId;
+ mStackId = stackId;
+ }
+ }
+
+ private final class H extends Handler {
+ private static final int ON_TASK_STACK_CHANGED = 1;
+ private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
+ private static final int ON_ACTIVITY_PINNED = 3;
+ private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
+ private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
+ private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
+ private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
+ private static final int ON_TASK_PROFILE_LOCKED = 8;
+ private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
+ private static final int ON_ACTIVITY_UNPINNED = 10;
+ private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mTaskStackListeners) {
+ switch (msg.what) {
+ case ON_TASK_STACK_CHANGED: {
+ Trace.beginSection("onTaskStackChanged");
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskStackChanged();
+ }
+ Trace.endSection();
+ break;
+ }
+ case ON_TASK_SNAPSHOT_CHANGED: {
+ Trace.beginSection("onTaskSnapshotChanged");
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
+ (TaskSnapshot) msg.obj);
+ }
+ Trace.endSection();
+ break;
+ }
+ case ON_ACTIVITY_PINNED: {
+ final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityPinned(
+ info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
+ }
+ break;
+ }
+ case ON_ACTIVITY_UNPINNED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityUnpinned();
+ }
+ break;
+ }
+ case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
+ msg.arg1 != 0);
+ }
+ break;
+ }
+ case ON_PINNED_STACK_ANIMATION_STARTED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
+ }
+ break;
+ }
+ case ON_PINNED_STACK_ANIMATION_ENDED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
+ }
+ break;
+ }
+ case ON_ACTIVITY_FORCED_RESIZABLE: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
+ }
+ case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityDismissingDockedStack();
+ }
+ break;
+ }
+ case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
+ }
+ break;
+ }
+ case ON_TASK_PROFILE_LOCKED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
}
diff --git a/com/android/systemui/recents/misc/TaskStackChangeListener.java b/com/android/systemui/recents/misc/TaskStackChangeListener.java
deleted file mode 100644
index 6d0952ab..00000000
--- a/com/android/systemui/recents/misc/TaskStackChangeListener.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.misc;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.content.Context;
-import android.os.UserHandle;
-import android.util.Log;
-
-/**
- * An abstract class to track task stack changes.
- * Classes should implement this instead of {@link android.app.ITaskStackListener}
- * to reduce IPC calls from system services. These callbacks will be called on the main thread.
- */
-public abstract class TaskStackChangeListener {
-
- /**
- * NOTE: This call is made of the thread that the binder call comes in on.
- */
- public void onTaskStackChangedBackground() { }
- public void onTaskStackChanged() { }
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
- public void onActivityUnpinned() { }
- public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
- public void onPinnedStackAnimationStarted() { }
- public void onPinnedStackAnimationEnded() { }
- public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
- public void onActivityDismissingDockedStack() { }
- public void onActivityLaunchOnSecondaryDisplayFailed() { }
- public void onTaskProfileLocked(int taskId, int userId) { }
-
- /**
- * Checks that the current user matches the user's SystemUI process. Since
- * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
- * TaskStackChangeListener should make this call to verify that we don't act on events from other
- * user's processes.
- */
- protected final boolean checkCurrentUserId(Context context, boolean debug) {
- int processUserId = UserHandle.myUserId();
- int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
- if (processUserId != currentUserId) {
- if (debug) {
- Log.d(SystemServicesProxy.TAG, "UID mismatch. SystemUI is running uid=" + processUserId
- + " and the current user is uid=" + currentUserId);
- }
- return false;
- }
- return true;
- }
-}
diff --git a/com/android/systemui/recents/misc/TaskStackChangeListeners.java b/com/android/systemui/recents/misc/TaskStackChangeListeners.java
deleted file mode 100644
index 8eb70f04..00000000
--- a/com/android/systemui/recents/misc/TaskStackChangeListeners.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.misc;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.IActivityManager;
-import android.app.TaskStackListener;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tracks all the task stack listeners
- */
-public class TaskStackChangeListeners extends TaskStackListener {
-
- private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
-
- /**
- * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
- */
- private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
- private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
-
- private final Handler mHandler;
-
- public TaskStackChangeListeners(Looper looper) {
- mHandler = new H(looper);
- }
-
- public void addListener(IActivityManager am, TaskStackChangeListener listener) {
- mTaskStackListeners.add(listener);
- if (mTaskStackListeners.size() == 1) {
- // Register mTaskStackListener to IActivityManager only once if needed.
- try {
- am.registerTaskStackListener(this);
- } catch (Exception e) {
- Log.w(TAG, "Failed to call registerTaskStackListener", e);
- }
- }
- }
-
- @Override
- public void onTaskStackChanged() throws RemoteException {
- // Call the task changed callback for the non-ui thread listeners first
- synchronized (mTaskStackListeners) {
- mTmpListeners.clear();
- mTmpListeners.addAll(mTaskStackListeners);
- }
- for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
- mTmpListeners.get(i).onTaskStackChangedBackground();
- }
-
- mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
- mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
- }
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
- throws RemoteException {
- mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
- mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
- new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
- }
-
- @Override
- public void onActivityUnpinned() throws RemoteException {
- mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
- mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
- }
-
- @Override
- public void onPinnedActivityRestartAttempt(boolean clearedTask)
- throws RemoteException{
- mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
- mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
- .sendToTarget();
- }
-
- @Override
- public void onPinnedStackAnimationStarted() throws RemoteException {
- mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
- mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
- }
-
- @Override
- public void onPinnedStackAnimationEnded() throws RemoteException {
- mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
- mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
- }
-
- @Override
- public void onActivityForcedResizable(String packageName, int taskId, int reason)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
- .sendToTarget();
- }
-
- @Override
- public void onActivityDismissingDockedStack() throws RemoteException {
- mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
- }
-
- @Override
- public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
- mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED);
- }
-
- @Override
- public void onTaskProfileLocked(int taskId, int userId) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
- }
-
- @Override
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
- }
-
- private final class H extends Handler {
- private static final int ON_TASK_STACK_CHANGED = 1;
- private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
- private static final int ON_ACTIVITY_PINNED = 3;
- private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
- private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
- private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
- private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
- private static final int ON_TASK_PROFILE_LOCKED = 8;
- private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
- private static final int ON_ACTIVITY_UNPINNED = 10;
- private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
-
- public H(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- synchronized (mTaskStackListeners) {
- switch (msg.what) {
- case ON_TASK_STACK_CHANGED: {
- Trace.beginSection("onTaskStackChanged");
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskStackChanged();
- }
- Trace.endSection();
- break;
- }
- case ON_TASK_SNAPSHOT_CHANGED: {
- Trace.beginSection("onTaskSnapshotChanged");
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
- (TaskSnapshot) msg.obj);
- }
- Trace.endSection();
- break;
- }
- case ON_ACTIVITY_PINNED: {
- final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityPinned(
- info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
- }
- break;
- }
- case ON_ACTIVITY_UNPINNED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityUnpinned();
- }
- break;
- }
- case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
- msg.arg1 != 0);
- }
- break;
- }
- case ON_PINNED_STACK_ANIMATION_STARTED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
- }
- break;
- }
- case ON_PINNED_STACK_ANIMATION_ENDED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
- }
- break;
- }
- case ON_ACTIVITY_FORCED_RESIZABLE: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityForcedResizable(
- (String) msg.obj, msg.arg1, msg.arg2);
- }
- break;
- }
- case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityDismissingDockedStack();
- }
- break;
- }
- case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
- }
- break;
- }
- case ON_TASK_PROFILE_LOCKED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
- }
- break;
- }
- }
- }
- }
- }
-
- private static class PinnedActivityInfo {
- final String mPackageName;
- final int mUserId;
- final int mTaskId;
- final int mStackId;
-
- PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
- mPackageName = packageName;
- mUserId = userId;
- mTaskId = taskId;
- mStackId = stackId;
- }
- }
-}
diff --git a/com/android/systemui/shared/recents/utilities/Utilities.java b/com/android/systemui/recents/misc/Utilities.java
index a5d19639..4349e30f 100644
--- a/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/com/android/systemui/recents/misc/Utilities.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.utilities;
+package com.android.systemui.recents.misc;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -38,8 +38,12 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewStub;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.TaskViewTransform;
+
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/* Common code */
public class Utilities {
@@ -72,6 +76,7 @@ public class Utilities {
public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+ public static final Rect EMPTY_RECT = new Rect();
/**
* @return the first parent walking up the view hierarchy that has the given class type.
@@ -248,6 +253,24 @@ public class Utilities {
}
/**
+ * Updates {@param transforms} to be the same size as {@param tasks}.
+ */
+ public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
+ // We can reuse the task transforms where possible to reduce object allocation
+ int taskTransformCount = transforms.size();
+ int taskCount = tasks.size();
+ if (taskTransformCount < taskCount) {
+ // If there are less transforms than tasks, then add as many transforms as necessary
+ for (int i = taskTransformCount; i < taskCount; i++) {
+ transforms.add(new TaskViewTransform());
+ }
+ } else if (taskTransformCount > taskCount) {
+ // If there are more transforms than tasks, then just subset the transform list
+ transforms.subList(taskCount, taskTransformCount).clear();
+ }
+ }
+
+ /**
* Used for debugging, converts DP to PX.
*/
public static float dpToPx(Resources res, float dp) {
diff --git a/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java b/com/android/systemui/recents/model/HighResThumbnailLoader.java
index 24ba9984..6414ea1e 100644
--- a/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
+++ b/com/android/systemui/recents/model/HighResThumbnailLoader.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
import static android.os.Process.setThreadPriority;
@@ -25,8 +25,10 @@ import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task.TaskCallbacks;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -36,8 +38,6 @@ import java.util.ArrayList;
*/
public class HighResThumbnailLoader implements TaskCallbacks {
- private final ActivityManagerWrapper mActivityManager;
-
@GuardedBy("mLoadQueue")
private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
@GuardedBy("mLoadQueue")
@@ -46,21 +46,20 @@ public class HighResThumbnailLoader implements TaskCallbacks {
private boolean mLoaderIdling;
private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
-
private final Thread mLoadThread;
private final Handler mMainThreadHandler;
+ private final SystemServicesProxy mSystemServicesProxy;
private final boolean mIsLowRamDevice;
private boolean mLoading;
private boolean mVisible;
private boolean mFlingingFast;
private boolean mTaskLoadQueueIdle;
- public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper,
- boolean isLowRamDevice) {
- mActivityManager = activityManager;
+ public HighResThumbnailLoader(SystemServicesProxy ssp, Looper looper, boolean isLowRamDevice) {
mMainThreadHandler = new Handler(looper);
mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
mLoadThread.start();
+ mSystemServicesProxy = ssp;
mIsLowRamDevice = isLowRamDevice;
}
@@ -221,7 +220,7 @@ public class HighResThumbnailLoader implements TaskCallbacks {
}
private void loadTask(Task t) {
- ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id,
+ ThumbnailData thumbnail = mSystemServicesProxy.getTaskThumbnail(t.key.id,
false /* reducedResolution */);
mMainThreadHandler.post(() -> {
synchronized (mLoadQueue) {
diff --git a/com/android/systemui/recents/model/RecentsPackageMonitor.java b/com/android/systemui/recents/model/RecentsPackageMonitor.java
new file mode 100644
index 00000000..308cece1
--- /dev/null
+++ b/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.model;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.misc.ForegroundThread;
+
+/**
+ * The package monitor listens for changes from PackageManager to update the contents of the
+ * Recents list.
+ */
+public class RecentsPackageMonitor extends PackageMonitor {
+
+ /** Registers the broadcast receivers with the specified callbacks. */
+ public void register(Context context) {
+ try {
+ // We register for events from all users, but will cross-reference them with
+ // packages for the current user and any profiles they have. Ensure that events are
+ // handled in a background thread.
+ register(context, ForegroundThread.get().getLooper(), UserHandle.ALL, true);
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Unregisters the broadcast receivers. */
+ @Override
+ public void unregister() {
+ try {
+ super.unregister();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ // Notify callbacks on the main thread that a package has changed
+ final int eventUserId = getChangingUserId();
+ EventBus.getDefault().post(new PackagesChangedEvent(this, packageName, eventUserId));
+ }
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ onPackageModified(packageName);
+ return true;
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ // Notify callbacks on the main thread that a package has changed
+ final int eventUserId = getChangingUserId();
+ EventBus.getDefault().post(new PackagesChangedEvent(this, packageName, eventUserId));
+ }
+}
diff --git a/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
new file mode 100644
index 00000000..d5e03135
--- /dev/null
+++ b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.model;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
+import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * This class stores the loading state as it goes through multiple stages of loading:
+ * 1) preloadRawTasks() will load the raw set of recents tasks from the system
+ * 2) preloadPlan() will construct a new task stack with all metadata and only icons and
+ * thumbnails that are currently in the cache
+ * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load
+ * options specified, such that we can transition into the Recents activity seamlessly
+ */
+public class RecentsTaskLoadPlan {
+
+ private static int MIN_NUM_TASKS = 5;
+ private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
+ 6 /* hrs */;
+
+ /** The set of conditions to load tasks. */
+ public static class Options {
+ public int runningTaskId = -1;
+ public boolean loadIcons = true;
+ public boolean loadThumbnails = false;
+ public boolean onlyLoadForCache = false;
+ public boolean onlyLoadPausedActivities = false;
+ public int numVisibleTasks = 0;
+ public int numVisibleTaskThumbnails = 0;
+ }
+
+ Context mContext;
+
+ int mPreloadedUserId;
+ List<ActivityManager.RecentTaskInfo> mRawTasks;
+ TaskStack mStack;
+ ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
+
+ /** Package level ctor */
+ RecentsTaskLoadPlan(Context context) {
+ mContext = context;
+ }
+
+ private void updateCurrentQuietProfilesCache(int currentUserId) {
+ mCurrentQuietProfiles.clear();
+
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+ if (profiles != null) {
+ for (int i = 0; i < profiles.size(); i++) {
+ UserInfo user = profiles.get(i);
+ if (user.isManagedProfile() && user.isQuietModeEnabled()) {
+ mCurrentQuietProfiles.add(user.id);
+ }
+ }
+ }
+ }
+
+ /**
+ * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
+ * to most-recent order.
+ *
+ * Note: Do not lock, callers should synchronize on the loader before making this call.
+ */
+ void preloadRawTasks(boolean includeFrontMostExcludedTask) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ int currentUserId = ssp.getCurrentUser();
+ updateCurrentQuietProfilesCache(currentUserId);
+ mPreloadedUserId = currentUserId;
+ mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
+ currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
+
+ // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
+ Collections.reverse(mRawTasks);
+ }
+
+ /**
+ * Preloads the list of recent tasks from the system. After this call, the TaskStack will
+ * have a list of all the recent tasks with their metadata, not including icons or
+ * thumbnails which were not cached and have to be loaded.
+ *
+ * The tasks will be ordered by:
+ * - least-recent to most-recent stack tasks
+ * - least-recent to most-recent freeform tasks
+ *
+ * Note: Do not lock, since this can be calling back to the loader, which separately also drives
+ * this call (callers should synchronize on the loader before making this call).
+ */
+ void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
+ boolean includeFrontMostExcludedTask) {
+ Resources res = mContext.getResources();
+ ArrayList<Task> allTasks = new ArrayList<>();
+ if (mRawTasks == null) {
+ preloadRawTasks(includeFrontMostExcludedTask);
+ }
+
+ SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
+ SparseIntArray affiliatedTaskCounts = new SparseIntArray();
+ SparseBooleanArray lockedUsers = new SparseBooleanArray();
+ String dismissDescFormat = mContext.getString(
+ R.string.accessibility_recents_item_will_be_dismissed);
+ String appInfoDescFormat = mContext.getString(
+ R.string.accessibility_recents_item_open_app_info);
+ int currentUserId = mPreloadedUserId;
+ long legacyLastStackActiveTime = migrateLegacyLastStackActiveTime(currentUserId);
+ long lastStackActiveTime = Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, legacyLastStackActiveTime, currentUserId);
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ lastStackActiveTime = 0;
+ }
+ long newLastStackActiveTime = -1;
+ int taskCount = mRawTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+
+ // Compose the task key
+ final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+ Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent,
+ t.userId, t.firstActiveTime, t.lastActiveTime);
+
+ // This task is only shown in the stack if it satisfies the historical time or min
+ // number of tasks constraints. Freeform tasks are also always shown.
+ boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
+ boolean isStackTask;
+ if (Recents.getConfiguration().isGridEnabled) {
+ // When grid layout is enabled, we only show the first
+ // TaskGridLayoutAlgorithm.MAX_LAYOUT_FROM_HOME_TASK_COUNT} tasks.
+ isStackTask = t.lastActiveTime >= lastStackActiveTime &&
+ i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
+ } else if (Recents.getConfiguration().isLowRamDevice) {
+ // Show a max of 3 items
+ isStackTask = t.lastActiveTime >= lastStackActiveTime &&
+ i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
+ } else {
+ isStackTask = isFreeformTask || !isHistoricalTask(t) ||
+ (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
+ }
+ boolean isLaunchTarget = taskKey.id == runningTaskId;
+
+ // The last stack active time is the baseline for which we show visible tasks. Since
+ // the system will store all the tasks, we don't want to show the tasks prior to the
+ // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
+ // the other stack-task constraints.
+ if (isStackTask && newLastStackActiveTime < 0) {
+ newLastStackActiveTime = t.lastActiveTime;
+ }
+
+ // Load the title, icon, and color
+ ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
+ String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
+ String titleDescription = loader.getAndUpdateContentDescription(taskKey,
+ t.taskDescription, res);
+ String dismissDescription = String.format(dismissDescFormat, titleDescription);
+ String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
+ Drawable icon = isStackTask
+ ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
+ : null;
+ ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
+ false /* loadIfNotCached */, false /* storeInCache */);
+ int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+ int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+ boolean isSystemApp = (info != null) &&
+ ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ if (lockedUsers.indexOfKey(t.userId) < 0) {
+ lockedUsers.put(t.userId, Recents.getSystemServices().isDeviceLocked(t.userId));
+ }
+ boolean isLocked = lockedUsers.get(t.userId);
+
+ // Add the task to the stack
+ Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
+ thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
+ activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
+ t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
+ isLocked);
+
+ allTasks.add(task);
+ affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
+ affiliatedTasks.put(taskKey.id, taskKey);
+ }
+ if (newLastStackActiveTime != -1) {
+ Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
+ newLastStackActiveTime, currentUserId);
+ }
+
+ // Initialize the stacks
+ mStack = new TaskStack();
+ mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
+ }
+
+ /**
+ * Called to apply the actual loading based on the specified conditions.
+ *
+ * Note: Do not lock, since this can be calling back to the loader, which separately also drives
+ * this call (callers should synchronize on the loader before making this call).
+ */
+ void executePlan(Options opts, RecentsTaskLoader loader) {
+ Resources res = mContext.getResources();
+
+ // Iterate through each of the tasks and load them according to the load conditions.
+ ArrayList<Task> tasks = mStack.getStackTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ Task.TaskKey taskKey = task.key;
+
+ boolean isRunningTask = (task.key.id == opts.runningTaskId);
+ boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+ boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+ // If requested, skip the running task
+ if (opts.onlyLoadPausedActivities && isRunningTask) {
+ continue;
+ }
+
+ if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+ if (task.icon == null) {
+ task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+ true);
+ }
+ }
+ if (opts.loadThumbnails && isVisibleThumbnail) {
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+ true /* loadIfNotCached */, true /* storeInCache */);
+ }
+ }
+ }
+
+ /**
+ * Returns the TaskStack from the preloaded list of recent tasks.
+ */
+ public TaskStack getTaskStack() {
+ return mStack;
+ }
+
+ /**
+ * Returns the raw list of recent tasks.
+ */
+ public List<ActivityManager.RecentTaskInfo> getRawTasks() {
+ return mRawTasks;
+ }
+
+ /** Returns whether there are any tasks in any stacks. */
+ public boolean hasTasks() {
+ if (mStack != null) {
+ return mStack.getTaskCount() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether this task is too old to be shown.
+ */
+ private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
+ return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
+ }
+
+
+ /**
+ * Migrate the last active time from the prefs to the secure settings.
+ *
+ * The first time this runs, it will:
+ * 1) fetch the last stack active time from the prefs
+ * 2) set the prefs to the last stack active time for all users
+ * 3) clear the pref
+ * 4) return the last stack active time
+ *
+ * Subsequent calls to this will return zero.
+ */
+ private long migrateLegacyLastStackActiveTime(int currentUserId) {
+ long legacyLastStackActiveTime = Prefs.getLong(mContext,
+ Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
+ if (legacyLastStackActiveTime != -1) {
+ Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME);
+ UserManager userMgr = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ List<UserInfo> users = userMgr.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ int userId = users.get(i).id;
+ if (userId != currentUserId) {
+ Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
+ legacyLastStackActiveTime, userId);
+ }
+ }
+ return legacyLastStackActiveTime;
+ }
+ return 0;
+ }
+}
diff --git a/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/com/android/systemui/recents/model/RecentsTaskLoader.java
index de4c72c2..1b893862 100644
--- a/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
@@ -25,47 +25,238 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Trace;
import android.util.Log;
import android.util.LruCache;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import java.io.PrintWriter;
import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedQueue;
/**
+ * A Task load queue
+ */
+class TaskResourceLoadQueue {
+
+ ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
+
+ /** Adds a new task to the load queue */
+ void addTask(Task t) {
+ if (!mQueue.contains(t)) {
+ mQueue.add(t);
+ }
+ synchronized(this) {
+ notifyAll();
+ }
+ }
+
+ /**
+ * Retrieves the next task from the load queue, as well as whether we want that task to be
+ * force reloaded.
+ */
+ Task nextTask() {
+ return mQueue.poll();
+ }
+
+ /** Removes a task from the load queue */
+ void removeTask(Task t) {
+ mQueue.remove(t);
+ }
+
+ /** Clears all the tasks from the load queue */
+ void clearTasks() {
+ mQueue.clear();
+ }
+
+ /** Returns whether the load queue is empty */
+ boolean isEmpty() {
+ return mQueue.isEmpty();
+ }
+}
+
+/**
+ * Task resource loader
+ */
+class BackgroundTaskLoader implements Runnable {
+ static String TAG = "TaskResourceLoader";
+ static boolean DEBUG = false;
+
+ Context mContext;
+ HandlerThread mLoadThread;
+ Handler mLoadThreadHandler;
+ Handler mMainThreadHandler;
+
+ TaskResourceLoadQueue mLoadQueue;
+ TaskKeyLruCache<Drawable> mIconCache;
+ BitmapDrawable mDefaultIcon;
+
+ boolean mStarted;
+ boolean mCancelled;
+ boolean mWaitingOnLoadQueue;
+
+ private final OnIdleChangedListener mOnIdleChangedListener;
+
+ /** Constructor, creates a new loading thread that loads task resources in the background */
+ public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
+ TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon,
+ OnIdleChangedListener onIdleChangedListener) {
+ mLoadQueue = loadQueue;
+ mIconCache = iconCache;
+ mDefaultIcon = defaultIcon;
+ mMainThreadHandler = new Handler();
+ mOnIdleChangedListener = onIdleChangedListener;
+ mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
+ android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ mLoadThread.start();
+ mLoadThreadHandler = new Handler(mLoadThread.getLooper());
+ }
+
+ /** Restarts the loader thread */
+ void start(Context context) {
+ mContext = context;
+ mCancelled = false;
+ if (!mStarted) {
+ // Start loading on the load thread
+ mStarted = true;
+ mLoadThreadHandler.post(this);
+ } else {
+ // Notify the load thread to start loading again
+ synchronized (mLoadThread) {
+ mLoadThread.notifyAll();
+ }
+ }
+ }
+
+ /** Requests the loader thread to stop after the current iteration */
+ void stop() {
+ // Mark as cancelled for the thread to pick up
+ mCancelled = true;
+ // If we are waiting for the load queue for more tasks, then we can just reset the
+ // Context now, since nothing is using it
+ if (mWaitingOnLoadQueue) {
+ mContext = null;
+ }
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ if (mCancelled) {
+ // We have to unset the context here, since the background thread may be using it
+ // when we call stop()
+ mContext = null;
+ // If we are cancelled, then wait until we are started again
+ synchronized(mLoadThread) {
+ try {
+ mLoadThread.wait();
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ }
+ } else {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ // If we've stopped the loader, then fall through to the above logic to wait on
+ // the load thread
+ if (ssp != null) {
+ processLoadQueueItem(ssp);
+ }
+
+ // If there are no other items in the list, then just wait until something is added
+ if (!mCancelled && mLoadQueue.isEmpty()) {
+ synchronized(mLoadQueue) {
+ try {
+ mWaitingOnLoadQueue = true;
+ mMainThreadHandler.post(
+ () -> mOnIdleChangedListener.onIdleChanged(true));
+ mLoadQueue.wait();
+ mMainThreadHandler.post(
+ () -> mOnIdleChangedListener.onIdleChanged(false));
+ mWaitingOnLoadQueue = false;
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This needs to be in a separate method to work around an surprising interpreter behavior:
+ * The register will keep the local reference to cachedThumbnailData even if it falls out of
+ * scope. Putting it into a method fixes this issue.
+ */
+ private void processLoadQueueItem(SystemServicesProxy ssp) {
+ // Load the next item from the queue
+ final Task t = mLoadQueue.nextTask();
+ if (t != null) {
+ Drawable cachedIcon = mIconCache.get(t.key);
+
+ // Load the icon if it is stale or we haven't cached one yet
+ if (cachedIcon == null) {
+ cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
+ t.key.userId, mContext.getResources());
+
+ if (cachedIcon == null) {
+ ActivityInfo info = ssp.getActivityInfo(
+ t.key.getComponent(), t.key.userId);
+ if (info != null) {
+ if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
+ cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
+ }
+ }
+
+ if (cachedIcon == null) {
+ cachedIcon = mDefaultIcon;
+ }
+
+ // At this point, even if we can't load the icon, we will set the
+ // default icon.
+ mIconCache.put(t.key, cachedIcon);
+ }
+
+ if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
+ final ThumbnailData thumbnailData = ssp.getTaskThumbnail(t.key.id,
+ true /* reducedResolution */);
+
+ if (!mCancelled) {
+ // Notify that the task data has changed
+ final Drawable finalIcon = cachedIcon;
+ mMainThreadHandler.post(
+ () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon));
+ }
+ }
+ }
+
+ interface OnIdleChangedListener {
+ void onIdleChanged(boolean idle);
+ }
+}
+
+/**
* Recents task loader
*/
public class RecentsTaskLoader {
+
private static final String TAG = "RecentsTaskLoader";
private static final boolean DEBUG = false;
- /** Levels of svelte in increasing severity/austerity. */
- // No svelting.
- public static final int SVELTE_NONE = 0;
- // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
- // caching thumbnails as you scroll.
- public static final int SVELTE_LIMIT_CACHE = 1;
- // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
- // evict all thumbnails when hidden.
- public static final int SVELTE_DISABLE_CACHE = 2;
- // Disable all thumbnail loading.
- public static final int SVELTE_DISABLE_LOADING = 3;
-
- private final Context mContext;
-
// This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
// for many tasks, which we use to get the activity labels and icons. Unlike the other caches
// below, this is per-package so we can't invalidate the items in the cache based on the last
- // active time. Instead, we rely on the PackageMonitor to keep us informed whenever a
+ // active time. Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
// package in the cache has been updated, so that we may remove it.
private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
private final TaskKeyLruCache<Drawable> mIconCache;
@@ -81,27 +272,31 @@ public class RecentsTaskLoader {
private final int mMaxThumbnailCacheSize;
private final int mMaxIconCacheSize;
private int mNumVisibleTasksLoaded;
- private int mSvelteLevel;
- private int mDefaultTaskBarBackgroundColor;
- private int mDefaultTaskViewBackgroundColor;
- private final BitmapDrawable mDefaultIcon;
+ int mDefaultTaskBarBackgroundColor;
+ int mDefaultTaskViewBackgroundColor;
+ BitmapDrawable mDefaultIcon;
- private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
+ private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
+ new TaskKeyLruCache.EvictionCallback() {
@Override
- public void onEntryEvicted(TaskKey key) {
+ public void onEntryEvicted(Task.TaskKey key) {
if (key != null) {
mActivityInfoCache.remove(key.getComponent());
}
}
};
- public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
- int svelteLevel) {
- mContext = context;
- mMaxThumbnailCacheSize = maxThumbnailCacheSize;
- mMaxIconCacheSize = maxIconCacheSize;
- mSvelteLevel = svelteLevel;
+ public RecentsTaskLoader(Context context) {
+ Resources res = context.getResources();
+ mDefaultTaskBarBackgroundColor =
+ context.getColor(R.color.recents_task_bar_default_background_color);
+ mDefaultTaskViewBackgroundColor =
+ context.getColor(R.color.recents_task_view_default_background_color);
+ mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
+ mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
+ int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
+ mMaxIconCacheSize;
// Create the default assets
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
@@ -110,27 +305,18 @@ public class RecentsTaskLoader {
// Initialize the proxy, cache and loaders
int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
- mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
- Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
+ mHighResThumbnailLoader = new HighResThumbnailLoader(Recents.getSystemServices(),
+ Looper.getMainLooper(), Recents.getConfiguration().isLowRamDevice);
mLoadQueue = new TaskResourceLoadQueue();
- mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction);
+ mIconCache = new TaskKeyLruCache<>(iconCacheSize, mClearActivityInfoOnEviction);
mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
mClearActivityInfoOnEviction);
- mActivityInfoCache = new LruCache<>(numRecentTasks);
+ mActivityInfoCache = new LruCache(numRecentTasks);
mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mDefaultIcon,
mHighResThumbnailLoader::setTaskLoadQueueIdle);
}
- /**
- * Sets the default task bar/view colors if none are provided by the app.
- */
- public void setDefaultColors(int defaultTaskBarBackgroundColor,
- int defaultTaskViewBackgroundColor) {
- mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor;
- mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor;
- }
-
/** Returns the size of the app icon cache. */
public int getIconCacheSize() {
return mMaxIconCacheSize;
@@ -145,22 +331,37 @@ public class RecentsTaskLoader {
return mHighResThumbnailLoader;
}
+ /** Creates a new plan for loading the recent tasks. */
+ public RecentsTaskLoadPlan createLoadPlan(Context context) {
+ RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
+ return plan;
+ }
+
+ /** Preloads raw recents tasks using the specified plan to store the output. */
+ public synchronized void preloadRawTasks(RecentsTaskLoadPlan plan,
+ boolean includeFrontMostExcludedTask) {
+ plan.preloadRawTasks(includeFrontMostExcludedTask);
+ }
+
/** Preloads recents tasks using the specified plan to store the output. */
- public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
+ public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
+ boolean includeFrontMostExcludedTask) {
try {
Trace.beginSection("preloadPlan");
- plan.preloadPlan(this, runningTaskId);
+ plan.preloadPlan(this, runningTaskId, includeFrontMostExcludedTask);
} finally {
Trace.endSection();
}
}
/** Begins loading the heavy task data according to the specified options. */
- public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) {
+ public synchronized void loadTasks(Context context, RecentsTaskLoadPlan plan,
+ RecentsTaskLoadPlan.Options opts) {
if (opts == null) {
throw new RuntimeException("Requires load options");
}
if (opts.onlyLoadForCache && opts.loadThumbnails) {
+
// If we are loading for the cache, we'd like to have the real cache only include the
// visible thumbnails. However, we also don't want to reload already cached thumbnails.
// Thus, we copy over the current entries into a second cache, and clear the real cache,
@@ -243,25 +444,12 @@ public class RecentsTaskLoader {
}
}
- public void onPackageChanged(String packageName) {
- // Remove all the cached activity infos for this package. The other caches do not need to
- // be pruned at this time, as the TaskKey expiration checks will flush them next time their
- // cached contents are requested
- Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
- for (ComponentName cn : activityInfoCache.keySet()) {
- if (cn.getPackageName().equals(packageName)) {
- if (DEBUG) {
- Log.d(TAG, "Removing activity info from cache: " + cn);
- }
- mActivityInfoCache.remove(cn);
- }
- }
- }
-
/**
* Returns the cached task label if the task key is not expired, updating the cache if it is.
*/
- String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) {
+ String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the task description label if it exists
if (td != null && td.getLabel() != null) {
return td.getLabel();
@@ -274,8 +462,7 @@ public class RecentsTaskLoader {
// All short paths failed, load the label from the activity info and cache it
ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo,
- taskKey.userId);
+ label = ssp.getBadgedActivityLabel(activityInfo, taskKey.userId);
mActivityLabelCache.put(taskKey, label);
return label;
}
@@ -288,7 +475,10 @@ public class RecentsTaskLoader {
* Returns the cached task content description if the task key is not expired, updating the
* cache if it is.
*/
- String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) {
+ String getAndUpdateContentDescription(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+ Resources res) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the cached content description if it exists
String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
if (label != null) {
@@ -298,8 +488,7 @@ public class RecentsTaskLoader {
// All short paths failed, load the label from the activity info and cache it
ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(
- activityInfo, taskKey.userId, td);
+ label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, td, res);
if (td == null) {
// Only add to the cache if the task description is null, otherwise, it is possible
// for the task description to change between calls without the last active time
@@ -318,8 +507,10 @@ public class RecentsTaskLoader {
/**
* Returns the cached task icon if the task key is not expired, updating the cache if it is.
*/
- Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
+ Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
Resources res, boolean loadIfNotCached) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the cached activity icon if it exists
Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
if (icon != null) {
@@ -328,8 +519,7 @@ public class RecentsTaskLoader {
if (loadIfNotCached) {
// Return and cache the task description icon if it exists
- icon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(mContext, td,
- taskKey.userId, res);
+ icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
if (icon != null) {
mIconCache.put(taskKey, icon);
return icon;
@@ -338,8 +528,7 @@ public class RecentsTaskLoader {
// Load the icon from the activity info and cache it
ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
- icon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(activityInfo,
- taskKey.userId);
+ icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
if (icon != null) {
mIconCache.put(taskKey, icon);
return icon;
@@ -353,8 +542,10 @@ public class RecentsTaskLoader {
/**
* Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
*/
- synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached,
+ synchronized ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached,
boolean storeInCache) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
if (cached != null) {
return cached;
@@ -367,10 +558,11 @@ public class RecentsTaskLoader {
}
if (loadIfNotCached) {
- if (mSvelteLevel < SVELTE_DISABLE_LOADING) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
// Load the thumbnail from the system
- ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
- taskKey.id, true /* reducedResolution */);
+ ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id,
+ true /* reducedResolution */);
if (thumbnailData.thumbnail != null) {
if (storeInCache) {
mThumbnailCache.put(taskKey, thumbnailData);
@@ -409,11 +601,12 @@ public class RecentsTaskLoader {
* Returns the activity info for the given task key, retrieving one from the system if the
* task key is expired.
*/
- ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
+ ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = taskKey.getComponent();
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
if (activityInfo == null) {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId);
+ activityInfo = ssp.getActivityInfo(cn, taskKey.userId);
if (cn == null || activityInfo == null) {
Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
activityInfo);
@@ -439,6 +632,23 @@ public class RecentsTaskLoader {
mLoadQueue.clearTasks();
}
+ /**** Event Bus Events ****/
+
+ public final void onBusEvent(PackagesChangedEvent event) {
+ // Remove all the cached activity infos for this package. The other caches do not need to
+ // be pruned at this time, as the TaskKey expiration checks will flush them next time their
+ // cached contents are requested
+ Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
+ for (ComponentName cn : activityInfoCache.keySet()) {
+ if (cn.getPackageName().equals(event.packageName)) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing activity info from cache: " + cn);
+ }
+ mActivityInfoCache.remove(cn);
+ }
+ }
+ }
+
public synchronized void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
diff --git a/com/android/systemui/shared/recents/model/Task.java b/com/android/systemui/recents/model/Task.java
index 6bddbe01..abdb5cb8 100644
--- a/com/android/systemui/shared/recents/model/Task.java
+++ b/com/android/systemui/recents/model/Task.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
-import android.app.ActivityManager.TaskDescription;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.ViewDebug;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -40,11 +45,11 @@ public class Task {
/* Task callbacks */
public interface TaskCallbacks {
/* Notifies when a task has been bound */
- void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
+ public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
/* Notifies when a task has been unbound */
- void onTaskDataUnloaded();
+ public void onTaskDataUnloaded();
/* Notifies when a task's windowing mode has changed. */
- void onTaskWindowingModeChanged();
+ public void onTaskWindowingModeChanged();
}
/* The Task Key represents the unique primary key for the task */
@@ -58,15 +63,19 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public final int userId;
@ViewDebug.ExportedProperty(category="recents")
+ public long firstActiveTime;
+ @ViewDebug.ExportedProperty(category="recents")
public long lastActiveTime;
private int mHashCode;
- public TaskKey(int id, int windowingMode, Intent intent, int userId, long lastActiveTime) {
+ public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime,
+ long lastActiveTime) {
this.id = id;
this.windowingMode = windowingMode;
this.baseIntent = intent;
this.userId = userId;
+ this.firstActiveTime = firstActiveTime;
this.lastActiveTime = lastActiveTime;
updateHashCode();
}
@@ -116,6 +125,20 @@ public class Task {
public int temporarySortIndexInStack;
/**
+ * The group will be computed separately from the initialization of the task
+ */
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="group_")
+ public TaskGrouping group;
+ /**
+ * The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
+ * with any task.
+ */
+ @ViewDebug.ExportedProperty(category="recents")
+ public int affiliationTaskId;
+ @ViewDebug.ExportedProperty(category="recents")
+ public int affiliationColor;
+
+ /**
* The icon is the task description icon (if provided), which falls back to the activity icon,
* which can then fall back to the application icon.
*/
@@ -126,6 +149,10 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public String titleDescription;
@ViewDebug.ExportedProperty(category="recents")
+ public String dismissDescription;
+ @ViewDebug.ExportedProperty(category="recents")
+ public String appInfoDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
@ViewDebug.ExportedProperty(category="recents")
public int colorBackground;
@@ -133,9 +160,15 @@ public class Task {
public boolean useLightOnPrimaryColor;
/**
+ * The bounds of the task, used only if it is a freeform task.
+ */
+ @ViewDebug.ExportedProperty(category="recents")
+ public Rect bounds;
+
+ /**
* The task description for this task, only used to reload task icons.
*/
- public TaskDescription taskDescription;
+ public ActivityManager.TaskDescription taskDescription;
/**
* The state isLaunchTarget will be set for the correct task upon launching Recents.
@@ -167,20 +200,28 @@ public class Task {
// Do nothing
}
- public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
- String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
- boolean isStackTask, boolean isSystemApp, boolean isDockable,
- TaskDescription taskDescription, int resizeMode, ComponentName topActivity,
- boolean isLocked) {
+ public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
+ ThumbnailData thumbnail, String title, String titleDescription,
+ String dismissDescription, String appInfoDescription, int colorPrimary,
+ int colorBackground, boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
+ boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription,
+ int resizeMode, ComponentName topActivity, boolean isLocked) {
+ boolean isInAffiliationGroup = (affiliationTaskId != key.id);
+ boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
+ this.affiliationTaskId = affiliationTaskId;
+ this.affiliationColor = affiliationColor;
this.icon = icon;
this.thumbnail = thumbnail;
this.title = title;
this.titleDescription = titleDescription;
- this.colorPrimary = colorPrimary;
+ this.dismissDescription = dismissDescription;
+ this.appInfoDescription = appInfoDescription;
+ this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
this.colorBackground = colorBackground;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
+ this.bounds = bounds;
this.taskDescription = taskDescription;
this.isLaunchTarget = isLaunchTarget;
this.isStackTask = isStackTask;
@@ -196,13 +237,19 @@ public class Task {
*/
public void copyFrom(Task o) {
this.key = o.key;
+ this.group = o.group;
+ this.affiliationTaskId = o.affiliationTaskId;
+ this.affiliationColor = o.affiliationColor;
this.icon = o.icon;
this.thumbnail = o.thumbnail;
this.title = o.title;
this.titleDescription = o.titleDescription;
+ this.dismissDescription = o.dismissDescription;
+ this.appInfoDescription = o.appInfoDescription;
this.colorPrimary = o.colorPrimary;
this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
+ this.bounds = o.bounds;
this.taskDescription = o.taskDescription;
this.isLaunchTarget = o.isLaunchTarget;
this.isStackTask = o.isStackTask;
@@ -229,6 +276,11 @@ public class Task {
mCallbacks.remove(cb);
}
+ /** Set the grouping */
+ public void setGroup(TaskGrouping group) {
+ this.group = group;
+ }
+
/** Updates the task's windowing mode. */
public void setWindowingMode(int windowingMode) {
key.setWindowingMode(windowingMode);
@@ -238,6 +290,14 @@ public class Task {
}
}
+ /**
+ * Returns whether this task is on the freeform task stack.
+ */
+ public boolean isFreeformTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ return ssp.hasFreeformWorkspaceSupport() && key.windowingMode == WINDOWING_MODE_FREEFORM;
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
this.icon = applicationIcon;
@@ -258,6 +318,13 @@ public class Task {
}
/**
+ * Returns whether this task is affiliated with another task.
+ */
+ public boolean isAffiliatedTask() {
+ return key.id != affiliationTaskId;
+ }
+
+ /**
* Returns the top activity component.
*/
public ComponentName getTopComponent() {
@@ -280,12 +347,18 @@ public class Task {
public void dump(String prefix, PrintWriter writer) {
writer.print(prefix); writer.print(key);
+ if (isAffiliatedTask()) {
+ writer.print(" "); writer.print("affTaskId=" + affiliationTaskId);
+ }
if (!isDockable) {
writer.print(" dockable=N");
}
if (isLaunchTarget) {
writer.print(" launchTarget=Y");
}
+ if (isFreeformTask()) {
+ writer.print(" freeform=Y");
+ }
if (isLocked) {
writer.print(" locked=Y");
}
diff --git a/com/android/systemui/recents/model/TaskGrouping.java b/com/android/systemui/recents/model/TaskGrouping.java
new file mode 100644
index 00000000..2109376d
--- /dev/null
+++ b/com/android/systemui/recents/model/TaskGrouping.java
@@ -0,0 +1,106 @@
+package com.android.systemui.recents.model;
+
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+
+/** Represents a grouping of tasks witihin a stack. */
+public class TaskGrouping {
+
+ int affiliation;
+ long latestActiveTimeInGroup;
+
+ Task.TaskKey mFrontMostTaskKey;
+ ArrayList<Task.TaskKey> mTaskKeys = new ArrayList<Task.TaskKey>();
+ ArrayMap<Task.TaskKey, Integer> mTaskKeyIndices = new ArrayMap<>();
+
+ /** Creates a group with a specified affiliation. */
+ public TaskGrouping(int affiliation) {
+ this.affiliation = affiliation;
+ }
+
+ /** Adds a new task to this group. */
+ void addTask(Task t) {
+ mTaskKeys.add(t.key);
+ if (t.key.lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = t.key.lastActiveTime;
+ }
+ t.setGroup(this);
+ updateTaskIndices();
+ }
+
+ /** Removes a task from this group. */
+ void removeTask(Task t) {
+ mTaskKeys.remove(t.key);
+ latestActiveTimeInGroup = 0;
+ int taskCount = mTaskKeys.size();
+ for (int i = 0; i < taskCount; i++) {
+ long lastActiveTime = mTaskKeys.get(i).lastActiveTime;
+ if (lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = lastActiveTime;
+ }
+ }
+ t.setGroup(null);
+ updateTaskIndices();
+ }
+
+ /** Returns the key of the next task in the group. */
+ public Task.TaskKey getNextTaskInGroup(Task t) {
+ int i = indexOf(t);
+ if ((i + 1) < getTaskCount()) {
+ return mTaskKeys.get(i + 1);
+ }
+ return null;
+ }
+
+ /** Returns the key of the previous task in the group. */
+ public Task.TaskKey getPrevTaskInGroup(Task t) {
+ int i = indexOf(t);
+ if ((i - 1) >= 0) {
+ return mTaskKeys.get(i - 1);
+ }
+ return null;
+ }
+
+ /** Gets the front task */
+ public boolean isFrontMostTask(Task t) {
+ return (t.key == mFrontMostTaskKey);
+ }
+
+ /** Finds the index of a given task in a group. */
+ public int indexOf(Task t) {
+ return mTaskKeyIndices.get(t.key);
+ }
+
+ /** Returns whether a task is in this grouping. */
+ public boolean containsTask(Task t) {
+ return mTaskKeyIndices.containsKey(t.key);
+ }
+
+ /** Returns whether one task is above another in the group. If they are not in the same group,
+ * this returns false. */
+ public boolean isTaskAboveTask(Task t, Task below) {
+ return mTaskKeyIndices.containsKey(t.key) && mTaskKeyIndices.containsKey(below.key) &&
+ mTaskKeyIndices.get(t.key) > mTaskKeyIndices.get(below.key);
+ }
+
+ /** Returns the number of tasks in this group. */
+ public int getTaskCount() { return mTaskKeys.size(); }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateTaskIndices() {
+ if (mTaskKeys.isEmpty()) {
+ mFrontMostTaskKey = null;
+ mTaskKeyIndices.clear();
+ return;
+ }
+
+ int taskCount = mTaskKeys.size();
+ mFrontMostTaskKey = mTaskKeys.get(mTaskKeys.size() - 1);
+ mTaskKeyIndices.clear();
+ for (int i = 0; i < taskCount; i++) {
+ Task.TaskKey k = mTaskKeys.get(i);
+ mTaskKeyIndices.put(k, i);
+ }
+ }
+}
diff --git a/com/android/systemui/shared/recents/model/TaskKeyCache.java b/com/android/systemui/recents/model/TaskKeyCache.java
index 4bf3500a..247a6542 100644
--- a/com/android/systemui/shared/recents/model/TaskKeyCache.java
+++ b/com/android/systemui/recents/model/TaskKeyCache.java
@@ -14,12 +14,12 @@
* limitations under the License
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
import android.util.Log;
import android.util.SparseArray;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.recents.model.Task.TaskKey;
/**
* Base class for both strong and LRU task key cache.
@@ -34,7 +34,7 @@ public abstract class TaskKeyCache<V> {
* Gets a specific entry in the cache with the specified key, regardless of whether the cached
* value is valid or not.
*/
- final V get(TaskKey key) {
+ final V get(Task.TaskKey key) {
return getCacheEntry(key.id);
}
@@ -42,8 +42,8 @@ public abstract class TaskKeyCache<V> {
* Returns the value only if the key is valid (has not been updated since the last time it was
* in the cache)
*/
- final V getAndInvalidateIfModified(TaskKey key) {
- TaskKey lastKey = mKeys.get(key.id);
+ final V getAndInvalidateIfModified(Task.TaskKey key) {
+ Task.TaskKey lastKey = mKeys.get(key.id);
if (lastKey != null) {
if ((lastKey.windowingMode != key.windowingMode) ||
(lastKey.lastActiveTime != key.lastActiveTime)) {
@@ -59,7 +59,7 @@ public abstract class TaskKeyCache<V> {
}
/** Puts an entry in the cache for a specific key. */
- final void put(TaskKey key, V value) {
+ final void put(Task.TaskKey key, V value) {
if (key == null || value == null) {
Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
return;
@@ -70,7 +70,7 @@ public abstract class TaskKeyCache<V> {
/** Removes a cache entry for a specific key. */
- final void remove(TaskKey key) {
+ final void remove(Task.TaskKey key) {
// Remove the key after the cache value because we need it to make the callback
removeCacheEntry(key.id);
mKeys.remove(key.id);
diff --git a/com/android/systemui/shared/recents/model/TaskKeyLruCache.java b/com/android/systemui/recents/model/TaskKeyLruCache.java
index 0ba2c3bf..778df6be 100644
--- a/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
+++ b/com/android/systemui/recents/model/TaskKeyLruCache.java
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
import android.util.LruCache;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
import java.io.PrintWriter;
/**
- * A mapping of {@link TaskKey} to value, with additional LRU functionality where the least
+ * A mapping of {@link Task.TaskKey} to value, with additional LRU functionality where the least
* recently referenced key/values will be evicted as more values than the given cache size are
* inserted.
*
@@ -33,7 +31,7 @@ import java.io.PrintWriter;
public class TaskKeyLruCache<V> extends TaskKeyCache<V> {
public interface EvictionCallback {
- void onEntryEvicted(TaskKey key);
+ public void onEntryEvicted(Task.TaskKey key);
}
private final LruCache<Integer, V> mCache;
diff --git a/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java b/com/android/systemui/recents/model/TaskKeyStrongCache.java
index 4408eced..c84df8a1 100644
--- a/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java
+++ b/com/android/systemui/recents/model/TaskKeyStrongCache.java
@@ -14,11 +14,13 @@
* limitations under the License
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.recents.model;
import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.recents.model.Task.TaskKey;
import java.io.PrintWriter;
diff --git a/com/android/systemui/recents/model/TaskStack.java b/com/android/systemui/recents/model/TaskStack.java
new file mode 100644
index 00000000..fdae917c
--- /dev/null
+++ b/com/android/systemui/recents/model/TaskStack.java
@@ -0,0 +1,1140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.model;
+
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.IntDef;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntProperty;
+import android.util.SparseArray;
+import android.view.animation.Interpolator;
+
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.misc.NamedCounter;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.views.AnimationProps;
+import com.android.systemui.recents.views.DropTarget;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Random;
+
+
+/**
+ * An interface for a task filter to query whether a particular task should show in a stack.
+ */
+interface TaskFilter {
+ /** Returns whether the filter accepts the specified task */
+ public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
+}
+
+/**
+ * A list of filtered tasks.
+ */
+class FilteredTaskList {
+
+ ArrayList<Task> mTasks = new ArrayList<>();
+ ArrayList<Task> mFilteredTasks = new ArrayList<>();
+ ArrayMap<Task.TaskKey, Integer> mTaskIndices = new ArrayMap<>();
+ TaskFilter mFilter;
+
+ /** Sets the task filter, saving the current touch state */
+ boolean setFilter(TaskFilter filter) {
+ ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
+ mFilter = filter;
+ updateFilteredTasks();
+ if (!prevFilteredTasks.equals(mFilteredTasks)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** Removes the task filter and returns the previous touch state */
+ void removeFilter() {
+ mFilter = null;
+ updateFilteredTasks();
+ }
+
+ /** Adds a new task to the task list */
+ void add(Task t) {
+ mTasks.add(t);
+ updateFilteredTasks();
+ }
+
+ /**
+ * Moves the given task.
+ */
+ public void setTaskWindowingMode(Task task, int insertIndex, int windowingMode) {
+ int taskIndex = indexOf(task);
+ if (taskIndex != insertIndex) {
+ mTasks.remove(taskIndex);
+ if (taskIndex < insertIndex) {
+ insertIndex--;
+ }
+ mTasks.add(insertIndex, task);
+ }
+
+ // Update the stack id now, after we've moved the task, and before we update the
+ // filtered tasks
+ task.setWindowingMode(windowingMode);
+ updateFilteredTasks();
+ }
+
+ /** Sets the list of tasks */
+ void set(List<Task> tasks) {
+ mTasks.clear();
+ mTasks.addAll(tasks);
+ updateFilteredTasks();
+ }
+
+ /** Removes a task from the base list only if it is in the filtered list */
+ boolean remove(Task t) {
+ if (mFilteredTasks.contains(t)) {
+ boolean removed = mTasks.remove(t);
+ updateFilteredTasks();
+ return removed;
+ }
+ return false;
+ }
+
+ /** Returns the index of this task in the list of filtered tasks */
+ int indexOf(Task t) {
+ if (t != null && mTaskIndices.containsKey(t.key)) {
+ return mTaskIndices.get(t.key);
+ }
+ return -1;
+ }
+
+ /** Returns the size of the list of filtered tasks */
+ int size() {
+ return mFilteredTasks.size();
+ }
+
+ /** Returns whether the filtered list contains this task */
+ boolean contains(Task t) {
+ return mTaskIndices.containsKey(t.key);
+ }
+
+ /** Updates the list of filtered tasks whenever the base task list changes */
+ private void updateFilteredTasks() {
+ mFilteredTasks.clear();
+ if (mFilter != null) {
+ // Create a sparse array from task id to Task
+ SparseArray<Task> taskIdMap = new SparseArray<>();
+ int taskCount = mTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = mTasks.get(i);
+ taskIdMap.put(t.key.id, t);
+ }
+
+ for (int i = 0; i < taskCount; i++) {
+ Task t = mTasks.get(i);
+ if (mFilter.acceptTask(taskIdMap, t, i)) {
+ mFilteredTasks.add(t);
+ }
+ }
+ } else {
+ mFilteredTasks.addAll(mTasks);
+ }
+ updateFilteredTaskIndices();
+ }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateFilteredTaskIndices() {
+ int taskCount = mFilteredTasks.size();
+ mTaskIndices.clear();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = mFilteredTasks.get(i);
+ mTaskIndices.put(t.key, i);
+ }
+ }
+
+ /** Returns whether this task list is filtered */
+ boolean hasFilter() {
+ return (mFilter != null);
+ }
+
+ /** Returns the list of filtered tasks */
+ ArrayList<Task> getTasks() {
+ return mFilteredTasks;
+ }
+}
+
+/**
+ * The task stack contains a list of multiple tasks.
+ */
+public class TaskStack {
+
+ private static final String TAG = "TaskStack";
+
+ /** Task stack callbacks */
+ public interface TaskStackCallbacks {
+ /**
+ * Notifies when a new task has been added to the stack.
+ */
+ void onStackTaskAdded(TaskStack stack, Task newTask);
+
+ /**
+ * Notifies when a task has been removed from the stack.
+ */
+ void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
+ AnimationProps animation, boolean fromDockGesture,
+ boolean dismissRecentsIfAllRemoved);
+
+ /**
+ * Notifies when all tasks have been removed from the stack.
+ */
+ void onStackTasksRemoved(TaskStack stack);
+
+ /**
+ * Notifies when tasks in the stack have been updated.
+ */
+ void onStackTasksUpdated(TaskStack stack);
+ }
+
+ /**
+ * The various possible dock states when dragging and dropping a task.
+ */
+ public static class DockState implements DropTarget {
+
+ public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+ public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
+ // The rotation to apply to the hint text
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HORIZONTAL, VERTICAL})
+ public @interface TextOrientation {}
+ private static final int HORIZONTAL = 0;
+ private static final int VERTICAL = 1;
+
+ private static final int DOCK_AREA_ALPHA = 80;
+ public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
+ null, null, null);
+ public static final DockState LEFT = new DockState(DOCKED_LEFT,
+ DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
+ new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
+ new RectF(0, 0, 0.5f, 1));
+ public static final DockState TOP = new DockState(DOCKED_TOP,
+ DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+ new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
+ new RectF(0, 0, 1, 0.5f));
+ public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
+ DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
+ new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
+ new RectF(0.5f, 0, 1, 1));
+ public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
+ DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+ new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
+ new RectF(0, 0.5f, 1, 1));
+
+ @Override
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
+ if (isCurrentTarget) {
+ getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
+ return mTmpRect.contains(x, y);
+ } else {
+ getMappedRect(touchArea, width, height, mTmpRect);
+ updateBoundsWithSystemInsets(mTmpRect, insets);
+ return mTmpRect.contains(x, y);
+ }
+ }
+
+ // Represents the view state of this dock state
+ public static class ViewState {
+ private static final IntProperty<ViewState> HINT_ALPHA =
+ new IntProperty<ViewState>("drawableAlpha") {
+ @Override
+ public void setValue(ViewState object, int alpha) {
+ object.mHintTextAlpha = alpha;
+ object.dockAreaOverlay.invalidateSelf();
+ }
+
+ @Override
+ public Integer get(ViewState object) {
+ return object.mHintTextAlpha;
+ }
+ };
+
+ public final int dockAreaAlpha;
+ public final ColorDrawable dockAreaOverlay;
+ public final int hintTextAlpha;
+ public final int hintTextOrientation;
+
+ private final int mHintTextResId;
+ private String mHintText;
+ private Paint mHintTextPaint;
+ private Point mHintTextBounds = new Point();
+ private int mHintTextAlpha = 255;
+ private AnimatorSet mDockAreaOverlayAnimator;
+ private Rect mTmpRect = new Rect();
+
+ private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
+ int hintTextResId) {
+ dockAreaAlpha = areaAlpha;
+ dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
+ ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
+ dockAreaOverlay.setAlpha(0);
+ hintTextAlpha = hintAlpha;
+ hintTextOrientation = hintOrientation;
+ mHintTextResId = hintTextResId;
+ mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mHintTextPaint.setColor(Color.WHITE);
+ }
+
+ /**
+ * Updates the view state with the given context.
+ */
+ public void update(Context context) {
+ Resources res = context.getResources();
+ mHintText = context.getString(mHintTextResId);
+ mHintTextPaint.setTextSize(res.getDimensionPixelSize(
+ R.dimen.recents_drag_hint_text_size));
+ mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
+ mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
+ }
+
+ /**
+ * Draws the current view state.
+ */
+ public void draw(Canvas canvas) {
+ // Draw the overlay background
+ if (dockAreaOverlay.getAlpha() > 0) {
+ dockAreaOverlay.draw(canvas);
+ }
+
+ // Draw the hint text
+ if (mHintTextAlpha > 0) {
+ Rect bounds = dockAreaOverlay.getBounds();
+ int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
+ int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
+ mHintTextPaint.setAlpha(mHintTextAlpha);
+ if (hintTextOrientation == VERTICAL) {
+ canvas.save();
+ canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
+ }
+ canvas.drawText(mHintText, x, y, mHintTextPaint);
+ if (hintTextOrientation == VERTICAL) {
+ canvas.restore();
+ }
+ }
+ }
+
+ /**
+ * Creates a new bounds and alpha animation.
+ */
+ public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
+ Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
+ if (mDockAreaOverlayAnimator != null) {
+ mDockAreaOverlayAnimator.cancel();
+ }
+
+ ObjectAnimator anim;
+ ArrayList<Animator> animators = new ArrayList<>();
+ if (dockAreaOverlay.getAlpha() != areaAlpha) {
+ if (animateAlpha) {
+ anim = ObjectAnimator.ofInt(dockAreaOverlay,
+ Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
+ anim.setDuration(duration);
+ anim.setInterpolator(interpolator);
+ animators.add(anim);
+ } else {
+ dockAreaOverlay.setAlpha(areaAlpha);
+ }
+ }
+ if (mHintTextAlpha != hintAlpha) {
+ if (animateAlpha) {
+ anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
+ hintAlpha);
+ anim.setDuration(150);
+ anim.setInterpolator(hintAlpha > mHintTextAlpha
+ ? Interpolators.ALPHA_IN
+ : Interpolators.ALPHA_OUT);
+ animators.add(anim);
+ } else {
+ mHintTextAlpha = hintAlpha;
+ dockAreaOverlay.invalidateSelf();
+ }
+ }
+ if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
+ if (animateBounds) {
+ PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
+ Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
+ new Rect(dockAreaOverlay.getBounds()), bounds);
+ anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
+ anim.setDuration(duration);
+ anim.setInterpolator(interpolator);
+ animators.add(anim);
+ } else {
+ dockAreaOverlay.setBounds(bounds);
+ }
+ }
+ if (!animators.isEmpty()) {
+ mDockAreaOverlayAnimator = new AnimatorSet();
+ mDockAreaOverlayAnimator.playTogether(animators);
+ mDockAreaOverlayAnimator.start();
+ }
+ }
+ }
+
+ public final int dockSide;
+ public final int createMode;
+ public final ViewState viewState;
+ private final RectF touchArea;
+ private final RectF dockArea;
+ private final RectF expandedTouchDockArea;
+ private static final Rect mTmpRect = new Rect();
+
+ /**
+ * @param createMode used to pass to ActivityManager to dock the task
+ * @param touchArea the area in which touch will initiate this dock state
+ * @param dockArea the visible dock area
+ * @param expandedTouchDockArea the area in which touch will continue to dock after entering
+ * the initial touch area. This is also the new dock area to
+ * draw.
+ */
+ DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
+ @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
+ RectF expandedTouchDockArea) {
+ this.dockSide = dockSide;
+ this.createMode = createMode;
+ this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
+ R.string.recents_drag_hint_message);
+ this.dockArea = dockArea;
+ this.touchArea = touchArea;
+ this.expandedTouchDockArea = expandedTouchDockArea;
+ }
+
+ /**
+ * Updates the dock state with the given context.
+ */
+ public void update(Context context) {
+ viewState.update(context);
+ }
+
+ /**
+ * Returns the docked task bounds with the given {@param width} and {@param height}.
+ */
+ public Rect getPreDockedBounds(int width, int height, Rect insets) {
+ getMappedRect(dockArea, width, height, mTmpRect);
+ return updateBoundsWithSystemInsets(mTmpRect, insets);
+ }
+
+ /**
+ * Returns the expanded docked task bounds with the given {@param width} and
+ * {@param height}.
+ */
+ public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
+ Resources res) {
+ // Calculate the docked task bounds
+ boolean isHorizontalDivision =
+ res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ insets, width, height, dividerSize);
+ Rect newWindowBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
+ width, height, dividerSize);
+ return newWindowBounds;
+ }
+
+ /**
+ * Returns the task stack bounds with the given {@param width} and
+ * {@param height}.
+ */
+ public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
+ int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
+ Resources res, Rect windowRectOut) {
+ // Calculate the inverse docked task bounds
+ boolean isHorizontalDivision =
+ res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ insets, width, height, dividerSize);
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
+ dividerSize);
+
+ // Calculate the task stack bounds from the new window bounds
+ Rect taskStackBounds = new Rect();
+ // If the task stack bounds is specifically under the dock area, then ignore the top
+ // inset
+ int top = dockArea.bottom < 1f
+ ? 0
+ : insets.top;
+ // For now, ignore the left insets since we always dock on the left and show Recents
+ // on the right
+ layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
+ taskStackBounds);
+ return taskStackBounds;
+ }
+
+ /**
+ * Returns the expanded bounds in certain dock sides such that the bounds account for the
+ * system insets (namely the vertical nav bar). This call modifies and returns the given
+ * {@param bounds}.
+ */
+ private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
+ if (dockSide == DOCKED_LEFT) {
+ bounds.right += insets.left;
+ } else if (dockSide == DOCKED_RIGHT) {
+ bounds.left -= insets.right;
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the mapped rect to the given dimensions.
+ */
+ private void getMappedRect(RectF bounds, int width, int height, Rect out) {
+ out.set((int) (bounds.left * width), (int) (bounds.top * height),
+ (int) (bounds.right * width), (int) (bounds.bottom * height));
+ }
+ }
+
+ // A comparator that sorts tasks by their freeform state
+ private Comparator<Task> FREEFORM_COMPARATOR = new Comparator<Task>() {
+ @Override
+ public int compare(Task o1, Task o2) {
+ if (o1.isFreeformTask() && !o2.isFreeformTask()) {
+ return 1;
+ } else if (o2.isFreeformTask() && !o1.isFreeformTask()) {
+ return -1;
+ }
+ return Long.compare(o1.temporarySortIndexInStack, o2.temporarySortIndexInStack);
+ }
+ };
+
+
+ // The task offset to apply to a task id as a group affiliation
+ static final int IndividualTaskIdOffset = 1 << 16;
+
+ ArrayList<Task> mRawTaskList = new ArrayList<>();
+ FilteredTaskList mStackTaskList = new FilteredTaskList();
+ TaskStackCallbacks mCb;
+
+ ArrayList<TaskGrouping> mGroups = new ArrayList<>();
+ ArrayMap<Integer, TaskGrouping> mAffinitiesGroups = new ArrayMap<>();
+
+ public TaskStack() {
+ // Ensure that we only show non-docked tasks
+ mStackTaskList.setFilter(new TaskFilter() {
+ @Override
+ public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ if (t.isAffiliatedTask()) {
+ // If this task is affiliated with another parent in the stack, then the
+ // historical state of this task depends on the state of the parent task
+ Task parentTask = taskIdMap.get(t.affiliationTaskId);
+ if (parentTask != null) {
+ t = parentTask;
+ }
+ }
+ }
+ return t.isStackTask;
+ }
+ });
+ }
+
+ /** Sets the callbacks for this task stack. */
+ public void setCallbacks(TaskStackCallbacks cb) {
+ mCb = cb;
+ }
+
+ /** Sets the windowing mode for a given task. */
+ public void setTaskWindowingMode(Task task, int windowingMode) {
+ // Find the index to insert into
+ ArrayList<Task> taskList = mStackTaskList.getTasks();
+ int taskCount = taskList.size();
+ if (!task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FREEFORM)) {
+ // Insert freeform tasks at the front
+ mStackTaskList.setTaskWindowingMode(task, taskCount, windowingMode);
+ } else if (task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FULLSCREEN)) {
+ // Insert after the first stacked task
+ int insertIndex = 0;
+ for (int i = taskCount - 1; i >= 0; i--) {
+ if (!taskList.get(i).isFreeformTask()) {
+ insertIndex = i + 1;
+ break;
+ }
+ }
+ mStackTaskList.setTaskWindowingMode(task, insertIndex, windowingMode);
+ }
+ }
+
+ /** Does the actual work associated with removing the task. */
+ void removeTaskImpl(FilteredTaskList taskList, Task t) {
+ // Remove the task from the list
+ taskList.remove(t);
+ // Remove it from the group as well, and if it is empty, remove the group
+ TaskGrouping group = t.group;
+ if (group != null) {
+ group.removeTask(t);
+ if (group.getTaskCount() == 0) {
+ removeGroup(group);
+ }
+ }
+ }
+
+ /**
+ * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
+ * how they should update themselves.
+ */
+ public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
+ removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
+ }
+
+ /**
+ * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
+ * how they should update themselves.
+ */
+ public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
+ boolean dismissRecentsIfAllRemoved) {
+ if (mStackTaskList.contains(t)) {
+ removeTaskImpl(mStackTaskList, t);
+ Task newFrontMostTask = getStackFrontMostTask(false /* includeFreeform */);
+ if (mCb != null) {
+ // Notify that a task has been removed
+ mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
+ fromDockGesture, dismissRecentsIfAllRemoved);
+ }
+ }
+ mRawTaskList.remove(t);
+ }
+
+ /**
+ * Removes all tasks from the stack.
+ */
+ public void removeAllTasks(boolean notifyStackChanges) {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ removeTaskImpl(mStackTaskList, t);
+ mRawTaskList.remove(t);
+ }
+ if (mCb != null && notifyStackChanges) {
+ // Notify that all tasks have been removed
+ mCb.onStackTasksRemoved(this);
+ }
+ }
+
+
+ /**
+ * @see #setTasks(Context, List, boolean, boolean)
+ */
+ public void setTasks(Context context, TaskStack stack, boolean notifyStackChanges) {
+ setTasks(context, stack.mRawTaskList, notifyStackChanges);
+ }
+
+ /**
+ * Sets a few tasks in one go, without calling any callbacks.
+ *
+ * @param tasks the new set of tasks to replace the current set.
+ * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
+ */
+ public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
+ // Compute a has set for each of the tasks
+ ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
+ ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
+ ArrayList<Task> addedTasks = new ArrayList<>();
+ ArrayList<Task> removedTasks = new ArrayList<>();
+ ArrayList<Task> allTasks = new ArrayList<>();
+
+ // Disable notifications if there are no callbacks
+ if (mCb == null) {
+ notifyStackChanges = false;
+ }
+
+ // Remove any tasks that no longer exist
+ int taskCount = mRawTaskList.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task task = mRawTaskList.get(i);
+ if (!newTasksMap.containsKey(task.key)) {
+ if (notifyStackChanges) {
+ removedTasks.add(task);
+ }
+ }
+ task.setGroup(null);
+ }
+
+ // Add any new tasks
+ taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task newTask = tasks.get(i);
+ Task currentTask = currentTasksMap.get(newTask.key);
+ if (currentTask == null && notifyStackChanges) {
+ addedTasks.add(newTask);
+ } else if (currentTask != null) {
+ // The current task has bound callbacks, so just copy the data from the new task
+ // state and add it back into the list
+ currentTask.copyFrom(newTask);
+ newTask = currentTask;
+ }
+ allTasks.add(newTask);
+ }
+
+ // Sort all the tasks to ensure they are ordered correctly
+ for (int i = allTasks.size() - 1; i >= 0; i--) {
+ allTasks.get(i).temporarySortIndexInStack = i;
+ }
+ Collections.sort(allTasks, FREEFORM_COMPARATOR);
+
+ mStackTaskList.set(allTasks);
+ mRawTaskList = allTasks;
+
+ // Update the affiliated groupings
+ createAffiliatedGroupings(context);
+
+ // Only callback for the removed tasks after the stack has updated
+ int removedTaskCount = removedTasks.size();
+ Task newFrontMostTask = getStackFrontMostTask(false);
+ for (int i = 0; i < removedTaskCount; i++) {
+ mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
+ AnimationProps.IMMEDIATE, false /* fromDockGesture */,
+ true /* dismissRecentsIfAllRemoved */);
+ }
+
+ // Only callback for the newly added tasks after this stack has been updated
+ int addedTaskCount = addedTasks.size();
+ for (int i = 0; i < addedTaskCount; i++) {
+ mCb.onStackTaskAdded(this, addedTasks.get(i));
+ }
+
+ // Notify that the task stack has been updated
+ if (notifyStackChanges) {
+ mCb.onStackTasksUpdated(this);
+ }
+ }
+
+ /**
+ * Gets the front-most task in the stack.
+ */
+ public Task getStackFrontMostTask(boolean includeFreeformTasks) {
+ ArrayList<Task> stackTasks = mStackTaskList.getTasks();
+ if (stackTasks.isEmpty()) {
+ return null;
+ }
+ for (int i = stackTasks.size() - 1; i >= 0; i--) {
+ Task task = stackTasks.get(i);
+ if (!task.isFreeformTask() || includeFreeformTasks) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ /** Gets the task keys */
+ public ArrayList<Task.TaskKey> getTaskKeys() {
+ ArrayList<Task.TaskKey> taskKeys = new ArrayList<>();
+ ArrayList<Task> tasks = computeAllTasksList();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ taskKeys.add(task.key);
+ }
+ return taskKeys;
+ }
+
+ /**
+ * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
+ */
+ public ArrayList<Task> getStackTasks() {
+ return mStackTaskList.getTasks();
+ }
+
+ /**
+ * Returns the set of "freeform" tasks in the stack.
+ */
+ public ArrayList<Task> getFreeformTasks() {
+ ArrayList<Task> freeformTasks = new ArrayList<>();
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ freeformTasks.add(task);
+ }
+ }
+ return freeformTasks;
+ }
+
+ /**
+ * Computes a set of all the active and historical tasks.
+ */
+ public ArrayList<Task> computeAllTasksList() {
+ ArrayList<Task> tasks = new ArrayList<>();
+ tasks.addAll(mStackTaskList.getTasks());
+ return tasks;
+ }
+
+ /**
+ * Returns the number of stack and freeform tasks.
+ */
+ public int getTaskCount() {
+ return mStackTaskList.size();
+ }
+
+ /**
+ * Returns the number of stack tasks.
+ */
+ public int getStackTaskCount() {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int stackCount = 0;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (!task.isFreeformTask()) {
+ stackCount++;
+ }
+ }
+ return stackCount;
+ }
+
+ /**
+ * Returns the number of freeform tasks.
+ */
+ public int getFreeformTaskCount() {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int freeformCount = 0;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ freeformCount++;
+ }
+ }
+ return freeformCount;
+ }
+
+ /**
+ * Returns the task in stack tasks which is the launch target.
+ */
+ public Task getLaunchTarget() {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.isLaunchTarget) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the next launch target should actually be the PiP task.
+ */
+ public boolean isNextLaunchTargetPip(long lastPipTime) {
+ Task launchTarget = getLaunchTarget();
+ Task nextLaunchTarget = getNextLaunchTargetRaw();
+ if (nextLaunchTarget != null && lastPipTime > 0) {
+ // If the PiP time is more recent than the next launch target, then launch the PiP task
+ return lastPipTime > nextLaunchTarget.key.lastActiveTime;
+ } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
+ // Otherwise, if there is no next launch target, but there is a PiP, then launch
+ // the PiP task
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the task in stack tasks which should be launched next if Recents are toggled
+ * again, or null if there is no task to be launched. Callers should check
+ * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
+ * stack.
+ */
+ public Task getNextLaunchTarget() {
+ Task nextLaunchTarget = getNextLaunchTargetRaw();
+ if (nextLaunchTarget != null) {
+ return nextLaunchTarget;
+ }
+ return getStackTasks().get(getTaskCount() - 1);
+ }
+
+ private Task getNextLaunchTargetRaw() {
+ int taskCount = getTaskCount();
+ if (taskCount == 0) {
+ return null;
+ }
+ int launchTaskIndex = indexOfStackTask(getLaunchTarget());
+ if (launchTaskIndex != -1 && launchTaskIndex > 0) {
+ return getStackTasks().get(launchTaskIndex - 1);
+ }
+ return null;
+ }
+
+ /** Returns the index of this task in this current task stack */
+ public int indexOfStackTask(Task t) {
+ return mStackTaskList.indexOf(t);
+ }
+
+ /** Finds the task with the specified task id. */
+ public Task findTaskWithId(int taskId) {
+ ArrayList<Task> tasks = computeAllTasksList();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.key.id == taskId) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ /******** Grouping ********/
+
+ /** Adds a group to the set */
+ public void addGroup(TaskGrouping group) {
+ mGroups.add(group);
+ mAffinitiesGroups.put(group.affiliation, group);
+ }
+
+ public void removeGroup(TaskGrouping group) {
+ mGroups.remove(group);
+ mAffinitiesGroups.remove(group.affiliation);
+ }
+
+ /** Returns the group with the specified affiliation. */
+ public TaskGrouping getGroupWithAffiliation(int affiliation) {
+ return mAffinitiesGroups.get(affiliation);
+ }
+
+ /**
+ * Temporary: This method will simulate affiliation groups
+ */
+ void createAffiliatedGroupings(Context context) {
+ mGroups.clear();
+ mAffinitiesGroups.clear();
+
+ if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
+ ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
+ // Sort all tasks by increasing firstActiveTime of the task
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ Collections.sort(tasks, new Comparator<Task>() {
+ @Override
+ public int compare(Task task, Task task2) {
+ return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime);
+ }
+ });
+ // Create groups when sequential packages are the same
+ NamedCounter counter = new NamedCounter("task-group", "");
+ int taskCount = tasks.size();
+ String prevPackage = "";
+ int prevAffiliation = -1;
+ Random r = new Random();
+ int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ String packageName = t.key.getComponent().getPackageName();
+ packageName = "pkg";
+ TaskGrouping group;
+ if (packageName.equals(prevPackage) && groupCountDown > 0) {
+ group = getGroupWithAffiliation(prevAffiliation);
+ groupCountDown--;
+ } else {
+ int affiliation = IndividualTaskIdOffset + t.key.id;
+ group = new TaskGrouping(affiliation);
+ addGroup(group);
+ prevAffiliation = affiliation;
+ prevPackage = packageName;
+ groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
+ }
+ group.addTask(t);
+ taskMap.put(t.key, t);
+ }
+ // Sort groups by increasing latestActiveTime of the group
+ Collections.sort(mGroups, new Comparator<TaskGrouping>() {
+ @Override
+ public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
+ return Long.compare(taskGrouping.latestActiveTimeInGroup,
+ taskGrouping2.latestActiveTimeInGroup);
+ }
+ });
+ // Sort group tasks by increasing firstActiveTime of the task, and also build a new list
+ // of tasks
+ int taskIndex = 0;
+ int groupCount = mGroups.size();
+ for (int i = 0; i < groupCount; i++) {
+ TaskGrouping group = mGroups.get(i);
+ Collections.sort(group.mTaskKeys, new Comparator<Task.TaskKey>() {
+ @Override
+ public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
+ return Long.compare(taskKey.firstActiveTime, taskKey2.firstActiveTime);
+ }
+ });
+ ArrayList<Task.TaskKey> groupTasks = group.mTaskKeys;
+ int groupTaskCount = groupTasks.size();
+ for (int j = 0; j < groupTaskCount; j++) {
+ tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
+ taskIndex++;
+ }
+ }
+ mStackTaskList.set(tasks);
+ } else {
+ // Create the task groups
+ ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ TaskGrouping group;
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
+ IndividualTaskIdOffset + t.key.id;
+ if (mAffinitiesGroups.containsKey(affiliation)) {
+ group = getGroupWithAffiliation(affiliation);
+ } else {
+ group = new TaskGrouping(affiliation);
+ addGroup(group);
+ }
+ } else {
+ group = new TaskGrouping(t.key.id);
+ addGroup(group);
+ }
+ group.addTask(t);
+ tasksMap.put(t.key, t);
+ }
+ // Update the task colors for each of the groups
+ float minAlpha = context.getResources().getFloat(
+ R.dimen.recents_task_affiliation_color_min_alpha_percentage);
+ int taskGroupCount = mGroups.size();
+ for (int i = 0; i < taskGroupCount; i++) {
+ TaskGrouping group = mGroups.get(i);
+ taskCount = group.getTaskCount();
+ // Ignore the groups that only have one task
+ if (taskCount <= 1) continue;
+ // Calculate the group color distribution
+ int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
+ float alphaStep = (1f - minAlpha) / taskCount;
+ float alpha = 1f;
+ for (int j = 0; j < taskCount; j++) {
+ Task t = tasksMap.get(group.mTaskKeys.get(j));
+ t.colorPrimary = Utilities.getColorWithOverlay(affiliationColor, Color.WHITE,
+ alpha);
+ alpha -= alphaStep;
+ }
+ }
+ }
+ }
+
+ /**
+ * Computes the components of tasks in this stack that have been removed as a result of a change
+ * in the specified package.
+ */
+ public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
+ // Identify all the tasks that should be removed as a result of the package being removed.
+ // Using a set to ensure that we callback once per unique component.
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ArraySet<ComponentName> existingComponents = new ArraySet<>();
+ ArraySet<ComponentName> removedComponents = new ArraySet<>();
+ ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
+ int taskKeyCount = taskKeys.size();
+ for (int i = 0; i < taskKeyCount; i++) {
+ Task.TaskKey t = taskKeys.get(i);
+
+ // Skip if this doesn't apply to the current user
+ if (t.userId != userId) continue;
+
+ ComponentName cn = t.getComponent();
+ if (cn.getPackageName().equals(packageName)) {
+ if (existingComponents.contains(cn)) {
+ // If we know that the component still exists in the package, then skip
+ continue;
+ }
+ if (ssp.getActivityInfo(cn, userId) != null) {
+ existingComponents.add(cn);
+ } else {
+ removedComponents.add(cn);
+ }
+ }
+ }
+ return removedComponents;
+ }
+
+ @Override
+ public String toString() {
+ String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ str += " " + tasks.get(i).toString() + "\n";
+ }
+ return str;
+ }
+
+ /**
+ * Given a list of tasks, returns a map of each task's key to the task.
+ */
+ private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
+ ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size());
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ map.put(task.key, task);
+ }
+ return map;
+ }
+
+ public void dump(String prefix, PrintWriter writer) {
+ String innerPrefix = prefix + " ";
+
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
+ writer.println();
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ tasks.get(i).dump(innerPrefix, writer);
+ }
+ }
+}
diff --git a/com/android/systemui/shared/recents/model/ThumbnailData.java b/com/android/systemui/recents/model/ThumbnailData.java
index dd1763bb..33ff1b63 100644
--- a/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/com/android/systemui/recents/model/ThumbnailData.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
-
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+package com.android.systemui.recents.model;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
@@ -27,25 +25,20 @@ import android.graphics.Rect;
*/
public class ThumbnailData {
- public final Bitmap thumbnail;
+ // TODO: Make these final once the non-snapshot path is removed.
+ public Bitmap thumbnail;
public int orientation;
- public Rect insets;
+ public final Rect insets = new Rect();
public boolean reducedResolution;
public float scale;
- public ThumbnailData() {
- thumbnail = null;
- orientation = ORIENTATION_UNDEFINED;
- insets = new Rect();
- reducedResolution = false;
- scale = 1f;
- }
-
- public ThumbnailData(TaskSnapshot snapshot) {
- thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
- insets = new Rect(snapshot.getContentInsets());
- orientation = snapshot.getOrientation();
- reducedResolution = snapshot.isReducedResolution();
- scale = snapshot.getScale();
+ public static ThumbnailData createFromTaskSnapshot(TaskSnapshot snapshot) {
+ ThumbnailData out = new ThumbnailData();
+ out.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
+ out.insets.set(snapshot.getContentInsets());
+ out.orientation = snapshot.getOrientation();
+ out.reducedResolution = snapshot.isReducedResolution();
+ out.scale = snapshot.getScale();
+ return out;
}
}
diff --git a/com/android/systemui/recents/views/AnimateableViewBounds.java b/com/android/systemui/recents/views/AnimateableViewBounds.java
index b598ec6f..7998ecb4 100644
--- a/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -22,7 +22,7 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.recents.misc.Utilities;
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
diff --git a/com/android/systemui/shared/recents/utilities/AnimationProps.java b/com/android/systemui/recents/views/AnimationProps.java
index 2de7f74b..716d1bcf 100644
--- a/com/android/systemui/shared/recents/utilities/AnimationProps.java
+++ b/com/android/systemui/recents/views/AnimationProps.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.utilities;
+package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -24,7 +24,8 @@ import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.View;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
+
+import com.android.systemui.Interpolators;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -36,8 +37,7 @@ import java.util.List;
*/
public class AnimationProps {
- private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR);
+ public static final AnimationProps IMMEDIATE = new AnimationProps(0, Interpolators.LINEAR);
@Retention(RetentionPolicy.SOURCE)
@IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
@@ -51,6 +51,7 @@ public class AnimationProps {
public static final int SCALE = 5;
public static final int BOUNDS = 6;
public static final int DIM_ALPHA = 7;
+ public static final int FOCUS_STATE = 8;
private SparseLongArray mPropStartDelay;
private SparseLongArray mPropDuration;
@@ -194,9 +195,9 @@ public class AnimationProps {
if (interp != null) {
return interp;
}
- return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR);
+ return mPropInterpolators.get(ALL, Interpolators.LINEAR);
}
- return LINEAR_INTERPOLATOR;
+ return Interpolators.LINEAR;
}
/**
diff --git a/com/android/systemui/recents/views/DockState.java b/com/android/systemui/recents/views/DockState.java
deleted file mode 100644
index 59f28680..00000000
--- a/com/android/systemui/recents/views/DockState.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
-import android.util.IntProperty;
-import android.view.animation.Interpolator;
-
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * The various possible dock states when dragging and dropping a task.
- */
-public class DockState implements DropTarget {
-
- public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
- public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
-
- // The rotation to apply to the hint text
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({HORIZONTAL, VERTICAL})
- public @interface TextOrientation {}
- private static final int HORIZONTAL = 0;
- private static final int VERTICAL = 1;
-
- private static final int DOCK_AREA_ALPHA = 80;
- public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
- null, null, null);
- public static final DockState LEFT = new DockState(DOCKED_LEFT,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
- new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
- new RectF(0, 0, 0.5f, 1));
- public static final DockState TOP = new DockState(DOCKED_TOP,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
- new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
- new RectF(0, 0, 1, 0.5f));
- public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
- new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
- new RectF(0.5f, 0, 1, 1));
- public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
- new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
- new RectF(0, 0.5f, 1, 1));
-
- @Override
- public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
- boolean isCurrentTarget) {
- if (isCurrentTarget) {
- getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
- return mTmpRect.contains(x, y);
- } else {
- getMappedRect(touchArea, width, height, mTmpRect);
- updateBoundsWithSystemInsets(mTmpRect, insets);
- return mTmpRect.contains(x, y);
- }
- }
-
- // Represents the view state of this dock state
- public static class ViewState {
- private static final IntProperty<ViewState> HINT_ALPHA =
- new IntProperty<ViewState>("drawableAlpha") {
- @Override
- public void setValue(ViewState object, int alpha) {
- object.mHintTextAlpha = alpha;
- object.dockAreaOverlay.invalidateSelf();
- }
-
- @Override
- public Integer get(ViewState object) {
- return object.mHintTextAlpha;
- }
- };
-
- public final int dockAreaAlpha;
- public final ColorDrawable dockAreaOverlay;
- public final int hintTextAlpha;
- public final int hintTextOrientation;
-
- private final int mHintTextResId;
- private String mHintText;
- private Paint mHintTextPaint;
- private Point mHintTextBounds = new Point();
- private int mHintTextAlpha = 255;
- private AnimatorSet mDockAreaOverlayAnimator;
- private Rect mTmpRect = new Rect();
-
- private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
- int hintTextResId) {
- dockAreaAlpha = areaAlpha;
- dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
- ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
- dockAreaOverlay.setAlpha(0);
- hintTextAlpha = hintAlpha;
- hintTextOrientation = hintOrientation;
- mHintTextResId = hintTextResId;
- mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mHintTextPaint.setColor(Color.WHITE);
- }
-
- /**
- * Updates the view state with the given context.
- */
- public void update(Context context) {
- Resources res = context.getResources();
- mHintText = context.getString(mHintTextResId);
- mHintTextPaint.setTextSize(res.getDimensionPixelSize(
- R.dimen.recents_drag_hint_text_size));
- mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
- mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
- }
-
- /**
- * Draws the current view state.
- */
- public void draw(Canvas canvas) {
- // Draw the overlay background
- if (dockAreaOverlay.getAlpha() > 0) {
- dockAreaOverlay.draw(canvas);
- }
-
- // Draw the hint text
- if (mHintTextAlpha > 0) {
- Rect bounds = dockAreaOverlay.getBounds();
- int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
- int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
- mHintTextPaint.setAlpha(mHintTextAlpha);
- if (hintTextOrientation == VERTICAL) {
- canvas.save();
- canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
- }
- canvas.drawText(mHintText, x, y, mHintTextPaint);
- if (hintTextOrientation == VERTICAL) {
- canvas.restore();
- }
- }
- }
-
- /**
- * Creates a new bounds and alpha animation.
- */
- public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
- Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
- if (mDockAreaOverlayAnimator != null) {
- mDockAreaOverlayAnimator.cancel();
- }
-
- ObjectAnimator anim;
- ArrayList<Animator> animators = new ArrayList<>();
- if (dockAreaOverlay.getAlpha() != areaAlpha) {
- if (animateAlpha) {
- anim = ObjectAnimator.ofInt(dockAreaOverlay,
- Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
- anim.setDuration(duration);
- anim.setInterpolator(interpolator);
- animators.add(anim);
- } else {
- dockAreaOverlay.setAlpha(areaAlpha);
- }
- }
- if (mHintTextAlpha != hintAlpha) {
- if (animateAlpha) {
- anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
- hintAlpha);
- anim.setDuration(150);
- anim.setInterpolator(hintAlpha > mHintTextAlpha
- ? Interpolators.ALPHA_IN
- : Interpolators.ALPHA_OUT);
- animators.add(anim);
- } else {
- mHintTextAlpha = hintAlpha;
- dockAreaOverlay.invalidateSelf();
- }
- }
- if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
- if (animateBounds) {
- PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
- Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
- new Rect(dockAreaOverlay.getBounds()), bounds);
- anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
- anim.setDuration(duration);
- anim.setInterpolator(interpolator);
- animators.add(anim);
- } else {
- dockAreaOverlay.setBounds(bounds);
- }
- }
- if (!animators.isEmpty()) {
- mDockAreaOverlayAnimator = new AnimatorSet();
- mDockAreaOverlayAnimator.playTogether(animators);
- mDockAreaOverlayAnimator.start();
- }
- }
- }
-
- public final int dockSide;
- public final int createMode;
- public final ViewState viewState;
- private final RectF touchArea;
- private final RectF dockArea;
- private final RectF expandedTouchDockArea;
- private static final Rect mTmpRect = new Rect();
-
- /**
- * @param createMode used to pass to ActivityManager to dock the task
- * @param touchArea the area in which touch will initiate this dock state
- * @param dockArea the visible dock area
- * @param expandedTouchDockArea the area in which touch will continue to dock after entering
- * the initial touch area. This is also the new dock area to
- * draw.
- */
- DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
- @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
- RectF expandedTouchDockArea) {
- this.dockSide = dockSide;
- this.createMode = createMode;
- this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
- R.string.recents_drag_hint_message);
- this.dockArea = dockArea;
- this.touchArea = touchArea;
- this.expandedTouchDockArea = expandedTouchDockArea;
- }
-
- /**
- * Updates the dock state with the given context.
- */
- public void update(Context context) {
- viewState.update(context);
- }
-
- /**
- * Returns the docked task bounds with the given {@param width} and {@param height}.
- */
- public Rect getPreDockedBounds(int width, int height, Rect insets) {
- getMappedRect(dockArea, width, height, mTmpRect);
- return updateBoundsWithSystemInsets(mTmpRect, insets);
- }
-
- /**
- * Returns the expanded docked task bounds with the given {@param width} and
- * {@param height}.
- */
- public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
- Resources res) {
- // Calculate the docked task bounds
- boolean isHorizontalDivision =
- res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
- int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
- insets, width, height, dividerSize);
- Rect newWindowBounds = new Rect();
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
- width, height, dividerSize);
- return newWindowBounds;
- }
-
- /**
- * Returns the task stack bounds with the given {@param width} and
- * {@param height}.
- */
- public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
- int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
- Resources res, Rect windowRectOut) {
- // Calculate the inverse docked task bounds
- boolean isHorizontalDivision =
- res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
- int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
- insets, width, height, dividerSize);
- DockedDividerUtils.calculateBoundsForPosition(position,
- DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
- dividerSize);
-
- // Calculate the task stack bounds from the new window bounds
- Rect taskStackBounds = new Rect();
- // If the task stack bounds is specifically under the dock area, then ignore the top
- // inset
- int top = dockArea.bottom < 1f
- ? 0
- : insets.top;
- // For now, ignore the left insets since we always dock on the left and show Recents
- // on the right
- layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
- taskStackBounds);
- return taskStackBounds;
- }
-
- /**
- * Returns the expanded bounds in certain dock sides such that the bounds account for the
- * system insets (namely the vertical nav bar). This call modifies and returns the given
- * {@param bounds}.
- */
- private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
- if (dockSide == DOCKED_LEFT) {
- bounds.right += insets.left;
- } else if (dockSide == DOCKED_RIGHT) {
- bounds.left -= insets.right;
- }
- return bounds;
- }
-
- /**
- * Returns the mapped rect to the given dimensions.
- */
- private void getMappedRect(RectF bounds, int width, int height, Rect out) {
- out.set((int) (bounds.left * width), (int) (bounds.top * height),
- (int) (bounds.right * width), (int) (bounds.bottom * height));
- }
-} \ No newline at end of file
diff --git a/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
new file mode 100644
index 00000000..035c058c
--- /dev/null
+++ b/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.ArrayMap;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.model.Task;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The layout logic for the contents of the freeform workspace.
+ */
+public class FreeformWorkspaceLayoutAlgorithm {
+
+ // Optimization, allows for quick lookup of task -> rect
+ private ArrayMap<Task.TaskKey, RectF> mTaskRectMap = new ArrayMap<>();
+
+ private int mTaskPadding;
+
+ public FreeformWorkspaceLayoutAlgorithm(Context context) {
+ reloadOnConfigurationChange(context);
+ }
+
+ /**
+ * Reloads the layout for the current configuration.
+ */
+ public void reloadOnConfigurationChange(Context context) {
+ // This is applied to the edges of each task
+ mTaskPadding = context.getResources().getDimensionPixelSize(
+ R.dimen.recents_freeform_layout_task_padding) / 2;
+ }
+
+ /**
+ * Updates the layout for each of the freeform workspace tasks. This is called after the stack
+ * layout is updated.
+ */
+ public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
+ Collections.reverse(freeformTasks);
+ mTaskRectMap.clear();
+
+ int numFreeformTasks = stackLayout.mNumFreeformTasks;
+ if (!freeformTasks.isEmpty()) {
+
+ // Normalize the widths so that we can calculate the best layout below
+ int workspaceWidth = stackLayout.mFreeformRect.width();
+ int workspaceHeight = stackLayout.mFreeformRect.height();
+ float normalizedWorkspaceWidth = (float) workspaceWidth / workspaceHeight;
+ float normalizedWorkspaceHeight = 1f;
+ float[] normalizedTaskWidths = new float[numFreeformTasks];
+ for (int i = 0; i < numFreeformTasks; i++) {
+ Task task = freeformTasks.get(i);
+ float rowTaskWidth;
+ if (task.bounds != null) {
+ rowTaskWidth = (float) task.bounds.width() / task.bounds.height();
+ } else {
+ // If this is a stack task that was dragged into the freeform workspace, then
+ // the task will not yet have an associated bounds, so assume the full workspace
+ // width for the time being
+ rowTaskWidth = normalizedWorkspaceWidth;
+ }
+ // Bound the task width to the workspace width so that at the worst case, it will
+ // fit its own row
+ normalizedTaskWidths[i] = Math.min(rowTaskWidth, normalizedWorkspaceWidth);
+ }
+
+ // Determine the scale to best fit each of the tasks in the workspace
+ float rowScale = 0.85f;
+ float rowWidth = 0f;
+ float maxRowWidth = 0f;
+ int rowCount = 1;
+ for (int i = 0; i < numFreeformTasks;) {
+ float width = normalizedTaskWidths[i] * rowScale;
+ if (rowWidth + width > normalizedWorkspaceWidth) {
+ // That is too long for this row, create new row
+ if ((rowCount + 1) * rowScale > normalizedWorkspaceHeight) {
+ // The new row is too high, so we need to try fitting again. Update the
+ // scale to be the smaller of the scale needed to fit the task in the
+ // previous row, or the scale needed to fit the new row
+ rowScale = Math.min(normalizedWorkspaceWidth / (rowWidth + width),
+ normalizedWorkspaceHeight / (rowCount + 1));
+ rowCount = 1;
+ rowWidth = 0;
+ i = 0;
+ } else {
+ // The new row fits, so continue
+ rowWidth = width;
+ rowCount++;
+ i++;
+ }
+ } else {
+ // Task is OK in this row
+ rowWidth += width;
+ i++;
+ }
+ maxRowWidth = Math.max(rowWidth, maxRowWidth);
+ }
+
+ // Normalize each of the actual rects to that scale
+ float defaultRowLeft = ((1f - (maxRowWidth / normalizedWorkspaceWidth)) *
+ workspaceWidth) / 2f;
+ float rowLeft = defaultRowLeft;
+ float rowTop = ((1f - (rowScale * rowCount)) * workspaceHeight) / 2f;
+ float rowHeight = rowScale * workspaceHeight;
+ for (int i = 0; i < numFreeformTasks; i++) {
+ Task task = freeformTasks.get(i);
+ float width = rowHeight * normalizedTaskWidths[i];
+ if (rowLeft + width > workspaceWidth) {
+ // This goes on the next line
+ rowTop += rowHeight;
+ rowLeft = defaultRowLeft;
+ }
+ RectF rect = new RectF(rowLeft, rowTop, rowLeft + width, rowTop + rowHeight);
+ rect.inset(mTaskPadding, mTaskPadding);
+ rowLeft += width;
+ mTaskRectMap.put(task.key, rect);
+ }
+ }
+ }
+
+ /**
+ * Returns whether the transform is available for the given task.
+ */
+ public boolean isTransformAvailable(Task task, TaskStackLayoutAlgorithm stackLayout) {
+ if (stackLayout.mNumFreeformTasks == 0 || task == null) {
+ return false;
+ }
+ return mTaskRectMap.containsKey(task.key);
+ }
+
+ /**
+ * Returns the transform for the given task. Any rect returned will be offset by the actual
+ * transform for the freeform workspace.
+ */
+ public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
+ TaskStackLayoutAlgorithm stackLayout) {
+ if (mTaskRectMap.containsKey(task.key)) {
+ final RectF ffRect = mTaskRectMap.get(task.key);
+
+ transformOut.scale = 1f;
+ transformOut.alpha = 1f;
+ transformOut.translationZ = stackLayout.mMaxTranslationZ;
+ transformOut.dimAlpha = 0f;
+ transformOut.viewOutlineAlpha = TaskStackLayoutAlgorithm.OUTLINE_ALPHA_MAX_VALUE;
+ transformOut.rect.set(ffRect);
+ transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
+ transformOut.visible = true;
+ return transformOut;
+ }
+ return null;
+ }
+}
diff --git a/com/android/systemui/recents/views/RecentsTransitionHelper.java b/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 25c2fc97..ee05d81c 100644
--- a/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -19,6 +19,7 @@ package com.android.systemui.recents.views;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -30,6 +31,8 @@ import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Bundle;
@@ -56,8 +59,8 @@ import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
@@ -185,9 +188,20 @@ public class RecentsTransitionHelper {
} else {
LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
screenPinningRequested);
- EventBus.getDefault().send(launchStartedEvent);
- startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
- activityType);
+ if (task.group != null && !task.group.isFrontMostTask(task)) {
+ launchStartedEvent.addPostAnimationCallback(new Runnable() {
+ @Override
+ public void run() {
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ windowingMode, activityType);
+ }
+ });
+ EventBus.getDefault().send(launchStartedEvent);
+ } else {
+ EventBus.getDefault().send(launchStartedEvent);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ windowingMode, activityType);
+ }
}
Recents.getSystemServices().sendCloseSystemWindows(
StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
@@ -315,6 +329,7 @@ public class RecentsTransitionHelper {
// If this is a full screen stack, the transition will be towards the single, full screen
// task. We only need the transition spec for this task.
+ List<AppTransitionAnimationSpec> specs = new ArrayList<>();
// TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
// check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
@@ -323,7 +338,6 @@ public class RecentsTransitionHelper {
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
|| activityType == ACTIVITY_TYPE_ASSISTANT
|| windowingMode == WINDOWING_MODE_UNDEFINED) {
- List<AppTransitionAnimationSpec> specs = new ArrayList<>();
if (taskView == null) {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
@@ -337,7 +351,34 @@ public class RecentsTransitionHelper {
}
return specs;
}
- return Collections.emptyList();
+
+ // Otherwise, for freeform tasks, create a new animation spec for each task we have to
+ // launch
+ TaskStack stack = stackView.getStack();
+ ArrayList<Task> tasks = stack.getStackTasks();
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.isFreeformTask() || windowingMode == WINDOWING_MODE_FREEFORM) {
+ TaskView tv = stackView.getChildViewForTask(t);
+ if (tv == null) {
+ // TODO: Create a different animation task rect for this case (though it should
+ // never happen)
+ specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
+ } else {
+ mTmpTransform.fillIn(taskView);
+ stackLayout.transformToScreenCoordinates(mTmpTransform,
+ null /* windowOverrideRect */);
+ AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, tv,
+ mTmpTransform, true /* addHeaderBitmap */);
+ if (spec != null) {
+ specs.add(spec);
+ }
+ }
+ }
+ }
+
+ return specs;
}
/**
@@ -421,7 +462,7 @@ public class RecentsTransitionHelper {
// force the task thumbnail to full stackView height immediately causing the transition
// jarring.
if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
- stackView.getStack().getStackFrontMostTask()) {
+ stackView.getStack().getStackFrontMostTask(false /* includeFreeformTasks */)) {
taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
}
return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
diff --git a/com/android/systemui/recents/views/RecentsView.java b/com/android/systemui/recents/views/RecentsView.java
index 5f12a04b..c7edb9ae 100644
--- a/com/android/systemui/recents/views/RecentsView.java
+++ b/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,10 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityOptions.OnAnimationStartedListener;
@@ -53,6 +57,7 @@ import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
@@ -73,9 +78,9 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
import com.android.systemui.stackdivider.WindowManagerProxy;
@@ -109,6 +114,7 @@ public class RecentsView extends FrameLayout {
private final int mStackButtonShadowColor;
private boolean mAwaitingFirstLayout = true;
+ private boolean mLastTaskLaunchedWasFreeform;
@ViewDebug.ExportedProperty(category="recents")
Rect mSystemInsets = new Rect();
@@ -159,20 +165,22 @@ public class RecentsView extends FrameLayout {
mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
- if (mStackActionButton != null) {
- removeView(mStackActionButton);
- }
- mStackActionButton = (TextView) inflater.inflate(Recents.getConfiguration()
- .isLowRamDevice
- ? R.layout.recents_low_ram_stack_action_button
- : R.layout.recents_stack_action_button,
- this, false);
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ if (mStackActionButton != null) {
+ removeView(mStackActionButton);
+ }
+ mStackActionButton = (TextView) inflater.inflate(Recents.getConfiguration()
+ .isLowRamDevice
+ ? R.layout.recents_low_ram_stack_action_button
+ : R.layout.recents_stack_action_button,
+ this, false);
- mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
- mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
- mStackActionButton.getShadowDy());
- mStackButtonShadowColor = mStackActionButton.getShadowColor();
- addView(mStackActionButton);
+ mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
+ mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
+ mStackActionButton.getShadowDy());
+ mStackButtonShadowColor = mStackActionButton.getShadowColor();
+ addView(mStackActionButton);
+ }
reevaluateStyles();
}
@@ -224,6 +232,7 @@ public class RecentsView extends FrameLayout {
// Reset the state
mAwaitingFirstLayout = !isResumingFromVisible;
+ mLastTaskLaunchedWasFreeform = false;
// Update the stack
mTaskStackView.onReload(isResumingFromVisible);
@@ -310,6 +319,13 @@ public class RecentsView extends FrameLayout {
}
}
+ /**
+ * Returns whether the last task launched was in the freeform stack or not.
+ */
+ public boolean isLastTaskLaunchedFreeform() {
+ return mLastTaskLaunchedWasFreeform;
+ }
+
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask(int logEvent) {
if (mTaskStackView != null) {
@@ -355,7 +371,9 @@ public class RecentsView extends FrameLayout {
mEmptyView.setText(msgResId);
mEmptyView.setVisibility(View.VISIBLE);
mEmptyView.bringToFront();
- mStackActionButton.bringToFront();
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButton.bringToFront();
+ }
}
/**
@@ -365,7 +383,9 @@ public class RecentsView extends FrameLayout {
mEmptyView.setVisibility(View.INVISIBLE);
mTaskStackView.setVisibility(View.VISIBLE);
mTaskStackView.bringToFront();
- mStackActionButton.bringToFront();
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButton.bringToFront();
+ }
}
/**
@@ -413,11 +433,13 @@ public class RecentsView extends FrameLayout {
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
- // Measure the stack action button within the constraints of the space above the stack
- Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
- measureChild(mStackActionButton,
- MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Measure the stack action button within the constraints of the space above the stack
+ Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
+ measureChild(mStackActionButton,
+ MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
+ }
setMeasuredDimension(width, height);
}
@@ -451,11 +473,13 @@ public class RecentsView extends FrameLayout {
mBackgroundScrim.setBounds(left, top, right, bottom);
mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
- // Layout the stack action button such that its drawable is start-aligned with the
- // stack, vertically centered in the available space above the stack
- Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
- mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
- buttonBounds.bottom);
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Layout the stack action button such that its drawable is start-aligned with the
+ // stack, vertically centered in the available space above the stack
+ Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+ mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
+ buttonBounds.bottom);
+ }
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
@@ -497,7 +521,7 @@ public class RecentsView extends FrameLayout {
public void onDrawForeground(Canvas canvas) {
super.onDrawForeground(canvas);
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+ ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
for (int i = visDockStates.size() - 1; i >= 0; i--) {
visDockStates.get(i).viewState.draw(canvas);
}
@@ -505,7 +529,7 @@ public class RecentsView extends FrameLayout {
@Override
protected boolean verifyDrawable(Drawable who) {
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+ ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
for (int i = visDockStates.size() - 1; i >= 0; i--) {
Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
if (d == who) {
@@ -518,6 +542,7 @@ public class RecentsView extends FrameLayout {
/**** EventBus Events ****/
public final void onBusEvent(LaunchTaskEvent event) {
+ mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
event.taskView, event.screenPinningRequested, event.targetWindowingMode,
event.targetActivityType);
@@ -528,8 +553,10 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
- // Hide the stack action button
- EventBus.getDefault().send(new HideStackActionButtonEvent());
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ // Hide the stack action button
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
+ }
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
if (Recents.getConfiguration().isLowRamDevice) {
@@ -539,8 +566,8 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(DragStartEvent event) {
updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(),
- true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
- DockState.NONE.viewState.hintTextAlpha,
+ true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ TaskStack.DockState.NONE.viewState.hintTextAlpha,
true /* animateAlpha */, false /* animateBounds */);
// Temporarily hide the stack action button without changing visibility
@@ -554,15 +581,15 @@ public class RecentsView extends FrameLayout {
}
public final void onBusEvent(DragDropTargetChangedEvent event) {
- if (event.dropTarget == null || !(event.dropTarget instanceof DockState)) {
+ if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
updateVisibleDockRegions(
Recents.getConfiguration().getDockStatesForCurrentOrientation(),
- true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
- DockState.NONE.viewState.hintTextAlpha,
+ true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ TaskStack.DockState.NONE.viewState.hintTextAlpha,
true /* animateAlpha */, true /* animateBounds */);
} else {
- final DockState dockState = (DockState) event.dropTarget;
- updateVisibleDockRegions(new DockState[] {dockState},
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+ updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
true /* animateBounds */);
}
@@ -581,8 +608,8 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(final DragEndEvent event) {
// Handle the case where we drop onto a dock region
- if (event.dropTarget instanceof DockState) {
- final DockState dockState = (DockState) event.dropTarget;
+ if (event.dropTarget instanceof TaskStack.DockState) {
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
// Hide the dock region
updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
@@ -704,10 +731,18 @@ public class RecentsView extends FrameLayout {
}
public final void onBusEvent(ShowStackActionButtonEvent event) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
+ return;
+ }
+
showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
}
public final void onBusEvent(HideStackActionButtonEvent event) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
+ return;
+ }
+
hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
}
@@ -723,6 +758,10 @@ public class RecentsView extends FrameLayout {
* Shows the stack action button.
*/
private void showStackActionButton(final int duration, final boolean translate) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
+ return;
+ }
+
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
if (mStackActionButton.getVisibility() == View.INVISIBLE) {
mStackActionButton.setVisibility(View.VISIBLE);
@@ -755,6 +794,10 @@ public class RecentsView extends FrameLayout {
* Hides the stack action button.
*/
private void hideStackActionButton(int duration, boolean translate) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
+ return;
+ }
+
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
hideStackActionButton(duration, translate, postAnimationTrigger);
postAnimationTrigger.flushLastDecrementRunnables();
@@ -765,6 +808,10 @@ public class RecentsView extends FrameLayout {
*/
private void hideStackActionButton(int duration, boolean translate,
final ReferenceCountedTrigger postAnimationTrigger) {
+ if (!RecentsDebugFlags.Static.EnableStackActionButton) {
+ return;
+ }
+
if (mStackActionButton.getVisibility() == View.VISIBLE) {
if (translate) {
mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
@@ -811,15 +858,15 @@ public class RecentsView extends FrameLayout {
/**
* Updates the dock region to match the specified dock state.
*/
- private void updateVisibleDockRegions(DockState[] newDockStates,
+ private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
boolean animateAlpha, boolean animateBounds) {
- ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
- new ArraySet<DockState>());
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+ ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
+ new ArraySet<TaskStack.DockState>());
+ ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
for (int i = visDockStates.size() - 1; i >= 0; i--) {
- DockState dockState = visDockStates.get(i);
- DockState.ViewState viewState = dockState.viewState;
+ TaskStack.DockState dockState = visDockStates.get(i);
+ TaskStack.DockState.ViewState viewState = dockState.viewState;
if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
// This is no longer visible, so hide it
viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
diff --git a/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 0cfdbdec..b6b24bcd 100644
--- a/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.app.ActivityManager;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.InputDevice;
@@ -31,6 +32,7 @@ import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
@@ -39,8 +41,8 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
@@ -70,7 +72,7 @@ public class RecentsViewTouchHandler {
private DropTarget mLastDropTarget;
private DividerSnapAlgorithm mDividerSnapAlgorithm;
private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
- private ArrayList<DockState> mVisibleDockStates = new ArrayList<>();
+ private ArrayList<TaskStack.DockState> mVisibleDockStates = new ArrayList<>();
public RecentsViewTouchHandler(RecentsView rv) {
mRv = rv;
@@ -94,7 +96,7 @@ public class RecentsViewTouchHandler {
/**
* Returns the set of visible dock states for this current drag.
*/
- public ArrayList<DockState> getVisibleDockStates() {
+ public ArrayList<TaskStack.DockState> getVisibleDockStates() {
return mVisibleDockStates;
}
@@ -146,9 +148,9 @@ public class RecentsViewTouchHandler {
EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
} else {
// Add the dock state drop targets (these take priority)
- DockState[] dockStates = Recents.getConfiguration()
+ TaskStack.DockState[] dockStates = Recents.getConfiguration()
.getDockStatesForCurrentOrientation();
- for (DockState dockState : dockStates) {
+ for (TaskStack.DockState dockState : dockStates) {
registerDropTargetForCurrentDrag(dockState);
dockState.update(mRv.getContext());
mVisibleDockStates.add(dockState);
diff --git a/com/android/systemui/recents/views/SystemBarScrimViews.java b/com/android/systemui/recents/views/SystemBarScrimViews.java
index 7827c590..8f784b83 100644
--- a/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -30,7 +30,7 @@ import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
+import com.android.systemui.recents.model.TaskStack;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
@@ -159,7 +159,7 @@ public class SystemBarScrimViews {
public final void onBusEvent(final DragEndEvent event) {
// Hide the nav bar scrims once we drop to a dock region
- if (event.dropTarget instanceof DockState) {
+ if (event.dropTarget instanceof TaskStack.DockState) {
animateScrimToCurrentNavBarState(false /* hasStackTasks */);
}
}
diff --git a/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 26db26fa..81bf6aff 100644
--- a/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -18,11 +18,13 @@ package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
+import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -35,10 +37,9 @@ import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
import java.util.ArrayList;
import java.util.List;
@@ -160,12 +161,20 @@ public class TaskStackAnimationHelper {
for (int i = taskViews.size() - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
+ boolean currentTaskOccludesLaunchTarget = launchTargetTask != null &&
+ launchTargetTask.group != null &&
+ launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
+ boolean hideTask = launchTargetTask != null &&
+ launchTargetTask.isFreeformTask() &&
+ task.isFreeformTask();
// Get the current transform for the task, which will be used to position it offscreen
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
- if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
+ if (hideTask) {
+ tv.setVisibility(View.INVISIBLE);
+ } else if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
} else if (isLowRamDevice && i >= taskViews.size() -
@@ -186,6 +195,13 @@ public class TaskStackAnimationHelper {
// com.android.server.wm.AppTransition#DEFAULT_APP_TRANSITION_DURATION}
mStackView.updateTaskViewToTransform(tv, mTmpTransform,
new AnimationProps(336, Interpolators.FAST_OUT_SLOW_IN));
+ } else if (currentTaskOccludesLaunchTarget) {
+ // Move the task view slightly lower so we can animate it in
+ mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform,
+ AnimationProps.IMMEDIATE);
+ tv.setClipViewInStack(false);
}
} else if (launchState.launchedFromHome) {
if (isLowRamDevice) {
@@ -250,6 +266,9 @@ public class TaskStackAnimationHelper {
int taskIndexFromBack = i;
final TaskView tv = taskViews.get(i);
Task task = tv.getTask();
+ boolean currentTaskOccludesLaunchTarget = launchTargetTask != null &&
+ launchTargetTask.group != null &&
+ launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
// Get the current transform for the task, which will be updated to the final transform
// to animate to depending on how recents was invoked
@@ -261,6 +280,21 @@ public class TaskStackAnimationHelper {
tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
postAnimationTrigger);
+ } else {
+ // Animate the task up if it was occluding the launch target
+ if (currentTaskOccludesLaunchTarget) {
+ AnimationProps taskAnimation = new AnimationProps(
+ taskViewEnterFromAffiliatedAppDuration, Interpolators.ALPHA_IN,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ postAnimationTrigger.decrement();
+ tv.setClipViewInStack(true);
+ }
+ });
+ postAnimationTrigger.increment();
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+ }
}
} else if (launchState.launchedFromHome) {
@@ -389,6 +423,9 @@ public class TaskStackAnimationHelper {
for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
+ boolean currentTaskOccludesLaunchTarget = launchingTask != null &&
+ launchingTask.group != null &&
+ launchingTask.group.isTaskAboveTask(task, launchingTask);
if (tv == launchingTaskView) {
tv.setClipViewInStack(false);
@@ -400,6 +437,17 @@ public class TaskStackAnimationHelper {
});
tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
screenPinningRequested, postAnimationTrigger);
+ } else if (currentTaskOccludesLaunchTarget) {
+ // Animate this task out of view
+ AnimationProps taskAnimation = new AnimationProps(
+ taskViewExitToAppDuration, Interpolators.ALPHA_OUT,
+ postAnimationTrigger.decrementOnAnimationEnd());
+ postAnimationTrigger.increment();
+
+ mTmpTransform.fillIn(tv);
+ mTmpTransform.alpha = 0f;
+ mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
}
@@ -563,7 +611,7 @@ public class TaskStackAnimationHelper {
false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
// Hide the front most task view until the scroll is complete
- Task frontMostTask = newStack.getStackFrontMostTask();
+ Task frontMostTask = newStack.getStackFrontMostTask(false /* includeFreeform */);
final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
stackTasks.indexOf(frontMostTask));
diff --git a/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index acb058ce..eaa32eef 100644
--- a/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -24,6 +24,7 @@ import android.graphics.Path;
import android.graphics.Rect;
import android.util.ArraySet;
import android.util.Log;
+import android.util.MutableFloat;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ViewDebug;
@@ -35,9 +36,9 @@ import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
@@ -146,6 +147,75 @@ public class TaskStackLayoutAlgorithm {
}
/**
+ * The various stack/freeform states.
+ */
+ public static class StackState {
+
+ public static final StackState FREEFORM_ONLY = new StackState(1f, 255);
+ public static final StackState STACK_ONLY = new StackState(0f, 0);
+ public static final StackState SPLIT = new StackState(0.5f, 255);
+
+ public final float freeformHeightPct;
+ public final int freeformBackgroundAlpha;
+
+ /**
+ * @param freeformHeightPct the percentage of the stack height (not including paddings) to
+ * allocate to the freeform workspace
+ * @param freeformBackgroundAlpha the background alpha for the freeform workspace
+ */
+ private StackState(float freeformHeightPct, int freeformBackgroundAlpha) {
+ this.freeformHeightPct = freeformHeightPct;
+ this.freeformBackgroundAlpha = freeformBackgroundAlpha;
+ }
+
+ /**
+ * Resolves the stack state for the layout given a task stack.
+ */
+ public static StackState getStackStateForStack(TaskStack stack) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport();
+ int freeformCount = stack.getFreeformTaskCount();
+ int stackCount = stack.getStackTaskCount();
+ if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) {
+ return SPLIT;
+ } else if (hasFreeformWorkspaces && freeformCount > 0) {
+ return FREEFORM_ONLY;
+ } else {
+ return STACK_ONLY;
+ }
+ }
+
+ /**
+ * Computes the freeform and stack rect for this state.
+ *
+ * @param freeformRectOut the freeform rect to be written out
+ * @param stackRectOut the stack rect, we only write out the top of the stack
+ * @param taskStackBounds the full rect that the freeform rect can take up
+ */
+ public void computeRects(Rect freeformRectOut, Rect stackRectOut,
+ Rect taskStackBounds, int topMargin, int freeformGap, int stackBottomOffset) {
+ // The freeform height is the visible height (not including system insets) - padding
+ // above freeform and below stack - gap between the freeform and stack
+ int availableHeight = taskStackBounds.height() - topMargin - stackBottomOffset;
+ int ffPaddedHeight = (int) (availableHeight * freeformHeightPct);
+ int ffHeight = Math.max(0, ffPaddedHeight - freeformGap);
+ freeformRectOut.set(taskStackBounds.left,
+ taskStackBounds.top + topMargin,
+ taskStackBounds.right,
+ taskStackBounds.top + topMargin + ffHeight);
+ stackRectOut.set(taskStackBounds.left,
+ taskStackBounds.top,
+ taskStackBounds.right,
+ taskStackBounds.bottom);
+ if (ffPaddedHeight > 0) {
+ stackRectOut.top += ffPaddedHeight;
+ } else {
+ stackRectOut.top += topMargin;
+ }
+ }
+ }
+
+ /**
* @return True if we should use the grid layout.
*/
boolean useGridLayout() {
@@ -164,11 +234,15 @@ public class TaskStackLayoutAlgorithm {
}
Context mContext;
+ private StackState mState = StackState.SPLIT;
private TaskStackLayoutAlgorithmCallbacks mCb;
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
@ViewDebug.ExportedProperty(category="recents")
public Rect mTaskRect = new Rect();
+ // The freeform workspace bounds, inset by the top system insets and is a fixed height
+ @ViewDebug.ExportedProperty(category="recents")
+ public Rect mFreeformRect = new Rect();
// The stack bounds, inset from the top system insets, and runs to the bottom of the screen
@ViewDebug.ExportedProperty(category="recents")
public Rect mStackRect = new Rect();
@@ -194,6 +268,10 @@ public class TaskStackLayoutAlgorithm {
private int mBaseBottomMargin;
private int mMinMargin;
+ // The gap between the freeform and stack layouts
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mFreeformStackGap;
+
// The initial offset that the focused task is from the top
@ViewDebug.ExportedProperty(category="recents")
private int mInitialTopOffset;
@@ -253,6 +331,8 @@ public class TaskStackLayoutAlgorithm {
// The last computed task counts
@ViewDebug.ExportedProperty(category="recents")
int mNumStackTasks;
+ @ViewDebug.ExportedProperty(category="recents")
+ int mNumFreeformTasks;
// The min/max z translations
@ViewDebug.ExportedProperty(category="recents")
@@ -264,6 +344,8 @@ public class TaskStackLayoutAlgorithm {
private SparseIntArray mTaskIndexMap = new SparseIntArray();
private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>();
+ // The freeform workspace layout
+ FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
TaskStackLowRamLayoutAlgorithm mTaskStackLowRamLayoutAlgorithm;
@@ -274,6 +356,7 @@ public class TaskStackLayoutAlgorithm {
public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
mContext = context;
mCb = cb;
+ mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
mTaskStackLowRamLayoutAlgorithm = new TaskStackLowRamLayoutAlgorithm(context);
reloadOnConfigurationChange(context);
@@ -310,6 +393,7 @@ public class TaskStackLayoutAlgorithm {
R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet,
R.dimen.recents_layout_initial_bottom_offset_tablet);
+ mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
mTaskStackLowRamLayoutAlgorithm.reloadOnConfigurationChange(context);
mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
@@ -324,6 +408,8 @@ public class TaskStackLayoutAlgorithm {
R.dimen.recents_layout_side_margin_tablet_xlarge,
R.dimen.recents_layout_side_margin_tablet);
mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
+ mFreeformStackGap =
+ res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
mTitleBarHeight = getDimensionForDevice(mContext,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height,
@@ -376,7 +462,8 @@ public class TaskStackLayoutAlgorithm {
* Computes the stack and task rects. The given task stack bounds already has the top/right
* insets and left/right padding already applied.
*/
- public void initialize(Rect displayRect, Rect windowRect, Rect taskStackBounds) {
+ public void initialize(Rect displayRect, Rect windowRect, Rect taskStackBounds,
+ StackState state) {
Rect lastStackRect = new Rect(mStackRect);
int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
@@ -387,9 +474,10 @@ public class TaskStackLayoutAlgorithm {
mInitialBottomOffset = mBaseInitialBottomOffset;
// Compute the stack bounds
+ mState = state;
mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
- mStackRect.set(taskStackBounds);
- mStackRect.top += topMargin;
+ state.computeRects(mFreeformRect, mStackRect, taskStackBounds, topMargin,
+ mFreeformStackGap, mStackBottomOffset);
// The stack action button will take the full un-padded header space above the stack
mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
@@ -442,20 +530,26 @@ public class TaskStackLayoutAlgorithm {
if (tasks.isEmpty()) {
mFrontMostTaskP = 0;
mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
- mNumStackTasks = 0;
+ mNumStackTasks = mNumFreeformTasks = 0;
return;
}
- // Filter the set of stack tasks
+ // Filter the set of freeform and stack tasks
+ ArrayList<Task> freeformTasks = new ArrayList<>();
ArrayList<Task> stackTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
if (ignoreTasksSet.contains(task.key)) {
continue;
}
- stackTasks.add(task);
+ if (task.isFreeformTask()) {
+ freeformTasks.add(task);
+ } else {
+ stackTasks.add(task);
+ }
}
mNumStackTasks = stackTasks.size();
+ mNumFreeformTasks = freeformTasks.size();
// Put each of the tasks in the progress map at a fixed index (does not need to actually
// map to a scroll position, just by index)
@@ -465,6 +559,11 @@ public class TaskStackLayoutAlgorithm {
mTaskIndexMap.put(task.key.id, i);
}
+ // Update the freeform tasks
+ if (!freeformTasks.isEmpty()) {
+ mFreeformLayoutAlgorithm.update(freeformTasks, this);
+ }
+
// Calculate the min/max/initial scroll
Task launchTask = stack.getLaunchTarget();
int launchTaskIndex = launchTask != null
@@ -483,7 +582,7 @@ public class TaskStackLayoutAlgorithm {
} else {
mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
}
- } else if (mNumStackTasks == 1) {
+ } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
// If there is one stack task, ignore the min/max/initial scroll positions
mMinScrollP = 0;
mMaxScrollP = 0;
@@ -504,7 +603,9 @@ public class TaskStackLayoutAlgorithm {
boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp
|| launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture;
- if (launchState.launchedWithAltTab) {
+ if (launchState.launchedFromBlacklistedApp) {
+ mInitialScrollP = mMaxScrollP;
+ } else if (launchState.launchedWithAltTab) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else if (Recents.getConfiguration().isLowRamDevice) {
mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
@@ -532,6 +633,7 @@ public class TaskStackLayoutAlgorithm {
boolean scrollToFront = launchState.launchedFromHome ||
launchState.launchedFromPipApp ||
launchState.launchedWithNextPipApp ||
+ launchState.launchedFromBlacklistedApp ||
launchState.launchedViaDockGesture;
if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
@@ -665,7 +767,7 @@ public class TaskStackLayoutAlgorithm {
public int getInitialFocusState() {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (launchState.launchedWithAltTab) {
+ if (debugFlags.isPagingEnabled() || launchState.launchedWithAltTab) {
return STATE_FOCUSED;
} else {
return STATE_UNFOCUSED;
@@ -692,6 +794,13 @@ public class TaskStackLayoutAlgorithm {
}
/**
+ * Returns the current stack state.
+ */
+ public StackState getStackState() {
+ return mState;
+ }
+
+ /**
* Returns whether this stack layout has been initialized.
*/
public boolean isInitialized() {
@@ -716,44 +825,62 @@ public class TaskStackLayoutAlgorithm {
return new VisibilityReport(1, 1);
}
+ // Quick return when there are no stack tasks
+ if (mNumStackTasks == 0) {
+ return new VisibilityReport(mNumFreeformTasks > 0 ? Math.max(mNumFreeformTasks, 1) : 0,
+ mNumFreeformTasks > 0 ? Math.max(mNumFreeformTasks, 1) : 0);
+ }
+
// Otherwise, walk backwards in the stack and count the number of tasks and visible
- // thumbnails and add that to the total task count
+ // thumbnails and add that to the total freeform task count
TaskViewTransform tmpTransform = new TaskViewTransform();
Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
currentRange.offset(mInitialScrollP);
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_view_header_height);
- int numVisibleTasks = 0;
- int numVisibleThumbnails = 0;
+ int numVisibleTasks = mNumFreeformTasks > 0 ? Math.max(mNumFreeformTasks, 1) : 0;
+ int numVisibleThumbnails = mNumFreeformTasks > 0 ? Math.max(mNumFreeformTasks, 0) : 0;
float prevScreenY = Integer.MAX_VALUE;
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
+ // Skip freeform
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
// Skip invisible
float taskProgress = getStackScrollForTask(task);
if (!currentRange.isInRange(taskProgress)) {
continue;
}
- getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
- tmpTransform, null, false /* ignoreSingleTaskCase */, false /* forceUpdate */);
- float screenY = tmpTransform.rect.top;
- boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
- if (hasVisibleThumbnail) {
- numVisibleThumbnails++;
- numVisibleTasks++;
- prevScreenY = screenY;
- } else {
- // Once we hit the next front most task that does not have a visible thumbnail,
- // walk through remaining visible set
- for (int j = i; j >= 0; j--) {
- taskProgress = getStackScrollForTask(tasks.get(j));
- if (!currentRange.isInRange(taskProgress)) {
- break;
- }
+ boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
+ if (isFrontMostTaskInGroup) {
+ getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
+ tmpTransform, null, false /* ignoreSingleTaskCase */,
+ false /* forceUpdate */);
+ float screenY = tmpTransform.rect.top;
+ boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
+ if (hasVisibleThumbnail) {
+ numVisibleThumbnails++;
numVisibleTasks++;
+ prevScreenY = screenY;
+ } else {
+ // Once we hit the next front most task that does not have a visible thumbnail,
+ // walk through remaining visible set
+ for (int j = i; j >= 0; j--) {
+ taskProgress = getStackScrollForTask(tasks.get(j));
+ if (!currentRange.isInRange(taskProgress)) {
+ break;
+ }
+ numVisibleTasks++;
+ }
+ break;
}
- break;
+ } else {
+ // Affiliated task, no thumbnail
+ numVisibleTasks++;
}
}
return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
@@ -779,7 +906,10 @@ public class TaskStackLayoutAlgorithm {
public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
boolean ignoreTaskOverrides) {
- if (useGridLayout()) {
+ if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
+ mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
+ return transformOut;
+ } else if (useGridLayout()) {
int taskIndex = mTaskIndexMap.get(task.key.id);
int taskCount = mTaskIndexMap.size();
mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
@@ -894,7 +1024,7 @@ public class TaskStackLayoutAlgorithm {
float z;
float dimAlpha;
float viewOutlineAlpha;
- if (mNumStackTasks == 1 && !ignoreSingleTaskCase) {
+ if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1 && !ignoreSingleTaskCase) {
// When there is exactly one task, then decouple the task from the stack and just move
// in screen space
float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
@@ -1248,6 +1378,7 @@ public class TaskStackLayoutAlgorithm {
writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
+ writer.print(" freeform="); writer.print(Utilities.dumpRect(mFreeformRect));
writer.print(" actionButton="); writer.print(Utilities.dumpRect(mStackActionButtonRect));
writer.println();
diff --git a/com/android/systemui/recents/views/TaskStackView.java b/com/android/systemui/recents/views/TaskStackView.java
index 428113a2..3160ee0e 100644
--- a/com/android/systemui/recents/views/TaskStackView.java
+++ b/com/android/systemui/recents/views/TaskStackView.java
@@ -16,15 +16,22 @@
package com.android.systemui.recents.views;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -57,6 +64,7 @@ import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimatio
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
@@ -75,11 +83,13 @@ import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
@@ -87,10 +97,9 @@ import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.grid.GridTaskView;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
@@ -144,6 +153,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
private TaskStackViewTouchHandler mTouchHandler;
private TaskStackAnimationHelper mAnimationHelper;
+ private GradientDrawable mFreeformWorkspaceBackground;
+ private ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
private ViewPool<TaskView, Task> mViewPool;
private ArrayList<TaskView> mTaskViews = new ArrayList<>();
@@ -228,6 +239,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
};
+ // The drop targets for a task drag
+ private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
+ @Override
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
+ // This drop target has a fixed bounds and should be checked last, so just fall through
+ // if it is the current target
+ if (!isCurrentTarget) {
+ return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+ }
+ return false;
+ }
+ };
+
private DropTarget mStackDropTarget = new DropTarget() {
@Override
public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
@@ -287,6 +312,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
});
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ setWillNotDraw(false);
+ }
+
+ mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
+ R.drawable.recents_freeform_workspace_bg);
+ mFreeformWorkspaceBackground.setCallback(this);
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ mFreeformWorkspaceBackground.setColor(
+ getContext().getColor(R.color.recents_freeform_workspace_bg_color));
+ }
}
@Override
@@ -323,7 +359,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
readSystemFlags();
mTaskViewsClipDirty = true;
mUIDozeTrigger.stopDozing();
- if (!isResumingFromVisible) {
+ if (isResumingFromVisible) {
+ // Animate in the freeform workspace
+ int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+ animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+ Interpolators.FAST_OUT_SLOW_IN));
+ } else {
mStackScroller.reset();
mStableLayoutAlgorithm.reset();
mLayoutAlgorithm.reset();
@@ -346,7 +387,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Only notify if we are already initialized, otherwise, everything will pick up all the
// new and old tasks when we next layout
- mStack.setTasks(stack, allowNotifyStackChanges && isInitialized);
+ mStack.setTasks(getContext(), stack, allowNotifyStackChanges && isInitialized);
}
/** Returns the task stack. */
@@ -381,13 +422,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**
* Returns the front most task view.
+ *
+ * @param stackTasksOnly if set, will return the front most task view in the stack (by default
+ * the front most task view will be freeform since they are placed above
+ * stack tasks)
*/
- private TaskView getFrontMostTaskView() {
+ private TaskView getFrontMostTaskView(boolean stackTasksOnly) {
List<TaskView> taskViews = getTaskViews();
- if (taskViews.isEmpty()) {
- return null;
+ int taskViewCount = taskViews.size();
+ for (int i = taskViewCount - 1; i >= 0; i--) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+ if (stackTasksOnly && task.isFreeformTask()) {
+ continue;
+ }
+ return tv;
}
- return taskViews.get(taskViews.size() - 1);
+ return null;
}
/**
@@ -449,6 +500,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
* visible range includes all tasks at the target stack scroll. This is useful for ensure that
* all views necessary for a transition or animation will be visible at the start.
*
+ * This call ignores freeform tasks.
+ *
* @param taskTransforms The set of task view transforms to reuse, this list will be sized to
* match the size of {@param tasks}
* @param tasks The set of tasks for which to generate transforms
@@ -471,7 +524,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
// We can reuse the task transforms where possible to reduce object allocation
- matchTaskListSize(tasks, taskTransforms);
+ Utilities.matchTaskListSize(tasks, taskTransforms);
// Update the stack transforms
TaskViewTransform frontTransform = null;
@@ -501,6 +554,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
continue;
}
+ // For freeform tasks, only calculate the stack transform and skip the calculation of
+ // the visible stack indices
+ if (task.isFreeformTask()) {
+ continue;
+ }
+
frontTransform = transform;
frontTransformAtTarget = transformAtTarget;
if (transform.visible) {
@@ -563,7 +622,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
transform = mCurrentTaskTransforms.get(taskIndex);
}
- if (transform != null && transform.visible) {
+ if (task.isFreeformTask() || (transform != null && transform.visible)) {
mTmpTaskViewMap.put(task.key, tv);
} else {
if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
@@ -584,20 +643,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
continue;
}
- // Skip the invisible stack tasks
- if (!transform.visible) {
+ // Skip the invisible non-freeform stack tasks
+ if (!task.isFreeformTask() && !transform.visible) {
continue;
}
TaskView tv = mTmpTaskViewMap.get(task.key);
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
- if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
- updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
- AnimationProps.IMMEDIATE);
+ if (task.isFreeformTask()) {
+ updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
} else {
- updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
- AnimationProps.IMMEDIATE);
+ if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
+ AnimationProps.IMMEDIATE);
+ } else {
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
+ AnimationProps.IMMEDIATE);
+ }
}
} else {
// Reattach it in the right z order
@@ -701,7 +764,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
public void getCurrentTaskTransforms(ArrayList<Task> tasks,
ArrayList<TaskViewTransform> transformsOut) {
- matchTaskListSize(tasks, transformsOut);
+ Utilities.matchTaskListSize(tasks, transformsOut);
int focusState = mLayoutAlgorithm.getFocusState();
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
@@ -724,7 +787,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
- matchTaskListSize(tasks, transformsOut);
+ Utilities.matchTaskListSize(tasks, transformsOut);
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
@@ -824,6 +887,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Compute the min and max scroll values
mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState);
+ // Update the freeform workspace background
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ mTmpRect.set(mLayoutAlgorithm.mFreeformRect);
+ mFreeformWorkspaceBackground.setBounds(mTmpRect);
+ }
+
if (boundScrollToNewMinMax) {
mStackScroller.boundScroll();
}
@@ -836,7 +906,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mWindowRect.set(mStableWindowRect);
mStackBounds.set(mStableStackBounds);
mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+ mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(true /* boundScroll */);
}
@@ -957,10 +1028,21 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
if (focusedTask != null) {
if (stackTasksOnly) {
List<Task> tasks = mStack.getStackTasks();
- // Try the next task if it is a stack task
- int tmpNewIndex = newIndex + (forward ? -1 : 1);
- if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
- newIndex = tmpNewIndex;
+ if (focusedTask.isFreeformTask()) {
+ // Try and focus the front most stack task
+ TaskView tv = getFrontMostTaskView(stackTasksOnly);
+ if (tv != null) {
+ newIndex = mStack.indexOfStackTask(tv.getTask());
+ }
+ } else {
+ // Try the next task if it is a stack task
+ int tmpNewIndex = newIndex + (forward ? -1 : 1);
+ if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
+ Task t = tasks.get(tmpNewIndex);
+ if (!t.isFreeformTask()) {
+ newIndex = tmpNewIndex;
+ }
+ }
}
} else {
// No restrictions, lets just move to the new task (looping forward/backwards if
@@ -1045,7 +1127,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return tv.getTask();
}
}
- TaskView frontTv = getFrontMostTaskView();
+ TaskView frontTv = getFrontMostTaskView(true /* stackTasksOnly */);
if (frontTv != null) {
return frontTv.getTask();
}
@@ -1196,8 +1278,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Compute the rects in the stack algorithm
- mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+ mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
+ mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(false /* boundScroll */);
// If this is the first layout, then scroll to the front of the stack, then update the
@@ -1320,6 +1404,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Setup the view for the enter animation
mAnimationHelper.prepareForEnterAnimation();
+ // Animate in the freeform workspace
+ int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+ animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+ Interpolators.FAST_OUT_SLOW_IN));
+
// Set the task focused state without requesting view focus, and leave the focus animations
// until after the enter-animation
RecentsConfiguration config = Recents.getConfiguration();
@@ -1367,6 +1456,43 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return null;
}
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw the freeform workspace background
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ if (mFreeformWorkspaceBackground.getAlpha() > 0) {
+ mFreeformWorkspaceBackground.draw(canvas);
+ }
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ if (who == mFreeformWorkspaceBackground) {
+ return true;
+ }
+ return super.verifyDrawable(who);
+ }
+
+ /**
+ * Launches the freeform tasks.
+ */
+ public boolean launchFreeformTasks() {
+ ArrayList<Task> tasks = mStack.getFreeformTasks();
+ if (!tasks.isEmpty()) {
+ Task frontTask = tasks.get(tasks.size() - 1);
+ if (frontTask != null && frontTask.isFreeformTask()) {
+ EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
+ frontTask, null, false));
+ return true;
+ }
+ }
+ return false;
+ }
+
/**** TaskStackCallbacks Implementation ****/
@Override
@@ -1545,7 +1671,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Restore the action button visibility if it is the front most task view
- if (mScreenPinningEnabled && tv.getTask() == mStack.getStackFrontMostTask()) {
+ if (mScreenPinningEnabled && tv.getTask() ==
+ mStack.getStackFrontMostTask(false /* includeFreeform */)) {
tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
}
}
@@ -1561,6 +1688,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.isAsleep() ||
+ Recents.getSystemServices().hasFreeformWorkspaceSupport() ||
useGridLayout() || Recents.getConfiguration().isLowRamDevice) {
tv.setNoUserInteractionState();
}
@@ -1692,17 +1820,21 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
if (mStack.getTaskCount() > 0) {
- Task mostRecentTask = mStack.getStackFrontMostTask();
+ Task mostRecentTask = mStack.getStackFrontMostTask(true /* includeFreefromTasks */);
launchTask(mostRecentTask);
}
}
public final void onBusEvent(ShowStackActionButtonEvent event) {
- mStackActionButtonVisible = true;
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButtonVisible = true;
+ }
}
public final void onBusEvent(HideStackActionButtonEvent event) {
- mStackActionButtonVisible = false;
+ if (RecentsDebugFlags.Static.EnableStackActionButton) {
+ mStackActionButtonVisible = false;
+ }
}
public final void onBusEvent(LaunchNextTaskRequestEvent event) {
@@ -1759,6 +1891,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Start the task animations
mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
+ // Dismiss the freeform workspace background
+ int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
+ animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
+ Interpolators.FAST_OUT_SLOW_IN));
+
// Dismiss the grid task view focus frame
if (mTaskViewFocusFrame != null) {
mTaskViewFocusFrame.moveGridTaskViewFocus(null);
@@ -1840,7 +1977,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
- setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, 0);
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
+ event.timerIndicatorDuration);
}
public final void onBusEvent(FocusPreviousTaskViewEvent event) {
@@ -1864,7 +2002,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
break;
case DOWN:
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
break;
}
}
@@ -1875,7 +2014,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mUIDozeTrigger.poke();
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (mFocusedTask != null) {
+ if (debugFlags.isFastToggleRecentsEnabled() && mFocusedTask != null) {
TaskView tv = getChildViewForTask(mFocusedTask);
if (tv != null) {
tv.getHeaderView().cancelFocusTimerIndicator();
@@ -1887,6 +2026,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Ensure that the drag task is not animated
addIgnoreTask(event.task);
+ if (event.task.isFreeformTask()) {
+ // Animate to the front of the stack
+ mStackScroller.animateScroll(mLayoutAlgorithm.mInitialScrollP, null);
+ }
+
// Enlarge the dragged view slightly
float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
@@ -1898,14 +2042,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
}
+ public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ event.handler.registerDropTargetForCurrentDrag(mStackDropTarget);
+ event.handler.registerDropTargetForCurrentDrag(mFreeformWorkspaceDropTarget);
+ }
+ }
+
public final void onBusEvent(DragDropTargetChangedEvent event) {
AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
Interpolators.FAST_OUT_SLOW_IN);
boolean ignoreTaskOverrides = false;
- if (event.dropTarget instanceof DockState) {
+ if (event.dropTarget instanceof TaskStack.DockState) {
// Calculate the new task stack bounds that matches the window size that Recents will
// have after the drop
- final DockState dockState = (DockState) event.dropTarget;
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
// When docked, the nav bar insets are consumed and the activity is measured without
// insets. However, the window bounds include the insets, so we need to subtract them
@@ -1917,7 +2069,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
height, mDividerSize, systemInsets,
mLayoutAlgorithm, getResources(), mWindowRect));
mLayoutAlgorithm.setSystemInsets(systemInsets);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+ mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
updateLayoutAlgorithm(true /* boundScroll */);
ignoreTaskOverrides = true;
} else {
@@ -1932,13 +2085,39 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public final void onBusEvent(final DragEndEvent event) {
// We don't handle drops on the dock regions
- if (event.dropTarget instanceof DockState) {
+ if (event.dropTarget instanceof TaskStack.DockState) {
// However, we do need to reset the overrides, since the last state of this task stack
// view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
mLayoutAlgorithm.clearUnfocusedTaskOverrides();
return;
}
+ boolean isFreeformTask = event.task.isFreeformTask();
+ boolean hasChangedWindowingMode =
+ (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
+ (isFreeformTask && event.dropTarget == mStackDropTarget);
+
+ if (hasChangedWindowingMode) {
+ // Move the task to the right position in the stack (ie. the front of the stack if
+ // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
+ // before we update their stack ids, otherwise, the keys will have changed.
+ if (event.dropTarget == mFreeformWorkspaceDropTarget) {
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FREEFORM);
+ } else if (event.dropTarget == mStackDropTarget) {
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FULLSCREEN);
+ }
+ updateLayoutAlgorithm(true /* boundScroll */);
+
+ // Move the task to the new stack in the system after the animation completes
+ event.addPostAnimationCallback(new Runnable() {
+ @Override
+ public void run() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.setTaskWindowingMode(event.task.key.id, event.task.key.windowingMode);
+ }
+ });
+ }
+
// Restore the task, so that relayout will apply to it below
removeIgnoreTask(event.task);
@@ -1973,6 +2152,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
event.getAnimationTrigger().increment();
}
+ public final void onBusEvent(IterateRecentsEvent event) {
+ if (!mEnterAnimationComplete) {
+ // Cancel the previous task's window transition before animating the focused state
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+ }
+ }
+
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
mEnterAnimationComplete = true;
tryStartEnterAnimation();
@@ -1991,7 +2177,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Add a runnable to the post animation ref counter to clear all the views
trigger.addLastDecrementRunnable(() -> {
// Start the dozer to trigger to trigger any UI that shows after a timeout
- mUIDozeTrigger.startDozing();
+ if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
+ mUIDozeTrigger.startDozing();
+ }
// Update the focused state here -- since we only set the focused task without
// requesting view focus in onFirstLayout(), actually request view focus and
@@ -2014,6 +2202,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackReloaded = false;
}
+ public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+ if (task.isFreeformTask()) {
+ tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+ }
+
public final void onBusEvent(final MultiWindowStateChangedEvent event) {
if (event.inMultiWindow || !event.showDeferredAnimation) {
setTasks(event.stack, true /* allowNotifyStackChanges */);
@@ -2115,6 +2315,27 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
/**
+ * Starts an alpha animation on the freeform workspace background.
+ */
+ private void animateFreeformWorkspaceBackgroundAlpha(int targetAlpha,
+ AnimationProps animation) {
+ if (mFreeformWorkspaceBackground.getAlpha() == targetAlpha) {
+ return;
+ }
+
+ Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
+ mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
+ Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
+ mFreeformWorkspaceBackgroundAnimator.setStartDelay(
+ animation.getDuration(AnimationProps.ALPHA));
+ mFreeformWorkspaceBackgroundAnimator.setDuration(
+ animation.getDuration(AnimationProps.ALPHA));
+ mFreeformWorkspaceBackgroundAnimator.setInterpolator(
+ animation.getInterpolator(AnimationProps.ALPHA));
+ mFreeformWorkspaceBackgroundAnimator.start();
+ }
+
+ /**
* Returns the insert index for the task in the current set of task views. If the given task
* is already in the task view list, then this method returns the insert index assuming it
* is first removed at the previous index.
@@ -2200,24 +2421,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
- /**
- * Updates {@param transforms} to be the same size as {@param tasks}.
- */
- private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
- // We can reuse the task transforms where possible to reduce object allocation
- int taskTransformCount = transforms.size();
- int taskCount = tasks.size();
- if (taskTransformCount < taskCount) {
- // If there are less transforms than tasks, then add as many transforms as necessary
- for (int i = taskTransformCount; i < taskCount; i++) {
- transforms.add(new TaskViewTransform());
- }
- } else if (taskTransformCount > taskCount) {
- // If there are more transforms than tasks, then just subset the transform list
- transforms.subList(taskCount, taskTransformCount).clear();
- }
- }
-
public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
String id = Integer.toHexString(System.identityHashCode(this));
diff --git a/com/android/systemui/recents/views/TaskStackViewScroller.java b/com/android/systemui/recents/views/TaskStackViewScroller.java
index 6b239774..0b20b105 100644
--- a/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -24,6 +24,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.util.FloatProperty;
import android.util.Log;
+import android.util.MutableFloat;
import android.util.Property;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
@@ -32,8 +33,7 @@ import android.widget.OverScroller;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
import com.android.systemui.statusbar.FlingAnimationUtils;
diff --git a/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index b9ca2483..32a249c2 100644
--- a/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -21,6 +21,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Path;
+import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.MutableBoolean;
import android.view.InputDevice;
@@ -44,9 +45,9 @@ import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.misc.FreePathInterpolator;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.statusbar.FlingAnimationUtils;
import java.util.ArrayList;
@@ -402,6 +403,18 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return;
}
+ // If tapping on the freeform workspace background, just launch the first freeform task
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ Rect freeformRect = mSv.mLayoutAlgorithm.mFreeformRect;
+ if (freeformRect.top <= y && y <= freeformRect.bottom) {
+ if (mSv.launchFreeformTasks()) {
+ // TODO: Animate Recents away as we launch the freeform tasks
+ return;
+ }
+ }
+ }
+
// The user intentionally tapped on the background, which is like a tap on the "desktop".
// Hide recents and transition to the launcher.
EventBus.getDefault().send(new HideRecentsEvent(false, true));
diff --git a/com/android/systemui/recents/views/TaskView.java b/com/android/systemui/recents/views/TaskView.java
index b4408474..9d639647 100644
--- a/com/android/systemui/recents/views/TaskView.java
+++ b/com/android/systemui/recents/views/TaskView.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -51,10 +53,10 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.model.ThumbnailData;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -194,7 +196,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
* Called from RecentsActivity when it is relaunched.
*/
void onReload(boolean isResumingFromVisible) {
- resetNoUserInteractionState();
+ if (!Recents.getSystemServices().hasFreeformWorkspaceSupport()) {
+ resetNoUserInteractionState();
+ }
if (!isResumingFromVisible) {
resetViewProperties();
}
@@ -411,7 +415,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
* view.
*/
boolean shouldClipViewInStack() {
- if (getVisibility() != View.VISIBLE || Recents.getConfiguration().isLowRamDevice) {
+ // Never clip for freeform tasks or if invisible
+ if (mTask.isFreeformTask() || getVisibility() != View.VISIBLE ||
+ Recents.getConfiguration().isLowRamDevice) {
return false;
}
return mClipViewInStack;
@@ -709,7 +715,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
/**** Events ****/
public final void onBusEvent(DragEndEvent event) {
- if (!(event.dropTarget instanceof DockState)) {
+ if (!(event.dropTarget instanceof TaskStack.DockState)) {
event.addPostAnimationCallback(() -> {
// Reset the clip state for the drag view after the end animation completes
setClipViewInStack(true);
diff --git a/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
index 0fc507b9..0c6b6b84 100644
--- a/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
+++ b/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
@@ -28,10 +28,11 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.TaskStack;
public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
private static final String TAG = "TaskViewAccessibilityDelegate";
@@ -60,14 +61,14 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
super.onInitializeAccessibilityNodeInfo(host, info);
if (ActivityManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
&& !Recents.getSystemServices().hasDockedTask()) {
- DockState[] dockStates = Recents.getConfiguration()
+ TaskStack.DockState[] dockStates = Recents.getConfiguration()
.getDockStatesForCurrentOrientation();
- for (DockState dockState: dockStates) {
- if (dockState == DockState.TOP) {
+ for (TaskStack.DockState dockState: dockStates) {
+ if (dockState == TaskStack.DockState.TOP) {
info.addAction(mActions.get(SPLIT_TASK_TOP));
- } else if (dockState == DockState.LEFT) {
+ } else if (dockState == TaskStack.DockState.LEFT) {
info.addAction(mActions.get(SPLIT_TASK_LEFT));
- } else if (dockState == DockState.RIGHT) {
+ } else if (dockState == TaskStack.DockState.RIGHT) {
info.addAction(mActions.get(SPLIT_TASK_RIGHT));
}
}
@@ -77,11 +78,11 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == SPLIT_TASK_TOP) {
- simulateDragIntoMultiwindow(DockState.TOP);
+ simulateDragIntoMultiwindow(TaskStack.DockState.TOP);
} else if (action == SPLIT_TASK_LEFT) {
- simulateDragIntoMultiwindow(DockState.LEFT);
+ simulateDragIntoMultiwindow(TaskStack.DockState.LEFT);
} else if (action == SPLIT_TASK_RIGHT) {
- simulateDragIntoMultiwindow(DockState.RIGHT);
+ simulateDragIntoMultiwindow(TaskStack.DockState.RIGHT);
} else {
return super.performAccessibilityAction(host, action, args);
}
@@ -89,7 +90,8 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
}
/** Simulate a user drag event to split the screen to the respected side */
- private void simulateDragIntoMultiwindow(DockState dockState) {
+ private void simulateDragIntoMultiwindow(TaskStack.DockState dockState) {
+ int orientation = Utilities.getAppConfiguration(mTaskView.getContext()).orientation;
EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView,
new Point(0,0), false /* isUserTouchInitiated */));
EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState));
diff --git a/com/android/systemui/recents/views/TaskViewHeader.java b/com/android/systemui/recents/views/TaskViewHeader.java
index 0272a903..198ecae2 100644
--- a/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/com/android/systemui/recents/views/TaskViewHeader.java
@@ -17,6 +17,8 @@
package com.android.systemui.recents.views;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.animation.Animator;
@@ -31,6 +33,7 @@ import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -56,10 +59,8 @@ import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
/* The task bar view */
public class TaskViewHeader extends FrameLayout
@@ -163,6 +164,8 @@ public class TaskViewHeader extends FrameLayout
float mDimAlpha;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
+ Drawable mLightFreeformIcon;
+ Drawable mDarkFreeformIcon;
Drawable mLightFullscreenIcon;
Drawable mDarkFullscreenIcon;
Drawable mLightInfoIcon;
@@ -170,8 +173,6 @@ public class TaskViewHeader extends FrameLayout
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
int mDisabledTaskBarBackgroundColor;
- String mDismissDescFormat;
- String mAppInfoDescFormat;
int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
// Header background
@@ -214,15 +215,14 @@ public class TaskViewHeader extends FrameLayout
mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
+ mLightFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_light);
+ mDarkFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_dark);
mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
mDisabledTaskBarBackgroundColor =
context.getColor(R.color.recents_task_bar_disabled_background_color);
- mDismissDescFormat = mContext.getString(
- R.string.accessibility_recents_item_will_be_dismissed);
- mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info);
// Configure the background and dim
mBackground = new HighlightColorDrawable();
@@ -249,6 +249,9 @@ public class TaskViewHeader extends FrameLayout
mIconView.setOnLongClickListener(this);
mTitleView = findViewById(R.id.title);
mDismissButton = findViewById(R.id.dismiss_task);
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ mMoveTaskButton = findViewById(R.id.move_task);
+ }
onConfigurationChanged();
}
@@ -338,6 +341,20 @@ public class TaskViewHeader extends FrameLayout
boolean showDismissIcon = true;
int rightInset = width - getMeasuredWidth();
+ if (mTask != null && mTask.isFreeformTask()) {
+ // For freeform tasks, we always show the app icon, and only show the title, move-task
+ // icon, and the dismiss icon if there is room
+ int appIconWidth = mIconView.getMeasuredWidth();
+ int titleWidth = (int) mTitleView.getPaint().measureText(mTask.title);
+ int dismissWidth = mDismissButton.getMeasuredWidth();
+ int moveTaskWidth = mMoveTaskButton != null
+ ? mMoveTaskButton.getMeasuredWidth()
+ : 0;
+ showTitle = width >= (appIconWidth + dismissWidth + moveTaskWidth + titleWidth);
+ showMoveIcon = width >= (appIconWidth + dismissWidth + moveTaskWidth);
+ showDismissIcon = width >= (appIconWidth + dismissWidth);
+ }
+
mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
if (mMoveTaskButton != null) {
mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
@@ -460,14 +477,44 @@ public class TaskViewHeader extends FrameLayout
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
- mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription));
+ mDismissButton.setContentDescription(t.dismissDescription);
mDismissButton.setOnClickListener(this);
mDismissButton.setClickable(false);
((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
+ // When freeform workspaces are enabled, then update the move-task button depending on the
+ // current task
+ if (mMoveTaskButton != null) {
+ if (t.isFreeformTask()) {
+ mTaskWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+ ? mLightFullscreenIcon
+ : mDarkFullscreenIcon);
+ } else {
+ mTaskWindowingMode = WINDOWING_MODE_FREEFORM;
+ mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+ ? mLightFreeformIcon
+ : mDarkFreeformIcon);
+ }
+ mMoveTaskButton.setOnClickListener(this);
+ mMoveTaskButton.setClickable(false);
+ ((RippleDrawable) mMoveTaskButton.getBackground()).setForceSoftware(true);
+ }
+
+ if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) {
+ if (mFocusTimerIndicator == null) {
+ mFocusTimerIndicator = (ProgressBar) Utilities.findViewStubById(this,
+ R.id.focus_timer_indicator_stub).inflate();
+ }
+ mFocusTimerIndicator.getProgressDrawable()
+ .setColorFilter(
+ getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor),
+ PorterDuff.Mode.SRC_IN);
+ }
+
// In accessibility, a single click on the focused app info button will show it
if (touchExplorationEnabled) {
- mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription));
+ mIconView.setContentDescription(t.appInfoDescription);
mIconView.setOnClickListener(this);
mIconView.setClickable(true);
}
@@ -604,7 +651,7 @@ public class TaskViewHeader extends FrameLayout
SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = mTask.key.getComponent();
int userId = mTask.key.userId;
- ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId);
+ ActivityInfo activityInfo = ssp.getActivityInfo(cn, userId);
if (activityInfo == null) {
return;
}
@@ -624,12 +671,11 @@ public class TaskViewHeader extends FrameLayout
}
// Update the overlay contents for the current app
- mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel(
- activityInfo.applicationInfo, userId));
+ mAppTitleView.setText(ssp.getBadgedApplicationLabel(activityInfo.applicationInfo, userId));
mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- mAppIconView.setImageDrawable(ActivityManagerWrapper.getInstance().getBadgedApplicationIcon(
- activityInfo.applicationInfo, userId));
+ mAppIconView.setImageDrawable(ssp.getBadgedApplicationIcon(activityInfo.applicationInfo,
+ userId));
mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
? mLightInfoIcon
: mDarkInfoIcon);
diff --git a/com/android/systemui/recents/views/TaskViewThumbnail.java b/com/android/systemui/recents/views/TaskViewThumbnail.java
index 4152b05a..a2190b3a 100644
--- a/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -37,9 +37,9 @@ import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.ThumbnailData;
import java.io.PrintWriter;
@@ -245,6 +245,10 @@ public class TaskViewThumbnail extends View {
public void updateThumbnailMatrix() {
mThumbnailScale = 1f;
if (mBitmapShader != null && mThumbnailData != null) {
+ // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
+ // dragged into the stack from the freeform workspace
+ boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
+ int xOffset, yOffset = 0;
if (mTaskViewRect.isEmpty()) {
// If we haven't measured , skip the thumbnail drawing and only draw the background
// color
@@ -262,7 +266,7 @@ public class TaskViewThumbnail extends View {
mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
/ (float) mThumbnailRect.height();
}
- } else {
+ } else if (isStackTask) {
float invThumbnailScale = 1f / mFullscreenThumbnailScale;
if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -279,6 +283,12 @@ public class TaskViewThumbnail extends View {
// Otherwise, scale the screenshot to fit 1:1 in the current orientation
mThumbnailScale = invThumbnailScale;
}
+ } else {
+ // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
+ // to fit the entire bitmap into the task bounds
+ mThumbnailScale = Math.min(
+ (float) mTaskViewRect.width() / mThumbnailRect.width(),
+ (float) mTaskViewRect.height() / mThumbnailRect.height());
}
mMatrix.setTranslate(-mThumbnailData.insets.left * mFullscreenThumbnailScale,
-mThumbnailData.insets.top * mFullscreenThumbnailScale);
diff --git a/com/android/systemui/recents/views/TaskViewTransform.java b/com/android/systemui/recents/views/TaskViewTransform.java
index 9b717e0e..397f24eb 100644
--- a/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/com/android/systemui/recents/views/TaskViewTransform.java
@@ -21,11 +21,11 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.IntProperty;
import android.util.Property;
import android.view.View;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.recents.misc.Utilities;
import java.util.ArrayList;
@@ -59,7 +59,7 @@ public class TaskViewTransform {
public boolean visible = false;
- // This is a window-space rect used for positioning the task in the stack
+ // This is a window-space rect used for positioning the task in the stack and freeform workspace
public RectF rect = new RectF();
/**
diff --git a/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index ccda4b5a..c5132024 100644
--- a/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -25,9 +25,10 @@ import android.graphics.Rect;
import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskViewTransform;
diff --git a/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
index 95f1d583..86ed583b 100644
--- a/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
+++ b/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -23,7 +23,7 @@ import android.view.View;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import com.android.systemui.R;
-import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.TaskStackView;
public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
diff --git a/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
index 49cac269..17e6b9e3 100644
--- a/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
+++ b/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
@@ -23,8 +23,8 @@ import android.view.ViewConfiguration;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskViewTransform;
diff --git a/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
deleted file mode 100644
index ddd27b0b..00000000
--- a/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.recents.model;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-/**
- * Background task resource loader
- */
-class BackgroundTaskLoader implements Runnable {
- static String TAG = "BackgroundTaskLoader";
- static boolean DEBUG = false;
-
- private Context mContext;
- private final HandlerThread mLoadThread;
- private final Handler mLoadThreadHandler;
- private final Handler mMainThreadHandler;
-
- private final TaskResourceLoadQueue mLoadQueue;
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final BitmapDrawable mDefaultIcon;
-
- private boolean mStarted;
- private boolean mCancelled;
- private boolean mWaitingOnLoadQueue;
-
- private final OnIdleChangedListener mOnIdleChangedListener;
-
- /** Constructor, creates a new loading thread that loads task resources in the background */
- public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon,
- OnIdleChangedListener onIdleChangedListener) {
- mLoadQueue = loadQueue;
- mIconCache = iconCache;
- mDefaultIcon = defaultIcon;
- mMainThreadHandler = new Handler();
- mOnIdleChangedListener = onIdleChangedListener;
- mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- mLoadThread.start();
- mLoadThreadHandler = new Handler(mLoadThread.getLooper());
- }
-
- /** Restarts the loader thread */
- void start(Context context) {
- mContext = context;
- mCancelled = false;
- if (!mStarted) {
- // Start loading on the load thread
- mStarted = true;
- mLoadThreadHandler.post(this);
- } else {
- // Notify the load thread to start loading again
- synchronized (mLoadThread) {
- mLoadThread.notifyAll();
- }
- }
- }
-
- /** Requests the loader thread to stop after the current iteration */
- void stop() {
- // Mark as cancelled for the thread to pick up
- mCancelled = true;
- // If we are waiting for the load queue for more tasks, then we can just reset the
- // Context now, since nothing is using it
- if (mWaitingOnLoadQueue) {
- mContext = null;
- }
- }
-
- @Override
- public void run() {
- while (true) {
- if (mCancelled) {
- // We have to unset the context here, since the background thread may be using it
- // when we call stop()
- mContext = null;
- // If we are cancelled, then wait until we are started again
- synchronized(mLoadThread) {
- try {
- mLoadThread.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- } else {
- // If we've stopped the loader, then fall through to the above logic to wait on
- // the load thread
- processLoadQueueItem();
-
- // If there are no other items in the list, then just wait until something is added
- if (!mCancelled && mLoadQueue.isEmpty()) {
- synchronized(mLoadQueue) {
- try {
- mWaitingOnLoadQueue = true;
- mMainThreadHandler.post(
- () -> mOnIdleChangedListener.onIdleChanged(true));
- mLoadQueue.wait();
- mMainThreadHandler.post(
- () -> mOnIdleChangedListener.onIdleChanged(false));
- mWaitingOnLoadQueue = false;
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- }
- }
- }
- }
-
- /**
- * This needs to be in a separate method to work around an surprising interpreter behavior:
- * The register will keep the local reference to cachedThumbnailData even if it falls out of
- * scope. Putting it into a method fixes this issue.
- */
- private void processLoadQueueItem() {
- // Load the next item from the queue
- final Task t = mLoadQueue.nextTask();
- if (t != null) {
- Drawable cachedIcon = mIconCache.get(t.key);
-
- // Load the icon if it is stale or we haven't cached one yet
- if (cachedIcon == null) {
- cachedIcon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(
- mContext, t.taskDescription, t.key.userId, mContext.getResources());
-
- if (cachedIcon == null) {
- ActivityInfo info = PackageManagerWrapper.getInstance().getActivityInfo(
- t.key.getComponent(), t.key.userId);
- if (info != null) {
- if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
- cachedIcon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(
- info, t.key.userId);
- }
- }
-
- if (cachedIcon == null) {
- cachedIcon = mDefaultIcon;
- }
-
- // At this point, even if we can't load the icon, we will set the
- // default icon.
- mIconCache.put(t.key, cachedIcon);
- }
-
- if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
- final ThumbnailData thumbnailData =
- ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
- true /* reducedResolution */);
-
- if (!mCancelled) {
- // Notify that the task data has changed
- final Drawable finalIcon = cachedIcon;
- mMainThreadHandler.post(
- () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon));
- }
- }
- }
-
- interface OnIdleChangedListener {
- void onIdleChanged(boolean idle);
- }
-}
diff --git a/com/android/systemui/shared/recents/model/FilteredTaskList.java b/com/android/systemui/shared/recents/model/FilteredTaskList.java
deleted file mode 100644
index 898d64a1..00000000
--- a/com/android/systemui/shared/recents/model/FilteredTaskList.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.recents.model;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A list of filtered tasks.
- */
-class FilteredTaskList {
-
- private final ArrayList<Task> mTasks = new ArrayList<>();
- private final ArrayList<Task> mFilteredTasks = new ArrayList<>();
- private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>();
- private TaskFilter mFilter;
-
- /** Sets the task filter, and returns whether the set of filtered tasks have changed. */
- boolean setFilter(TaskFilter filter) {
- ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
- mFilter = filter;
- updateFilteredTasks();
- return !prevFilteredTasks.equals(mFilteredTasks);
- }
-
- /** Adds a new task to the task list */
- void add(Task t) {
- mTasks.add(t);
- updateFilteredTasks();
- }
-
- /** Sets the list of tasks */
- void set(List<Task> tasks) {
- mTasks.clear();
- mTasks.addAll(tasks);
- updateFilteredTasks();
- }
-
- /** Removes a task from the base list only if it is in the filtered list */
- boolean remove(Task t) {
- if (mFilteredTasks.contains(t)) {
- boolean removed = mTasks.remove(t);
- updateFilteredTasks();
- return removed;
- }
- return false;
- }
-
- /** Returns the index of this task in the list of filtered tasks */
- int indexOf(Task t) {
- if (t != null && mFilteredTaskIndices.containsKey(t.key)) {
- return mFilteredTaskIndices.get(t.key);
- }
- return -1;
- }
-
- /** Returns the size of the list of filtered tasks */
- int size() {
- return mFilteredTasks.size();
- }
-
- /** Returns whether the filtered list contains this task */
- boolean contains(Task t) {
- return mFilteredTaskIndices.containsKey(t.key);
- }
-
- /** Updates the list of filtered tasks whenever the base task list changes */
- private void updateFilteredTasks() {
- mFilteredTasks.clear();
- if (mFilter != null) {
- // Create a sparse array from task id to Task
- SparseArray<Task> taskIdMap = new SparseArray<>();
- int taskCount = mTasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- taskIdMap.put(t.key.id, t);
- }
-
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- if (mFilter.acceptTask(taskIdMap, t, i)) {
- mFilteredTasks.add(t);
- }
- }
- } else {
- mFilteredTasks.addAll(mTasks);
- }
- updateFilteredTaskIndices();
- }
-
- /** Updates the mapping of tasks to indices. */
- private void updateFilteredTaskIndices() {
- int taskCount = mFilteredTasks.size();
- mFilteredTaskIndices.clear();
- for (int i = 0; i < taskCount; i++) {
- Task t = mFilteredTasks.get(i);
- mFilteredTaskIndices.put(t.key, i);
- }
- }
-
- /** Returns the list of filtered tasks */
- ArrayList<Task> getTasks() {
- return mFilteredTasks;
- }
-}
diff --git a/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
deleted file mode 100644
index c9368f3e..00000000
--- a/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.recents.model;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.SparseBooleanArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class stores the loading state as it goes through multiple stages of loading:
- * 1) preloadRawTasks() will load the raw set of recents tasks from the system
- * 2) preloadPlan() will construct a new task stack with all metadata and only icons and
- * thumbnails that are currently in the cache
- * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load
- * options specified, such that we can transition into the Recents activity seamlessly
- */
-public class RecentsTaskLoadPlan {
-
- /** The set of conditions to load tasks. */
- public static class Options {
- public int runningTaskId = -1;
- public boolean loadIcons = true;
- public boolean loadThumbnails = false;
- public boolean onlyLoadForCache = false;
- public boolean onlyLoadPausedActivities = false;
- public int numVisibleTasks = 0;
- public int numVisibleTaskThumbnails = 0;
- }
-
- private final Context mContext;
- private final KeyguardManager mKeyguardManager;
-
- private List<ActivityManager.RecentTaskInfo> mRawTasks;
- private TaskStack mStack;
-
- private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray();
-
- public RecentsTaskLoadPlan(Context context) {
- mContext = context;
- mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- }
-
- /**
- * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
- * to most-recent order.
- *
- * Note: Do not lock, callers should synchronize on the loader before making this call.
- */
- void preloadRawTasks() {
- int currentUserId = ActivityManagerWrapper.getInstance().getCurrentUserId();
- mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
- ActivityManager.getMaxRecentTasksStatic(), currentUserId);
-
- // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
- Collections.reverse(mRawTasks);
- }
-
- /**
- * Preloads the list of recent tasks from the system. After this call, the TaskStack will
- * have a list of all the recent tasks with their metadata, not including icons or
- * thumbnails which were not cached and have to be loaded.
- *
- * The tasks will be ordered by:
- * - least-recent to most-recent stack tasks
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- void preloadPlan(RecentsTaskLoader loader, int runningTaskId) {
- Resources res = mContext.getResources();
- ArrayList<Task> allTasks = new ArrayList<>();
- if (mRawTasks == null) {
- preloadRawTasks();
- }
-
- int taskCount = mRawTasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-
- // Compose the task key
- final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
- TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
- t.userId, t.lastActiveTime);
-
- boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
- boolean isStackTask = !isFreeformTask;
- boolean isLaunchTarget = taskKey.id == runningTaskId;
-
- // Load the title, icon, and color
- ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
- String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
- String titleDescription = loader.getAndUpdateContentDescription(taskKey,
- t.taskDescription);
- Drawable icon = isStackTask
- ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
- : null;
- ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
- false /* loadIfNotCached */, false /* storeInCache */);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
- int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
- boolean isSystemApp = (info != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
-
- // TODO: Refactor to not do this every preload
- if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
- mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
- }
- boolean isLocked = mTmpLockedUsers.get(t.userId);
-
- // Add the task to the stack
- Task task = new Task(taskKey, icon,
- thumbnail, title, titleDescription, activityColor, backgroundColor,
- isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
- t.taskDescription, t.resizeMode, t.topActivity, isLocked);
-
- allTasks.add(task);
- }
-
- // Initialize the stacks
- mStack = new TaskStack();
- mStack.setTasks(allTasks, false /* notifyStackChanges */);
- }
-
- /**
- * Called to apply the actual loading based on the specified conditions.
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- void executePlan(Options opts, RecentsTaskLoader loader) {
- Resources res = mContext.getResources();
-
- // Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getStackTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- TaskKey taskKey = task.key;
-
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
-
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
- true);
- }
- }
- if (opts.loadThumbnails && isVisibleThumbnail) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
- true /* loadIfNotCached */, true /* storeInCache */);
- }
- }
- }
-
- /**
- * Returns the TaskStack from the preloaded list of recent tasks.
- */
- public TaskStack getTaskStack() {
- return mStack;
- }
-
- /** Returns whether there are any tasks in any stacks. */
- public boolean hasTasks() {
- if (mStack != null) {
- return mStack.getTaskCount() > 0;
- }
- return false;
- }
-}
diff --git a/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java b/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
deleted file mode 100644
index fbb6aceb..00000000
--- a/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.recents.model;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * A Task load queue
- */
-class TaskResourceLoadQueue {
-
- private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
-
- /** Adds a new task to the load queue */
- void addTask(Task t) {
- if (!mQueue.contains(t)) {
- mQueue.add(t);
- }
- synchronized(this) {
- notifyAll();
- }
- }
-
- /**
- * Retrieves the next task from the load queue, as well as whether we want that task to be
- * force reloaded.
- */
- Task nextTask() {
- return mQueue.poll();
- }
-
- /** Removes a task from the load queue */
- void removeTask(Task t) {
- mQueue.remove(t);
- }
-
- /** Clears all the tasks from the load queue */
- void clearTasks() {
- mQueue.clear();
- }
-
- /** Returns whether the load queue is empty */
- boolean isEmpty() {
- return mQueue.isEmpty();
- }
-}
diff --git a/com/android/systemui/shared/recents/model/TaskStack.java b/com/android/systemui/shared/recents/model/TaskStack.java
deleted file mode 100644
index 693379d3..00000000
--- a/com/android/systemui/shared/recents/model/TaskStack.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.recents.model;
-
-import android.content.ComponentName;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * The task stack contains a list of multiple tasks.
- */
-public class TaskStack {
-
- private static final String TAG = "TaskStack";
-
- /** Task stack callbacks */
- public interface TaskStackCallbacks {
- /**
- * Notifies when a new task has been added to the stack.
- */
- void onStackTaskAdded(TaskStack stack, Task newTask);
-
- /**
- * Notifies when a task has been removed from the stack.
- */
- void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
- AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved);
-
- /**
- * Notifies when all tasks have been removed from the stack.
- */
- void onStackTasksRemoved(TaskStack stack);
-
- /**
- * Notifies when tasks in the stack have been updated.
- */
- void onStackTasksUpdated(TaskStack stack);
- }
-
- private final ArrayList<Task> mRawTaskList = new ArrayList<>();
- private final FilteredTaskList mStackTaskList = new FilteredTaskList();
- private TaskStackCallbacks mCb;
-
- public TaskStack() {
- // Ensure that we only show stack tasks
- mStackTaskList.setFilter((taskIdMap, t, index) -> t.isStackTask);
- }
-
- /** Sets the callbacks for this task stack. */
- public void setCallbacks(TaskStackCallbacks cb) {
- mCb = cb;
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
- removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved) {
- if (mStackTaskList.contains(t)) {
- mStackTaskList.remove(t);
- Task newFrontMostTask = getStackFrontMostTask();
- if (mCb != null) {
- // Notify that a task has been removed
- mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
- fromDockGesture, dismissRecentsIfAllRemoved);
- }
- }
- mRawTaskList.remove(t);
- }
-
- /**
- * Removes all tasks from the stack.
- */
- public void removeAllTasks(boolean notifyStackChanges) {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task t = tasks.get(i);
- mStackTaskList.remove(t);
- mRawTaskList.remove(t);
- }
- if (mCb != null && notifyStackChanges) {
- // Notify that all tasks have been removed
- mCb.onStackTasksRemoved(this);
- }
- }
-
-
- /**
- * @see #setTasks(List, boolean)
- */
- public void setTasks(TaskStack stack, boolean notifyStackChanges) {
- setTasks(stack.mRawTaskList, notifyStackChanges);
- }
-
- /**
- * Sets a few tasks in one go, without calling any callbacks.
- *
- * @param tasks the new set of tasks to replace the current set.
- * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
- */
- public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
- // Compute a has set for each of the tasks
- ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
- ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
- ArrayList<Task> addedTasks = new ArrayList<>();
- ArrayList<Task> removedTasks = new ArrayList<>();
- ArrayList<Task> allTasks = new ArrayList<>();
-
- // Disable notifications if there are no callbacks
- if (mCb == null) {
- notifyStackChanges = false;
- }
-
- // Remove any tasks that no longer exist
- int taskCount = mRawTaskList.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task task = mRawTaskList.get(i);
- if (!newTasksMap.containsKey(task.key)) {
- if (notifyStackChanges) {
- removedTasks.add(task);
- }
- }
- }
-
- // Add any new tasks
- taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task newTask = tasks.get(i);
- Task currentTask = currentTasksMap.get(newTask.key);
- if (currentTask == null && notifyStackChanges) {
- addedTasks.add(newTask);
- } else if (currentTask != null) {
- // The current task has bound callbacks, so just copy the data from the new task
- // state and add it back into the list
- currentTask.copyFrom(newTask);
- newTask = currentTask;
- }
- allTasks.add(newTask);
- }
-
- // Sort all the tasks to ensure they are ordered correctly
- for (int i = allTasks.size() - 1; i >= 0; i--) {
- allTasks.get(i).temporarySortIndexInStack = i;
- }
-
- mStackTaskList.set(allTasks);
- mRawTaskList.clear();
- mRawTaskList.addAll(allTasks);
-
- // Only callback for the removed tasks after the stack has updated
- int removedTaskCount = removedTasks.size();
- Task newFrontMostTask = getStackFrontMostTask();
- for (int i = 0; i < removedTaskCount; i++) {
- mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
- AnimationProps.IMMEDIATE, false /* fromDockGesture */,
- true /* dismissRecentsIfAllRemoved */);
- }
-
- // Only callback for the newly added tasks after this stack has been updated
- int addedTaskCount = addedTasks.size();
- for (int i = 0; i < addedTaskCount; i++) {
- mCb.onStackTaskAdded(this, addedTasks.get(i));
- }
-
- // Notify that the task stack has been updated
- if (notifyStackChanges) {
- mCb.onStackTasksUpdated(this);
- }
- }
-
- /**
- * Gets the front-most task in the stack.
- */
- public Task getStackFrontMostTask() {
- ArrayList<Task> stackTasks = mStackTaskList.getTasks();
- if (stackTasks.isEmpty()) {
- return null;
- }
- return stackTasks.get(stackTasks.size() - 1);
- }
-
- /** Gets the task keys */
- public ArrayList<TaskKey> getTaskKeys() {
- ArrayList<TaskKey> taskKeys = new ArrayList<>();
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- taskKeys.add(task.key);
- }
- return taskKeys;
- }
-
- /**
- * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
- */
- public ArrayList<Task> getStackTasks() {
- return mStackTaskList.getTasks();
- }
-
- /**
- * Computes a set of all the active and historical tasks.
- */
- public ArrayList<Task> computeAllTasksList() {
- ArrayList<Task> tasks = new ArrayList<>();
- tasks.addAll(mStackTaskList.getTasks());
- return tasks;
- }
-
- /**
- * Returns the number of stacktasks.
- */
- public int getTaskCount() {
- return mStackTaskList.size();
- }
-
- /**
- * Returns the number of stack tasks.
- */
- public int getStackTaskCount() {
- return mStackTaskList.size();
- }
-
- /**
- * Returns the task in stack tasks which is the launch target.
- */
- public Task getLaunchTarget() {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.isLaunchTarget) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Returns whether the next launch target should actually be the PiP task.
- */
- public boolean isNextLaunchTargetPip(long lastPipTime) {
- Task launchTarget = getLaunchTarget();
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null && lastPipTime > 0) {
- // If the PiP time is more recent than the next launch target, then launch the PiP task
- return lastPipTime > nextLaunchTarget.key.lastActiveTime;
- } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
- // Otherwise, if there is no next launch target, but there is a PiP, then launch
- // the PiP task
- return true;
- }
- return false;
- }
-
- /**
- * Returns the task in stack tasks which should be launched next if Recents are toggled
- * again, or null if there is no task to be launched. Callers should check
- * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
- * stack.
- */
- public Task getNextLaunchTarget() {
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null) {
- return nextLaunchTarget;
- }
- return getStackTasks().get(getTaskCount() - 1);
- }
-
- private Task getNextLaunchTargetRaw() {
- int taskCount = getTaskCount();
- if (taskCount == 0) {
- return null;
- }
- int launchTaskIndex = indexOfStackTask(getLaunchTarget());
- if (launchTaskIndex != -1 && launchTaskIndex > 0) {
- return getStackTasks().get(launchTaskIndex - 1);
- }
- return null;
- }
-
- /** Returns the index of this task in this current task stack */
- public int indexOfStackTask(Task t) {
- return mStackTaskList.indexOf(t);
- }
-
- /** Finds the task with the specified task id. */
- public Task findTaskWithId(int taskId) {
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == taskId) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Computes the components of tasks in this stack that have been removed as a result of a change
- * in the specified package.
- */
- public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
- // Identify all the tasks that should be removed as a result of the package being removed.
- // Using a set to ensure that we callback once per unique component.
- ArraySet<ComponentName> existingComponents = new ArraySet<>();
- ArraySet<ComponentName> removedComponents = new ArraySet<>();
- ArrayList<TaskKey> taskKeys = getTaskKeys();
- int taskKeyCount = taskKeys.size();
- for (int i = 0; i < taskKeyCount; i++) {
- TaskKey t = taskKeys.get(i);
-
- // Skip if this doesn't apply to the current user
- if (t.userId != userId) continue;
-
- ComponentName cn = t.getComponent();
- if (cn.getPackageName().equals(packageName)) {
- if (existingComponents.contains(cn)) {
- // If we know that the component still exists in the package, then skip
- continue;
- }
- if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) {
- existingComponents.add(cn);
- } else {
- removedComponents.add(cn);
- }
- }
- }
- return removedComponents;
- }
-
- @Override
- public String toString() {
- String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- str += " " + tasks.get(i).toString() + "\n";
- }
- return str;
- }
-
- /**
- * Given a list of tasks, returns a map of each task's key to the task.
- */
- private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
- ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size());
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- map.put(task.key, task);
- }
- return map;
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
- writer.println();
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- tasks.get(i).dump(innerPrefix, writer);
- }
- }
-}
diff --git a/com/android/systemui/shared/system/ActivityManagerWrapper.java b/com/android/systemui/shared/system/ActivityManagerWrapper.java
deleted file mode 100644
index 3f93f76a..00000000
--- a/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.system;
-
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.AppGlobals;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.IconDrawableFactory;
-import android.util.Log;
-
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ActivityManagerWrapper {
-
- private static final String TAG = "ActivityManagerWrapper";
-
- private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
-
- private final PackageManager mPackageManager;
- private final IconDrawableFactory mDrawableFactory;
-
- private ActivityManagerWrapper() {
- final Context context = AppGlobals.getInitialApplication();
- mPackageManager = context.getPackageManager();
- mDrawableFactory = IconDrawableFactory.newInstance(context);
- }
-
- public static ActivityManagerWrapper getInstance() {
- return sInstance;
- }
-
- /**
- * @return the current user's id.
- */
- public int getCurrentUserId() {
- UserInfo ui;
- try {
- ui = ActivityManager.getService().getCurrentUser();
- return ui != null ? ui.id : 0;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * @return a list of the recents tasks.
- */
- public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
- try {
- return ActivityManager.getService().getRecentTasks(numTasks,
- RECENT_IGNORE_UNAVAILABLE, userId).getList();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get recent tasks", e);
- return new ArrayList<>();
- }
- }
-
- /**
- * @return the task snapshot for the given {@param taskId}.
- */
- public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) {
- ActivityManager.TaskSnapshot snapshot = null;
- try {
- snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to retrieve task snapshot", e);
- }
- if (snapshot != null) {
- return new ThumbnailData(snapshot);
- } else {
- return new ThumbnailData();
- }
- }
-
- /**
- * @return the task description icon, loading and badging it if it necessary.
- */
- public Drawable getBadgedTaskDescriptionIcon(Context context,
- ActivityManager.TaskDescription taskDescription, int userId, Resources res) {
- Bitmap tdIcon = taskDescription.getInMemoryIcon();
- Drawable dIcon = null;
- if (tdIcon != null) {
- dIcon = new BitmapDrawable(res, tdIcon);
- } else if (taskDescription.getIconResource() != 0) {
- try {
- dIcon = context.getDrawable(taskDescription.getIconResource());
- } catch (NotFoundException e) {
- Log.e(TAG, "Could not find icon drawable from resource", e);
- }
- } else {
- tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
- taskDescription.getIconFilename(), userId);
- if (tdIcon != null) {
- dIcon = new BitmapDrawable(res, tdIcon);
- }
- }
- if (dIcon != null) {
- return getBadgedIcon(dIcon, userId);
- }
- return null;
- }
-
- /**
- * @return the given icon for a user, badging if necessary.
- */
- private Drawable getBadgedIcon(Drawable icon, int userId) {
- if (userId != UserHandle.myUserId()) {
- icon = mPackageManager.getUserBadgedIcon(icon, new UserHandle(userId));
- }
- return icon;
- }
-
- /**
- * @return the activity icon for the ActivityInfo for a user, badging if necessary.
- */
- public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
- return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
- }
-
- /**
- * @return the application icon for the ApplicationInfo for a user, badging if necessary.
- */
- public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
- return mDrawableFactory.getBadgedIcon(appInfo, userId);
- }
-
- /**
- * @return the activity label, badging if necessary.
- */
- public String getBadgedActivityLabel(ActivityInfo info, int userId) {
- return getBadgedLabel(info.loadLabel(mPackageManager).toString(), userId);
- }
-
- /**
- * @return the application label, badging if necessary.
- */
- public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
- return getBadgedLabel(appInfo.loadLabel(mPackageManager).toString(), userId);
- }
-
- /**
- * @return the content description for a given task, badging it if necessary. The content
- * description joins the app and activity labels.
- */
- public String getBadgedContentDescription(ActivityInfo info, int userId,
- ActivityManager.TaskDescription td) {
- String activityLabel;
- if (td != null && td.getLabel() != null) {
- activityLabel = td.getLabel();
- } else {
- activityLabel = info.loadLabel(mPackageManager).toString();
- }
- String applicationLabel = info.applicationInfo.loadLabel(mPackageManager).toString();
- String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
- return applicationLabel.equals(activityLabel)
- ? badgedApplicationLabel
- : badgedApplicationLabel + " " + activityLabel;
- }
-
- /**
- * @return the given label for a user, badging if necessary.
- */
- private String getBadgedLabel(String label, int userId) {
- if (userId != UserHandle.myUserId()) {
- label = mPackageManager.getUserBadgedLabel(label, new UserHandle(userId)).toString();
- }
- return label;
- }
-}
diff --git a/com/android/systemui/shared/system/PackageManagerWrapper.java b/com/android/systemui/shared/system/PackageManagerWrapper.java
deleted file mode 100644
index d5e6e6ef..00000000
--- a/com/android/systemui/shared/system/PackageManagerWrapper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.system;
-
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-
-public class PackageManagerWrapper {
-
- private static final String TAG = "PackageManagerWrapper";
-
- private static final PackageManagerWrapper sInstance = new PackageManagerWrapper();
-
- private static final IPackageManager mIPackageManager = AppGlobals.getPackageManager();
-
- public static PackageManagerWrapper getInstance() {
- return sInstance;
- }
-
- /**
- * @return the activity info for a given {@param componentName} and {@param userId}.
- */
- public ActivityInfo getActivityInfo(ComponentName componentName, int userId) {
- try {
- return mIPackageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA,
- userId);
- } catch (RemoteException e) {
- e.printStackTrace();
- return null;
- }
- }
-}
diff --git a/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 195f4d3f..7699bb90 100644
--- a/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,28 +16,37 @@
package com.android.systemui.shortcut;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.os.UserHandle.USER_CURRENT;
-
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-
+import android.view.accessibility.AccessibilityManager;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.settingslib.accessibility.AccessibilityUtils;
+import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import java.util.List;
+import java.util.Set;
/**
* Dispatches shortcut to System UI components
@@ -49,6 +58,7 @@ public class ShortcutKeyDispatcher extends SystemUI
private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ private IActivityManager mActivityManager = ActivityManager.getService();
protected final long META_MASK = ((long) KeyEvent.META_META_ON) << Integer.SIZE;
protected final long ALT_MASK = ((long) KeyEvent.META_ALT_ON) << Integer.SIZE;
@@ -92,10 +102,11 @@ public class ShortcutKeyDispatcher extends SystemUI
// If there is no window docked, we dock the top-most window.
Recents recents = getComponent(Recents.class);
int dockMode = (shortcutCode == SC_DOCK_LEFT)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
List<ActivityManager.RecentTaskInfo> taskList =
- ActivityManagerWrapper.getInstance().getRecentTasks(1, USER_CURRENT);
+ SystemServicesProxy.getInstance(mContext).getRecentTasks(1,
+ UserHandle.USER_CURRENT, false, new ArraySet<>());
recents.showRecentApps(
false /* triggeredFromAltTab */,
false /* fromHome */);
diff --git a/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index 0997983a..578a18a0 100644
--- a/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -32,7 +32,7 @@ import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.stackdivider.events.StartedDragingEvent;
import com.android.systemui.stackdivider.events.StoppedDragingEvent;
@@ -76,7 +76,7 @@ public class ForcedResizableInfoActivityController {
mContext = context;
EventBus.getDefault().register(this);
SystemServicesProxy.getInstance(context).registerTaskStackListener(
- new TaskStackChangeListener() {
+ new TaskStackListener() {
@Override
public void onActivityForcedResizable(String packageName, int taskId,
int reason) {
diff --git a/com/android/systemui/statusbar/ExpandableNotificationRow.java b/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 6c5f4b23..966e7899 100644
--- a/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,7 +36,6 @@ import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
@@ -175,11 +174,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mShowNoBackground;
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
-
- // Listener will be called when receiving a long click event.
- // Use #setLongPressPosition to optionally assign positional data with the long press.
- private LongPressListener mLongPressListener;
-
private boolean mGroupExpansionChanging;
/**
@@ -794,10 +788,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mOnExpandClickListener = onExpandClickListener;
}
- public void setLongPressListener(LongPressListener longPressListener) {
- mLongPressListener = longPressListener;
- }
-
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
super.setOnClickListener(l);
@@ -1348,47 +1338,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
- private void doLongClickCallback() {
- doLongClickCallback(getWidth() / 2, getHeight() / 2);
- }
-
- public void doLongClickCallback(int x, int y) {
- createMenu();
- MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
- if (mLongPressListener != null && menuItem != null) {
- mLongPressListener.onLongPress(this, x, y, menuItem);
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
- event.startTracking();
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
- if (!event.isCanceled()) {
- performClick();
- }
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
- doLongClickCallback();
- return true;
- }
- return false;
- }
-
public void resetTranslation() {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
@@ -2256,7 +2205,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
if (canViewBeDismissed()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
}
@@ -2296,9 +2244,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
case AccessibilityNodeInfo.ACTION_EXPAND:
mExpandClickListener.onClick(this);
return true;
- case AccessibilityNodeInfo.ACTION_LONG_CLICK:
- doLongClickCallback();
- return true;
}
return false;
}
@@ -2387,15 +2332,4 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
mChildrenContainer = childrenContainer;
}
-
- /**
- * Equivalent to View.OnLongClickListener with coordinates
- */
- public interface LongPressListener {
- /**
- * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
- * @return whether the longpress was handled
- */
- boolean onLongPress(View v, int x, int y, MenuItem item);
- }
}
diff --git a/com/android/systemui/statusbar/car/CarStatusBar.java b/com/android/systemui/statusbar/car/CarStatusBar.java
index fed2ebe9..59d3e0a3 100644
--- a/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -36,18 +36,14 @@ import android.view.ViewStub;
import android.view.WindowManager;
import android.widget.LinearLayout;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Dependency;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingLog;
-import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.SwipeHelper;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -55,6 +51,10 @@ import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.classifier.FalsingLog;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.Prefs;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -247,12 +247,11 @@ public class CarStatusBar extends StatusBar implements
}
/**
- * Returns the
- * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
- * be triggered when a notification card is long-pressed.
+ * Returns the {@link com.android.systemui.SwipeHelper.LongPressListener} that will be
+ * triggered when a notification card is long-pressed.
*/
@Override
- protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ protected SwipeHelper.LongPressListener getNotificationLongClicker() {
// For the automative use case, we do not want to the user to be able to interact with
// a notification other than a regular click. As a result, just return null for the
// long click listener.
@@ -305,10 +304,10 @@ public class CarStatusBar extends StatusBar implements
}
/**
- * An implementation of TaskStackChangeListener, that listens for changes in the system task
+ * An implementation of TaskStackListener, that listens for changes in the system task
* stack and notifies the navigation bar.
*/
- private class TaskStackListenerImpl extends TaskStackChangeListener {
+ private class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChanged() {
SystemServicesProxy ssp = Recents.getSystemServices();
diff --git a/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 40ddf5b4..87f5ca7a 100644
--- a/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -124,8 +124,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
} else {
if (selectedUser != null) {
// Show the selected user's static wallpaper.
- return LoaderResult.success(mWallpaperManager.getBitmapAsUser(
- selectedUser.getIdentifier(), true /* hardware */));
+ return LoaderResult.success(
+ mWallpaperManager.getBitmapAsUser(selectedUser.getIdentifier()));
} else {
// When there is no selected user, show the system wallpaper
diff --git a/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index c9500363..f3ca66ff 100644
--- a/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -17,19 +17,12 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
-import android.os.Handler;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWallpaperVisibilityListener;
-import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
-import android.view.WindowManagerGlobal;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
public final class NavigationBarTransitions extends BarTransitions {
@@ -37,7 +30,6 @@ public final class NavigationBarTransitions extends BarTransitions {
private final NavigationBarView mView;
private final IStatusBarService mBarService;
private final LightBarTransitionsController mLightTransitionsController;
- private boolean mWallpaperVisible;
private boolean mLightsOut;
private boolean mAutoDim;
@@ -49,21 +41,6 @@ public final class NavigationBarTransitions extends BarTransitions {
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mLightTransitionsController = new LightBarTransitionsController(view.getContext(),
this::applyDarkIntensity);
-
- IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
- Handler handler = Handler.getMain();
- try {
- mWallpaperVisible = windowManagerService.registerWallpaperVisibilityListener(
- new IWallpaperVisibilityListener.Stub() {
- @Override
- public void onWallpaperVisibilityChanged(boolean newVisibility,
- int displayId) throws RemoteException {
- mWallpaperVisible = newVisibility;
- handler.post(() -> applyLightsOut(true, false));
- }
- }, Display.DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- }
}
public void init() {
@@ -80,7 +57,7 @@ public final class NavigationBarTransitions extends BarTransitions {
@Override
protected boolean isLightsOut(int mode) {
- return super.isLightsOut(mode) || (mAutoDim && !mWallpaperVisible);
+ return super.isLightsOut(mode) || mAutoDim;
}
public LightBarTransitionsController getLightTransitionsController() {
@@ -108,7 +85,7 @@ public final class NavigationBarTransitions extends BarTransitions {
// ok, everyone, stop it right there
navButtons.animate().cancel();
- final float navButtonsAlpha = lightsOut ? 0.6f : 1f;
+ final float navButtonsAlpha = lightsOut ? 0.5f : 1f;
if (!animate) {
navButtons.setAlpha(navButtonsAlpha);
diff --git a/com/android/systemui/statusbar/phone/NotificationPanelView.java b/com/android/systemui/statusbar/phone/NotificationPanelView.java
index af034406..7b11ace8 100644
--- a/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -704,7 +704,7 @@ public class NotificationPanelView extends PanelView implements
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
mIntercepting = false;
- mNotificationStackScroller.cancelLongPress();
+ mNotificationStackScroller.removeLongPressCallback();
}
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -740,7 +740,7 @@ public class NotificationPanelView extends PanelView implements
mInitialTouchY = y;
mInitialTouchX = x;
mIntercepting = false;
- mNotificationStackScroller.cancelLongPress();
+ mNotificationStackScroller.removeLongPressCallback();
return true;
}
break;
diff --git a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b876286b..9c837ed8 100644
--- a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -66,7 +66,7 @@ import com.android.systemui.UiOffloadThread;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -639,17 +639,12 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
}
private Intent getTaskIntent(int taskId, int userId) {
- try {
- final List<ActivityManager.RecentTaskInfo> tasks =
- ActivityManager.getService().getRecentTasks(
- NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList();
- for (int i = 0; i < tasks.size(); i++) {
- if (tasks.get(i).id == taskId) {
- return tasks.get(i).baseIntent;
- }
+ List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class)
+ .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId);
+ for (int i = 0; i < tasks.size(); i++) {
+ if (tasks.get(i).id == taskId) {
+ return tasks.get(i).baseIntent;
}
- } catch (RemoteException e) {
- // Fall through
}
return null;
}
@@ -768,7 +763,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
- private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
+ private final TaskStackListener mTaskListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
// Listen for changes to stacks and then check which instant apps are foreground.
diff --git a/com/android/systemui/statusbar/phone/StatusBar.java b/com/android/systemui/statusbar/phone/StatusBar.java
index 9f039543..54be857d 100644
--- a/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/com/android/systemui/statusbar/phone/StatusBar.java
@@ -160,6 +160,7 @@ import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
@@ -4837,7 +4838,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onTouchSlopExceeded() {
- mStackScroller.cancelLongPress();
+ mStackScroller.removeLongPressCallback();
mStackScroller.checkSnoozeLeavebehind();
}
@@ -5469,7 +5470,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onDoubleTap(float screenX, float screenY) {
- if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
+ if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
&& mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
float viewX = screenX - mTmpInt2[0];
@@ -5881,7 +5882,8 @@ public class StatusBar extends SystemUI implements DemoMode,
List<ActivityManager.RecentTaskInfo> recentTask = null;
try {
recentTask = ActivityManager.getService().getRecentTasks(1,
- ActivityManager.RECENT_WITH_EXCLUDED,
+ ActivityManager.RECENT_WITH_EXCLUDED
+ | ActivityManager.RECENT_INCLUDE_PROFILES,
mCurrentUserId).getList();
} catch (RemoteException e) {
// Abandon hope activity manager not running.
@@ -6294,15 +6296,14 @@ public class StatusBar extends SystemUI implements DemoMode,
true /* removeControls */, x, y, true /* resetMenu */);
}
- protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return new ExpandableNotificationRow.LongPressListener() {
+ protected SwipeHelper.LongPressListener getNotificationLongClicker() {
+ return new SwipeHelper.LongPressListener() {
@Override
public boolean onLongPress(View v, final int x, final int y,
MenuItem item) {
if (!(v instanceof ExpandableNotificationRow)) {
return false;
}
-
if (v.getWindowToken() == null) {
Log.e(TAG, "Trying to show notification guts, but not attached to window");
return false;
@@ -6317,7 +6318,7 @@ public class StatusBar extends SystemUI implements DemoMode,
closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */,
true /* resetMenu */);
- return true;
+ return false;
}
bindGuts(row, item);
NotificationGuts guts = row.getGuts();
@@ -6597,7 +6598,6 @@ public class StatusBar extends SystemUI implements DemoMode,
row.setRemoteViewClickHandler(mOnClickHandler);
row.setInflationCallback(this);
row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
- row.setLongPressListener(getNotificationLongClicker());
// Get the app name.
// Note that Notification.Builder#bindHeaderAppName has similar logic
diff --git a/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1e14626d..75532d9e 100644
--- a/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -240,7 +240,7 @@ public class NotificationStackScrollLayout extends ViewGroup
* motion.
*/
private int mMaxScrollAfterExpand;
- private ExpandableNotificationRow.LongPressListener mLongPressListener;
+ private SwipeHelper.LongPressListener mLongPressListener;
private NotificationMenuRowPlugin mCurrMenuRow;
private View mTranslatingParentView;
@@ -410,6 +410,7 @@ public class NotificationStackScrollLayout extends ViewGroup
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
+ mSwipeHelper.setLongPressListener(mLongPressListener);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
@@ -883,7 +884,8 @@ public class NotificationStackScrollLayout extends ViewGroup
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
- public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+ public void setLongPressListener(SwipeHelper.LongPressListener listener) {
+ mSwipeHelper.setLongPressListener(listener);
mLongPressListener = listener;
}
@@ -1173,7 +1175,7 @@ public class NotificationStackScrollLayout extends ViewGroup
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
}
- cancelLongPress();
+ removeLongPressCallback();
requestDisallowInterceptTouchEvent(true);
}
@@ -2579,7 +2581,7 @@ public class NotificationStackScrollLayout extends ViewGroup
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
if (disallowIntercept) {
- cancelLongPress();
+ mSwipeHelper.removeLongPressCallback();
}
}
@@ -3300,7 +3302,7 @@ public class NotificationStackScrollLayout extends ViewGroup
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
- cancelLongPress();
+ removeLongPressCallback();
}
}
@@ -3308,7 +3310,7 @@ public class NotificationStackScrollLayout extends ViewGroup
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
- cancelLongPress();
+ removeLongPressCallback();
}
}
@@ -3322,7 +3324,7 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public void requestDisallowLongPress() {
- cancelLongPress();
+ removeLongPressCallback();
}
@Override
@@ -3330,8 +3332,8 @@ public class NotificationStackScrollLayout extends ViewGroup
mDisallowDismissInThisMotion = true;
}
- public void cancelLongPress() {
- mSwipeHelper.cancelLongPress();
+ public void removeLongPressCallback() {
+ mSwipeHelper.removeLongPressCallback();
}
@Override
diff --git a/com/android/systemui/util/wakelock/DelayedWakeLock.java b/com/android/systemui/util/wakelock/DelayedWakeLock.java
index 5ec3dff0..b8359097 100644
--- a/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@ import android.os.Handler;
*/
public class DelayedWakeLock implements WakeLock {
- private static final long RELEASE_DELAY_MS = 120;
+ private static final long RELEASE_DELAY_MS = 100;
private final Handler mHandler;
private final WakeLock mInner;
diff --git a/foo/bar/ComplexDao.java b/foo/bar/ComplexDao.java
index dbb54fbc..89859e50 100644
--- a/foo/bar/ComplexDao.java
+++ b/foo/bar/ComplexDao.java
@@ -1,433 +1,69 @@
-package foo.bar;
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-import android.arch.lifecycle.ComputableLiveData;
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.room.InvalidationTracker.Observer;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.RoomSQLiteQuery;
-import android.arch.persistence.room.util.StringUtil;
-import android.database.Cursor;
-import android.support.annotation.NonNull;
-import java.lang.Integer;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
-import java.util.ArrayList;
+package foo.bar;
+import android.arch.persistence.room.*;
import java.util.List;
-import java.util.Set;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class ComplexDao_Impl extends ComplexDao {
- private final RoomDatabase __db;
-
- public ComplexDao_Impl(ComplexDatabase __db) {
- super(__db);
- this.__db = __db;
- }
-
- @Override
- public boolean transactionMethod(int i, String s, long l) {
- __db.beginTransaction();
- try {
- boolean _result = super.transactionMethod(i, s, l);
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public List<ComplexDao.FullName> fullNames(int id) {
- final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
- int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfFullName = _cursor.getColumnIndexOrThrow("fullName");
- final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
- final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>(_cursor.getCount());
- while(_cursor.moveToNext()) {
- final ComplexDao.FullName _item;
- _item = new ComplexDao.FullName();
- _item.fullName = _cursor.getString(_cursorIndexOfFullName);
- _item.id = _cursor.getInt(_cursorIndexOfId);
- _result.add(_item);
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
+import android.arch.lifecycle.LiveData;
+@Dao
+abstract class ComplexDao {
+ static class FullName {
+ public int id;
+ public String fullName;
}
- @Override
- public User getById(int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
- int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
- final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
- final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
- final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
- final User _result;
- if(_cursor.moveToFirst()) {
- _result = new User();
- _result.uid = _cursor.getInt(_cursorIndexOfUid);
- _result.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = _cursor.getInt(_cursorIndexOfAge);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
- }
+ private final ComplexDatabase mDb;
- @Override
- public User findByName(String name, String lastName) {
- final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 2);
- int _argIndex = 1;
- if (name == null) {
- _statement.bindNull(_argIndex);
- } else {
- _statement.bindString(_argIndex, name);
- }
- _argIndex = 2;
- if (lastName == null) {
- _statement.bindNull(_argIndex);
- } else {
- _statement.bindString(_argIndex, lastName);
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
- final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
- final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
- final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
- final User _result;
- if(_cursor.moveToFirst()) {
- _result = new User();
- _result.uid = _cursor.getInt(_cursorIndexOfUid);
- _result.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = _cursor.getInt(_cursorIndexOfAge);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
+ public ComplexDao(ComplexDatabase db) {
+ mDb = db;
}
- @Override
- public List<User> loadAllByIds(int... ids) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex ++;
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
- final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
- final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
- final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
- final List<User> _result = new ArrayList<User>(_cursor.getCount());
- while(_cursor.moveToNext()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
- _item_1.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _item_1.setLastName(_tmpLastName);
- _item_1.age = _cursor.getInt(_cursorIndexOfAge);
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
+ @Transaction
+ public boolean transactionMethod(int i, String s, long l) {
+ return true;
}
- @Override
- int getAge(int id) {
- final String _sql = "SELECT ageColumn FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
- int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _result;
- if(_cursor.moveToFirst()) {
- _result = _cursor.getInt(0);
- } else {
- _result = 0;
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
- }
+ @Query("SELECT name || lastName as fullName, uid as id FROM user where uid = :id")
+ abstract public List<FullName> fullNames(int id);
- @Override
- public int[] getAllAges(int... ids) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex ++;
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final int[] _result = new int[_cursor.getCount()];
- int _index = 0;
- while(_cursor.moveToNext()) {
- final int _item_1;
- _item_1 = _cursor.getInt(0);
- _result[_index] = _item_1;
- _index ++;
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
- }
+ @Query("SELECT * FROM user where uid = :id")
+ abstract public User getById(int id);
- @Override
- public List<Integer> getAllAgesAsList(List<Integer> ids) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- for (Integer _item : ids) {
- if (_item == null) {
- _statement.bindNull(_argIndex);
- } else {
- _statement.bindLong(_argIndex, _item);
- }
- _argIndex ++;
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
- while(_cursor.moveToNext()) {
- final Integer _item_1;
- if (_cursor.isNull(0)) {
- _item_1 = null;
- } else {
- _item_1 = _cursor.getInt(0);
- }
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
- }
+ @Query("SELECT * FROM user where name LIKE :name AND lastName LIKE :lastName")
+ abstract public User findByName(String name, String lastName);
- @Override
- public LiveData<User> getByIdLive(int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
- int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- return new ComputableLiveData<User>() {
- private Observer _observer;
+ @Query("SELECT * FROM user where uid IN (:ids)")
+ abstract public List<User> loadAllByIds(int... ids);
- @Override
- protected User compute() {
- if (_observer == null) {
- _observer = new Observer("user") {
- @Override
- public void onInvalidated(@NonNull Set<String> tables) {
- invalidate();
- }
- };
- __db.getInvalidationTracker().addWeakObserver(_observer);
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
- final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
- final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
- final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
- final User _result;
- if(_cursor.moveToFirst()) {
- _result = new User();
- _result.uid = _cursor.getInt(_cursorIndexOfUid);
- _result.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = _cursor.getInt(_cursorIndexOfAge);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
+ @Query("SELECT ageColumn FROM user where uid = :id")
+ abstract int getAge(int id);
- @Override
- protected void finalize() {
- _statement.release();
- }
- }.getLiveData();
- }
+ @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+ abstract public int[] getAllAges(int... ids);
- @Override
- public LiveData<List<User>> loadUsersByIdsLive(int... ids) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex ++;
- }
- return new ComputableLiveData<List<User>>() {
- private Observer _observer;
+ @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+ abstract public List<Integer> getAllAgesAsList(List<Integer> ids);
- @Override
- protected List<User> compute() {
- if (_observer == null) {
- _observer = new Observer("user") {
- @Override
- public void onInvalidated(@NonNull Set<String> tables) {
- invalidate();
- }
- };
- __db.getInvalidationTracker().addWeakObserver(_observer);
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
- final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
- final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
- final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
- final List<User> _result = new ArrayList<User>(_cursor.getCount());
- while(_cursor.moveToNext()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
- _item_1.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _item_1.setLastName(_tmpLastName);
- _item_1.age = _cursor.getInt(_cursorIndexOfAge);
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
+ @Query("SELECT * FROM user where uid = :id")
+ abstract public LiveData<User> getByIdLive(int id);
- @Override
- protected void finalize() {
- _statement.release();
- }
- }.getLiveData();
- }
+ @Query("SELECT * FROM user where uid IN (:ids)")
+ abstract public LiveData<List<User>> loadUsersByIdsLive(int... ids);
- @Override
- public List<Integer> getAllAgesAsList(List<Integer> ids1, int[] ids2, int... ids3) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids1.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_1 = ids2.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_2 = ids3.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize + _inputSize_1 + _inputSize_2;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- for (Integer _item : ids1) {
- if (_item == null) {
- _statement.bindNull(_argIndex);
- } else {
- _statement.bindLong(_argIndex, _item);
- }
- _argIndex ++;
- }
- _argIndex = 1 + _inputSize;
- for (int _item_1 : ids2) {
- _statement.bindLong(_argIndex, _item_1);
- _argIndex ++;
- }
- _argIndex = 1 + _inputSize + _inputSize_1;
- for (int _item_2 : ids3) {
- _statement.bindLong(_argIndex, _item_2);
- _argIndex ++;
- }
- final Cursor _cursor = __db.query(_statement);
- try {
- final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
- while(_cursor.moveToNext()) {
- final Integer _item_3;
- if (_cursor.isNull(0)) {
- _item_3 = null;
- } else {
- _item_3 = _cursor.getInt(0);
- }
- _result.add(_item_3);
- }
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
- }
+ @Query("SELECT ageColumn FROM user where uid IN(:ids1) OR uid IN (:ids2) OR uid IN (:ids3)")
+ abstract public List<Integer> getAllAgesAsList(List<Integer> ids1,
+ int[] ids2, int... ids3);
}
diff --git a/foo/bar/ComplexDatabase.java b/foo/bar/ComplexDatabase.java
index cfdc1101..f35e0b8a 100644
--- a/foo/bar/ComplexDatabase.java
+++ b/foo/bar/ComplexDatabase.java
@@ -1,99 +1,23 @@
-package foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteDatabase;
-import android.arch.persistence.db.SupportSQLiteOpenHelper;
-import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
-import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
-import android.arch.persistence.room.DatabaseConfiguration;
-import android.arch.persistence.room.InvalidationTracker;
-import android.arch.persistence.room.RoomOpenHelper;
-import android.arch.persistence.room.RoomOpenHelper.Delegate;
-import android.arch.persistence.room.util.TableInfo;
-import android.arch.persistence.room.util.TableInfo.Column;
-import android.arch.persistence.room.util.TableInfo.ForeignKey;
-import android.arch.persistence.room.util.TableInfo.Index;
-import java.lang.IllegalStateException;
-import java.lang.Override;
-import java.lang.String;
-import java.util.HashMap;
-import java.util.HashSet;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class ComplexDatabase_Impl extends ComplexDatabase {
- private volatile ComplexDao _complexDao;
-
- protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
- final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1923) {
- public void createAllTables(SupportSQLiteDatabase _db) {
- _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`uid` INTEGER NOT NULL, `name` TEXT, `lastName` TEXT, `ageColumn` INTEGER NOT NULL, PRIMARY KEY(`uid`))");
- _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
- _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6773601c5bcf94c71ee4eb0de04f21a4\")");
- }
-
- public void dropAllTables(SupportSQLiteDatabase _db) {
- _db.execSQL("DROP TABLE IF EXISTS `User`");
- }
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
- protected void onCreate(SupportSQLiteDatabase _db) {
- if (mCallbacks != null) {
- for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
- mCallbacks.get(_i).onCreate(_db);
- }
- }
- }
-
- public void onOpen(SupportSQLiteDatabase _db) {
- mDatabase = _db;
- internalInitInvalidationTracker(_db);
- if (mCallbacks != null) {
- for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
- mCallbacks.get(_i).onOpen(_db);
- }
- }
- }
-
- protected void validateMigration(SupportSQLiteDatabase _db) {
- final HashMap<String, TableInfo.Column> _columnsUser = new HashMap<String, TableInfo.Column>(4);
- _columnsUser.put("uid", new TableInfo.Column("uid", "INTEGER", true, 1));
- _columnsUser.put("name", new TableInfo.Column("name", "TEXT", false, 0));
- _columnsUser.put("lastName", new TableInfo.Column("lastName", "TEXT", false, 0));
- _columnsUser.put("ageColumn", new TableInfo.Column("ageColumn", "INTEGER", true, 0));
- final HashSet<TableInfo.ForeignKey> _foreignKeysUser = new HashSet<TableInfo.ForeignKey>(0);
- final HashSet<TableInfo.Index> _indicesUser = new HashSet<TableInfo.Index>(0);
- final TableInfo _infoUser = new TableInfo("User", _columnsUser, _foreignKeysUser, _indicesUser);
- final TableInfo _existingUser = TableInfo.read(_db, "User");
- if (! _infoUser.equals(_existingUser)) {
- throw new IllegalStateException("Migration didn't properly handle User(foo.bar.User).\n"
- + " Expected:\n" + _infoUser + "\n"
- + " Found:\n" + _existingUser);
- }
- }
- }, "6773601c5bcf94c71ee4eb0de04f21a4");
- final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
- .name(configuration.name)
- .callback(_openCallback)
- .build();
- final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
- return _helper;
- }
-
- @Override
- protected InvalidationTracker createInvalidationTracker() {
- return new InvalidationTracker(this, "User");
- }
-
- @Override
- ComplexDao getComplexDao() {
- if (_complexDao != null) {
- return _complexDao;
- } else {
- synchronized(this) {
- if(_complexDao == null) {
- _complexDao = new ComplexDao_Impl(this);
- }
- return _complexDao;
- }
- }
- }
+package foo.bar;
+import android.arch.persistence.room.*;
+import java.util.List;
+@Database(entities = {User.class}, version = 1923)
+abstract class ComplexDatabase extends RoomDatabase {
+ abstract ComplexDao getComplexDao();
}
diff --git a/foo/bar/DeletionDao.java b/foo/bar/DeletionDao.java
index 067bf670..997f2906 100644
--- a/foo/bar/DeletionDao.java
+++ b/foo/bar/DeletionDao.java
@@ -1,240 +1,51 @@
-package foo.bar;
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.SharedSQLiteStatement;
-import android.arch.persistence.room.util.StringUtil;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
+package foo.bar;
+import android.arch.persistence.room.*;
import java.util.List;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class DeletionDao_Impl implements DeletionDao {
- private final RoomDatabase __db;
-
- private final EntityDeletionOrUpdateAdapter __deletionAdapterOfUser;
-
- private final EntityDeletionOrUpdateAdapter __deletionAdapterOfMultiPKeyEntity;
-
- private final EntityDeletionOrUpdateAdapter __deletionAdapterOfBook;
-
- private final SharedSQLiteStatement __preparedStmtOfDeleteByUid;
-
- private final SharedSQLiteStatement __preparedStmtOfDeleteEverything;
-
- public DeletionDao_Impl(RoomDatabase __db) {
- this.__db = __db;
- this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- public String createQuery() {
- return "DELETE FROM `User` WHERE `uid` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(1, value.uid);
- }
- };
- this.__deletionAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
- @Override
- public String createQuery() {
- return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
- if (value.name == null) {
- stmt.bindNull(1);
- } else {
- stmt.bindString(1, value.name);
- }
- if (value.lastName == null) {
- stmt.bindNull(2);
- } else {
- stmt.bindString(2, value.lastName);
- }
- }
- };
- this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
- @Override
- public String createQuery() {
- return "DELETE FROM `Book` WHERE `bookId` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, Book value) {
- stmt.bindLong(1, value.bookId);
- }
- };
- this.__preparedStmtOfDeleteByUid = new SharedSQLiteStatement(__db) {
- @Override
- public String createQuery() {
- final String _query = "DELETE FROM user where uid = ?";
- return _query;
- }
- };
- this.__preparedStmtOfDeleteEverything = new SharedSQLiteStatement(__db) {
- @Override
- public String createQuery() {
- final String _query = "DELETE FROM user";
- return _query;
- }
- };
- }
-
- @Override
- public void deleteUser(User user) {
- __db.beginTransaction();
- try {
- __deletionAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void deleteUsers(User user1, List<User> others) {
- __db.beginTransaction();
- try {
- __deletionAdapterOfUser.handle(user1);
- __deletionAdapterOfUser.handleMultiple(others);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void deleteArrayOfUsers(User[] users) {
- __db.beginTransaction();
- try {
- __deletionAdapterOfUser.handleMultiple(users);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public int deleteUserAndReturnCount(User user) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__deletionAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- @Override
- public int deleteUserAndReturnCount(User user1, List<User> others) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__deletionAdapterOfUser.handle(user1);
- _total +=__deletionAdapterOfUser.handleMultiple(others);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
+@Dao
+abstract interface DeletionDao {
+ @Delete
+ void deleteUser(User user);
+ @Delete
+ void deleteUsers(User user1, List<User> others);
+ @Delete
+ void deleteArrayOfUsers(User[] users);
- @Override
- public int deleteUserAndReturnCount(User[] users) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__deletionAdapterOfUser.handleMultiple(users);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
+ @Delete
+ int deleteUserAndReturnCount(User user);
+ @Delete
+ int deleteUserAndReturnCount(User user1, List<User> others);
+ @Delete
+ int deleteUserAndReturnCount(User[] users);
- @Override
- public int multiPKey(MultiPKeyEntity entity) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__deletionAdapterOfMultiPKeyEntity.handle(entity);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
+ @Delete
+ int multiPKey(MultiPKeyEntity entity);
- @Override
- public void deleteUserAndBook(User user, Book book) {
- __db.beginTransaction();
- try {
- __deletionAdapterOfUser.handle(user);
- __deletionAdapterOfBook.handle(book);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
+ @Query("DELETE FROM user where uid = :uid")
+ int deleteByUid(int uid);
- @Override
- public int deleteByUid(int uid) {
- final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
- __db.beginTransaction();
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- final int _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- __preparedStmtOfDeleteByUid.release(_stmt);
- }
- }
+ @Query("DELETE FROM user where uid IN(:uid)")
+ int deleteByUidList(int... uid);
- @Override
- public int deleteEverything() {
- final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteEverything.acquire();
- __db.beginTransaction();
- try {
- final int _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- __preparedStmtOfDeleteEverything.release(_stmt);
- }
- }
+ @Delete
+ void deleteUserAndBook(User user, Book book);
- @Override
- public int deleteByUidList(int... uid) {
- StringBuilder _stringBuilder = StringUtil.newStringBuilder();
- _stringBuilder.append("DELETE FROM user where uid IN(");
- final int _inputSize = uid.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- for (int _item : uid) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex ++;
- }
- __db.beginTransaction();
- try {
- final int _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
+ @Query("DELETE FROM user")
+ int deleteEverything();
}
diff --git a/foo/bar/UpdateDao.java b/foo/bar/UpdateDao.java
index 1190a0df..040b5c79 100644
--- a/foo/bar/UpdateDao.java
+++ b/foo/bar/UpdateDao.java
@@ -1,240 +1,48 @@
-package foo.bar;
+/*
+ * 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.
+ */
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.SharedSQLiteStatement;
-import java.lang.Override;
-import java.lang.String;
+package foo.bar;
+import android.arch.persistence.room.*;
import java.util.List;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class UpdateDao_Impl implements UpdateDao {
- private final RoomDatabase __db;
-
- private final EntityDeletionOrUpdateAdapter __updateAdapterOfUser;
-
- private final EntityDeletionOrUpdateAdapter __updateAdapterOfMultiPKeyEntity;
-
- private final EntityDeletionOrUpdateAdapter __updateAdapterOfBook;
-
- private final SharedSQLiteStatement __preparedStmtOfAgeUserByUid;
-
- private final SharedSQLiteStatement __preparedStmtOfAgeUserAll;
-
- public UpdateDao_Impl(RoomDatabase __db) {
- this.__db = __db;
- this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- public String createQuery() {
- return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(1, value.uid);
- if (value.name == null) {
- stmt.bindNull(2);
- } else {
- stmt.bindString(2, value.name);
- }
- if (value.getLastName() == null) {
- stmt.bindNull(3);
- } else {
- stmt.bindString(3, value.getLastName());
- }
- stmt.bindLong(4, value.age);
- stmt.bindLong(5, value.uid);
- }
- };
- this.__updateAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
- @Override
- public String createQuery() {
- return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
- if (value.name == null) {
- stmt.bindNull(1);
- } else {
- stmt.bindString(1, value.name);
- }
- if (value.lastName == null) {
- stmt.bindNull(2);
- } else {
- stmt.bindString(2, value.lastName);
- }
- if (value.name == null) {
- stmt.bindNull(3);
- } else {
- stmt.bindString(3, value.name);
- }
- if (value.lastName == null) {
- stmt.bindNull(4);
- } else {
- stmt.bindString(4, value.lastName);
- }
- }
- };
- this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
- @Override
- public String createQuery() {
- return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, Book value) {
- stmt.bindLong(1, value.bookId);
- stmt.bindLong(2, value.uid);
- stmt.bindLong(3, value.bookId);
- }
- };
- this.__preparedStmtOfAgeUserByUid = new SharedSQLiteStatement(__db) {
- @Override
- public String createQuery() {
- final String _query = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
- return _query;
- }
- };
- this.__preparedStmtOfAgeUserAll = new SharedSQLiteStatement(__db) {
- @Override
- public String createQuery() {
- final String _query = "UPDATE User SET ageColumn = ageColumn + 1";
- return _query;
- }
- };
- }
-
- @Override
- public void updateUser(User user) {
- __db.beginTransaction();
- try {
- __updateAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void updateUsers(User user1, List<User> others) {
- __db.beginTransaction();
- try {
- __updateAdapterOfUser.handle(user1);
- __updateAdapterOfUser.handleMultiple(others);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void updateArrayOfUsers(User[] users) {
- __db.beginTransaction();
- try {
- __updateAdapterOfUser.handleMultiple(users);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public int updateUserAndReturnCount(User user) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__updateAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public int updateUserAndReturnCount(User user1, List<User> others) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__updateAdapterOfUser.handle(user1);
- _total +=__updateAdapterOfUser.handleMultiple(others);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public int updateUserAndReturnCount(User[] users) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__updateAdapterOfUser.handleMultiple(users);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public int multiPKey(MultiPKeyEntity entity) {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total +=__updateAdapterOfMultiPKeyEntity.handle(entity);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void updateUserAndBook(User user, Book book) {
- __db.beginTransaction();
- try {
- __updateAdapterOfUser.handle(user);
- __updateAdapterOfBook.handle(book);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void ageUserByUid(String uid) {
- final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserByUid.acquire();
- __db.beginTransaction();
- try {
- int _argIndex = 1;
- if (uid == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindString(_argIndex, uid);
- }
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- __preparedStmtOfAgeUserByUid.release(_stmt);
- }
- }
- @Override
- public void ageUserAll() {
- final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
- __db.beginTransaction();
- try {
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- __preparedStmtOfAgeUserAll.release(_stmt);
- }
- }
+@Dao
+abstract interface UpdateDao {
+ @Update
+ void updateUser(User user);
+ @Update
+ void updateUsers(User user1, List<User> others);
+ @Update
+ void updateArrayOfUsers(User[] users);
+
+ @Update
+ int updateUserAndReturnCount(User user);
+ @Update
+ int updateUserAndReturnCount(User user1, List<User> others);
+ @Update
+ int updateUserAndReturnCount(User[] users);
+
+ @Update
+ int multiPKey(MultiPKeyEntity entity);
+
+ @Update
+ void updateUserAndBook(User user, Book book);
+
+ @Query("UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = :uid")
+ void ageUserByUid(String uid);
+
+ @Query("UPDATE User SET ageColumn = ageColumn + 1")
+ void ageUserAll();
}
diff --git a/foo/bar/WriterDao.java b/foo/bar/WriterDao.java
index cfad0469..e122479b 100644
--- a/foo/bar/WriterDao.java
+++ b/foo/bar/WriterDao.java
@@ -15,131 +15,17 @@
*/
package foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityInsertionAdapter;
-import android.arch.persistence.room.RoomDatabase;
-
-import java.lang.Override;
-import java.lang.String;
+import android.arch.persistence.room.*;
import java.util.List;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class WriterDao_Impl implements WriterDao {
- private final RoomDatabase __db;
-
- private final EntityInsertionAdapter __insertionAdapterOfUser;
-
- private final EntityInsertionAdapter __insertionAdapterOfUser_1;
-
- private final EntityInsertionAdapter __insertionAdapterOfBook;
-
- public WriterDao_Impl(RoomDatabase __db) {
- this.__db = __db;
- this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
- @Override
- public String createQuery() {
- return "INSERT OR ABORT INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
- + " (?,?,?,?)";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(1, value.uid);
- if (value.name == null) {
- stmt.bindNull(2);
- } else {
- stmt.bindString(2, value.name);
- }
- if (value.getLastName() == null) {
- stmt.bindNull(3);
- } else {
- stmt.bindString(3, value.getLastName());
- }
- stmt.bindLong(4, value.age);
- }
- };
- this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
- @Override
- public String createQuery() {
- return "INSERT OR REPLACE INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
- + " (?,?,?,?)";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, User value) {
- stmt.bindLong(1, value.uid);
- if (value.name == null) {
- stmt.bindNull(2);
- } else {
- stmt.bindString(2, value.name);
- }
- if (value.getLastName() == null) {
- stmt.bindNull(3);
- } else {
- stmt.bindString(3, value.getLastName());
- }
- stmt.bindLong(4, value.age);
- }
- };
- this.__insertionAdapterOfBook = new EntityInsertionAdapter<Book>(__db) {
- @Override
- public String createQuery() {
- return "INSERT OR ABORT INTO `Book`(`bookId`,`uid`) VALUES (?,?)";
- }
-
- @Override
- public void bind(SupportSQLiteStatement stmt, Book value) {
- stmt.bindLong(1, value.bookId);
- stmt.bindLong(2, value.uid);
- }
- };
- }
-
- @Override
- public void insertUser(User user) {
- __db.beginTransaction();
- try {
- __insertionAdapterOfUser.insert(user);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void insertUsers(User user1, List<User> others) {
- __db.beginTransaction();
- try {
- __insertionAdapterOfUser.insert(user1);
- __insertionAdapterOfUser.insert(others);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
-
- @Override
- public void insertUsers(User[] users) {
- __db.beginTransaction();
- try {
- __insertionAdapterOfUser_1.insert(users);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
- @Override
- public void insertUserAndBook(User user, Book book) {
- __db.beginTransaction();
- try {
- __insertionAdapterOfUser.insert(user);
- __insertionAdapterOfBook.insert(book);
- __db.setTransactionSuccessful();
- } finally {
- __db.endTransaction();
- }
- }
+@Dao
+abstract interface WriterDao {
+ @Insert
+ void insertUser(User user);
+ @Insert
+ void insertUsers(User user1, List<User> others);
+ @Insert(onConflict=OnConflictStrategy.REPLACE)
+ void insertUsers(User[] users);
+ @Insert
+ void insertUserAndBook(User user, Book book);
}
diff --git a/java/lang/Boolean.java b/java/lang/Boolean.java
index 397bf092..802d9104 100644
--- a/java/lang/Boolean.java
+++ b/java/lang/Boolean.java
@@ -61,8 +61,6 @@ public final class Boolean implements java.io.Serializable,
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");
public static final Class<Boolean> TYPE = (Class<Boolean>) boolean[].class.getComponentType();
/**
diff --git a/java/lang/Byte.java b/java/lang/Byte.java
index 2333afd3..d0031d0c 100644
--- a/java/lang/Byte.java
+++ b/java/lang/Byte.java
@@ -60,8 +60,6 @@ public final class Byte extends Number implements Comparable<Byte> {
* {@code byte}.
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Byte> TYPE = (Class<Byte>) Class.getPrimitiveClass("byte");
public static final Class<Byte> TYPE = (Class<Byte>) byte[].class.getComponentType();
/**
diff --git a/java/lang/Character.java b/java/lang/Character.java
index 7615dab7..fb0d576e 100644
--- a/java/lang/Character.java
+++ b/java/lang/Character.java
@@ -173,8 +173,6 @@ class Character implements java.io.Serializable, Comparable<Character> {
* @since 1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char");
public static final Class<Character> TYPE = (Class<Character>) char[].class.getComponentType();
/*
diff --git a/java/lang/Double.java b/java/lang/Double.java
index 2721a842..2bc2bf39 100644
--- a/java/lang/Double.java
+++ b/java/lang/Double.java
@@ -137,8 +137,6 @@ public final class Double extends Number implements Comparable<Double> {
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Double> TYPE = (Class<Double>) Class.getPrimitiveClass("double");
public static final Class<Double> TYPE = (Class<Double>) double[].class.getComponentType();
/**
diff --git a/java/lang/Float.java b/java/lang/Float.java
index 6a2b9336..32bd625a 100644
--- a/java/lang/Float.java
+++ b/java/lang/Float.java
@@ -135,8 +135,6 @@ public final class Float extends Number implements Comparable<Float> {
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Float> TYPE = (Class<Float>) Class.getPrimitiveClass("float");
public static final Class<Float> TYPE = (Class<Float>) float[].class.getComponentType();
/**
diff --git a/java/lang/Integer.java b/java/lang/Integer.java
index c63b0d55..f742505a 100644
--- a/java/lang/Integer.java
+++ b/java/lang/Integer.java
@@ -70,8 +70,6 @@ public final class Integer extends Number implements Comparable<Integer> {
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
public static final Class<Integer> TYPE = (Class<Integer>) int[].class.getComponentType();
/**
@@ -317,8 +315,7 @@ public final class Integer extends Number implements Comparable<Integer> {
formatUnsignedInt(val, shift, buf, 0, chars);
- // Android-changed: Use regular constructor instead of one which takes over "buf".
- // return new String(buf, true);
+ // Use special constructor which takes over "buf".
return new String(buf);
}
@@ -343,10 +340,8 @@ public final class Integer extends Number implements Comparable<Integer> {
return charPos;
}
- // BEGIN Android-changed: Cache the toString() result for small values.
private static final String[] SMALL_NEG_VALUES = new String[100];
private static final String[] SMALL_NONNEG_VALUES = new String[100];
- // END Android-changed: Cache the toString() result for small values.
final static char [] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
@@ -407,8 +402,7 @@ public final class Integer extends Number implements Comparable<Integer> {
if (i == Integer.MIN_VALUE)
return "-2147483648";
- // BEGIN Android-changed: Cache the String for small values.
- // int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
+ // Android-changed: cache the string literal for small values.
boolean negative = i < 0;
boolean small = negative ? i > -100 : i < 100;
if (small) {
@@ -430,12 +424,10 @@ public final class Integer extends Number implements Comparable<Integer> {
}
return smallValues[i];
}
+
int size = negative ? stringSize(-i) + 1 : stringSize(i);
- // END Android-changed: Cache the String for small values.
char[] buf = new char[size];
getChars(i, size, buf);
- // Android-changed: Use regular constructor instead of one which takes over "buf".
- // return new String(buf, true);
return new String(buf);
}
@@ -575,7 +567,6 @@ public final class Integer extends Number implements Comparable<Integer> {
*/
if (s == null) {
- // Android-changed: Improve exception message for parseInt.
throw new NumberFormatException("s == null");
}
diff --git a/java/lang/Long.java b/java/lang/Long.java
index c752957a..3f383b40 100644
--- a/java/lang/Long.java
+++ b/java/lang/Long.java
@@ -72,8 +72,6 @@ public final class Long extends Number implements Comparable<Long> {
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Long> TYPE = (Class<Long>) Class.getPrimitiveClass("long");
public static final Class<Long> TYPE = (Class<Long>) long[].class.getComponentType();
/**
@@ -359,8 +357,6 @@ public final class Long extends Number implements Comparable<Long> {
char[] buf = new char[chars];
formatUnsignedLong(val, shift, buf, 0, chars);
- // Android-changed: Use regular constructor instead of one which takes over "buf".
- // return new String(buf, true);
return new String(buf);
}
@@ -401,8 +397,6 @@ public final class Long extends Number implements Comparable<Long> {
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
- // Android-changed: Use regular constructor instead of one which takes over "buf".
- // return new String(buf, true);
return new String(buf);
}
diff --git a/java/lang/Short.java b/java/lang/Short.java
index 0f600a84..00317114 100644
--- a/java/lang/Short.java
+++ b/java/lang/Short.java
@@ -60,8 +60,6 @@ public final class Short extends Number implements Comparable<Short> {
* {@code short}.
*/
@SuppressWarnings("unchecked")
- // Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Short> TYPE = (Class<Short>) Class.getPrimitiveClass("short");
public static final Class<Short> TYPE = (Class<Short>) short[].class.getComponentType();
/**
diff --git a/java/lang/String.java b/java/lang/String.java
index 7e823479..4cefed81 100644
--- a/java/lang/String.java
+++ b/java/lang/String.java
@@ -543,10 +543,8 @@ public final class String
throw new UnsupportedOperationException("Use StringFactory instead.");
}
- // Android-removed: Unused package-private constructor String(char[] value, boolean share).
- // BEGIN Android-added: Constructor for internal use.
- // Not implemented in java as all calls are intercepted by the runtime.
+ // BEGIN Android-changed: Deprecated & unsupported as all calls are intercepted by the runtime.
/**
* Package private constructor
*
@@ -556,7 +554,7 @@ public final class String
String(int offset, int count, char[] value) {
throw new UnsupportedOperationException("Use StringFactory instead.");
}
- // END Android-added: Constructor for internal use.
+ // END Android-changed: Deprecated & unsupported as all calls are intercepted by the runtime.
/**
* Returns the length of this string.
@@ -1561,6 +1559,12 @@ public final class String
}
}
+ // BEGIN Android-added: Native method to access char storage managed by runtime.
+ // TODO(b/67411061): This seems to be unused, see whether we can remove it.
+ @FastNative
+ private native int fastIndexOf(int c, int start);
+ // END Android-added: Native method to access char storage managed by runtime.
+
/**
* Handles (rare) calls of indexOf with a supplementary character.
*/
diff --git a/java/lang/Void.java b/java/lang/Void.java
index 14268c78..8311ea8c 100644
--- a/java/lang/Void.java
+++ b/java/lang/Void.java
@@ -44,13 +44,24 @@ class Void {
* The {@code Class} object representing the pseudo-type corresponding to
* the keyword {@code void}.
*/
- // BEGIN Android-changed: Avoid use of removed Class.getPrimitiveClass method.
- // public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
public static final Class<Void> TYPE = lookupType();
+ // Android-changed: Upstream code would use reflection to establish the value of "void.class".
+ // ART makes a native call instead because the reflection approach could lead to initialization
+ // of TYPE with the current, i.e. uninitialized, value of TYPE due to other Android changes.
@dalvik.annotation.optimization.FastNative
private static native Class<Void> lookupType();
- // END Android-changed: Avoid use of removed Class.getPrimitiveClass method.
+ /*
+ @SuppressWarnings("unchecked")
+ private static Class<Void> lookupType() {
+ try {
+ Method method = Runnable.class.getMethod("run", EmptyArray.CLASS);
+ return (Class<Void>) method.getReturnType();
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ */
/*
* The Void class cannot be instantiated.
diff --git a/java/lang/invoke/ByteArrayVarHandle.java b/java/lang/invoke/ByteArrayVarHandle.java
deleted file mode 100644
index 4237058d..00000000
--- a/java/lang/invoke/ByteArrayVarHandle.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.lang.invoke;
-
-import java.nio.ByteOrder;
-
-/**
- * A VarHandle to access byte array elements as an array of primitive types.
- * @hide
- */
-final class ByteArrayVarHandle extends VarHandle {
- private ByteOrder byteOrder;
-
- private ByteArrayVarHandle(Class<?> arrayClass, ByteOrder byteOrder) {
- super(arrayClass.getComponentType(), byte[].class, false /* isFinal */,
- byte[].class, int.class);
- this.byteOrder = byteOrder;
- }
-
- static ByteArrayVarHandle create(Class<?> arrayClass, ByteOrder byteOrder) {
- return new ByteArrayVarHandle(arrayClass, byteOrder);
- }
-}
diff --git a/java/lang/invoke/ByteBufferViewVarHandle.java b/java/lang/invoke/ByteBufferViewVarHandle.java
deleted file mode 100644
index 74f3b0cb..00000000
--- a/java/lang/invoke/ByteBufferViewVarHandle.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.lang.invoke;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * A VarHandle to access byte array elements as an array of primitive types.
- * @hide
- */
-final class ByteBufferViewVarHandle extends VarHandle {
- private ByteOrder byteOrder;
-
- private ByteBufferViewVarHandle(Class<?> arrayClass, ByteOrder byteOrder) {
- super(arrayClass.getComponentType(), byte[].class, false /* isFinal */,
- ByteBuffer.class, int.class);
- this.byteOrder = byteOrder;
- }
-
- static ByteBufferViewVarHandle create(Class<?> arrayClass, ByteOrder byteOrder) {
- return new ByteBufferViewVarHandle(arrayClass, byteOrder);
- }
-}
diff --git a/java/lang/invoke/CallSite.java b/java/lang/invoke/CallSite.java
index 1ff1eb84..85b4bb9f 100644
--- a/java/lang/invoke/CallSite.java
+++ b/java/lang/invoke/CallSite.java
@@ -25,15 +25,363 @@
package java.lang.invoke;
+// Android-changed: Not using Empty
+//import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+
+/**
+ * A {@code CallSite} is a holder for a variable {@link MethodHandle},
+ * which is called its {@code target}.
+ * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
+ * all calls to the site's current target.
+ * A {@code CallSite} may be associated with several {@code invokedynamic}
+ * instructions, or it may be "free floating", associated with none.
+ * In any case, it may be invoked through an associated method handle
+ * called its {@linkplain #dynamicInvoker dynamic invoker}.
+ * <p>
+ * {@code CallSite} is an abstract class which does not allow
+ * direct subclassing by users. It has three immediate,
+ * concrete subclasses that may be either instantiated or subclassed.
+ * <ul>
+ * <li>If a mutable target is not required, an {@code invokedynamic} instruction
+ * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
+ * <li>If a mutable target is required which has volatile variable semantics,
+ * because updates to the target must be immediately and reliably witnessed by other threads,
+ * a {@linkplain VolatileCallSite volatile call site} may be used.
+ * <li>Otherwise, if a mutable target is required,
+ * a {@linkplain MutableCallSite mutable call site} may be used.
+ * </ul>
+ * <p>
+ * A non-constant call site may be <em>relinked</em> by changing its target.
+ * The new target must have the same {@linkplain MethodHandle#type() type}
+ * as the previous target.
+ * Thus, though a call site can be relinked to a series of
+ * successive targets, it cannot change its type.
+ * <p>
+ * Here is a sample use of call sites and bootstrap methods which links every
+ * dynamic call site to print its arguments:
+<blockquote><pre>{@code
+static void test() throws Throwable {
+ // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
+ InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
+}
+private static void printArgs(Object... args) {
+ System.out.println(java.util.Arrays.deepToString(args));
+}
+private static final MethodHandle printArgs;
+static {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Class thisClass = lookup.lookupClass(); // (who am I?)
+ printArgs = lookup.findStatic(thisClass,
+ "printArgs", MethodType.methodType(void.class, Object[].class));
+}
+private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
+ // ignore caller and name, but match the type:
+ return new ConstantCallSite(printArgs.asType(type));
+}
+}</pre></blockquote>
+ * @author John Rose, JSR 292 EG
+ */
abstract
public class CallSite {
+ // Android-changed: not used.
+ // static { MethodHandleImpl.initStatics(); }
+
+ // The actual payload of this call site:
+ /*package-private*/
+ MethodHandle target; // Note: This field is known to the JVM. Do not change.
+
+ /**
+ * Make a blank call site object with the given method type.
+ * An initial target method is supplied which will throw
+ * an {@link IllegalStateException} if called.
+ * <p>
+ * Before this {@code CallSite} object is returned from a bootstrap method,
+ * it is usually provided with a more useful target method,
+ * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @throws NullPointerException if the proposed type is null
+ */
+ /*package-private*/
+ CallSite(MethodType type) {
+ // Android-changed: No cache for these so create uninitializedCallSite target here using
+ // method handle transformations to create a method handle that has the expected method
+ // type but throws an IllegalStateException.
+ // target = makeUninitializedCallSite(type);
+ this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
+ this.target = MethodHandles.insertArguments(
+ this.target, 0, new IllegalStateException("uninitialized call site"));
+ if (type.parameterCount() > 0) {
+ this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
+ }
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param target the method handle which will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ /*package-private*/
+ CallSite(MethodHandle target) {
+ target.type(); // null check
+ this.target = target;
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
- public MethodType type() { return null; }
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param targetType the desired type of the call site
+ * @param createTargetHook a hook which will bind the call site to the target method handle
+ * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+ * or if the target returned by the hook is not of the given {@code targetType}
+ * @throws NullPointerException if the hook returns a null value
+ * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+ * @throws Throwable anything else thrown by the hook function
+ */
+ /*package-private*/
+ CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+ this(targetType);
+ ConstantCallSite selfCCS = (ConstantCallSite) this;
+ MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
+ checkTargetChange(this.target, boundTarget);
+ this.target = boundTarget;
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Returns the type of this call site's target.
+ * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
+ * The {@code setTarget} method enforces this invariant by refusing any new target that does
+ * not have the previous target's type.
+ * @return the type of the current target, which is also the type of any future target
+ */
+ public MethodType type() {
+ // warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
+ return target.type();
+ }
+
+ /**
+ * Returns the target method of the call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ *
+ * @return the current linkage state of the call site, its target method handle
+ * @see ConstantCallSite
+ * @see VolatileCallSite
+ * @see #setTarget
+ * @see ConstantCallSite#getTarget
+ * @see MutableCallSite#getTarget
+ * @see VolatileCallSite#getTarget
+ */
public abstract MethodHandle getTarget();
+ /**
+ * Updates the target method of this call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ * <p>
+ * The type of the new target must be {@linkplain MethodType#equals equal to}
+ * the type of the old target.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see CallSite#getTarget
+ * @see ConstantCallSite#setTarget
+ * @see MutableCallSite#setTarget
+ * @see VolatileCallSite#setTarget
+ */
public abstract void setTarget(MethodHandle newTarget);
+ void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
+ MethodType oldType = oldTarget.type();
+ MethodType newType = newTarget.type(); // null check!
+ if (!newType.equals(oldType))
+ throw wrongTargetType(newTarget, oldType);
+ }
+
+ private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+ return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
+ }
+
+ /**
+ * Produces a method handle equivalent to an invokedynamic instruction
+ * which has been linked to this call site.
+ * <p>
+ * This method is equivalent to the following code:
+ * <blockquote><pre>{@code
+ * MethodHandle getTarget, invoker, result;
+ * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+ * invoker = MethodHandles.exactInvoker(this.type());
+ * result = MethodHandles.foldArguments(invoker, getTarget)
+ * }</pre></blockquote>
+ *
+ * @return a method handle which always invokes this call site's current target
+ */
public abstract MethodHandle dynamicInvoker();
+ /*non-public*/ MethodHandle makeDynamicInvoker() {
+ // Android-changed: Use bindTo() rather than bindArgumentL() (not implemented).
+ MethodHandle getTarget = GET_TARGET.bindTo(this);
+ MethodHandle invoker = MethodHandles.exactInvoker(this.type());
+ return MethodHandles.foldArguments(invoker, getTarget);
+ }
+
+ // Android-changed: no longer final. GET_TARGET assigned in initializeGetTarget().
+ private static MethodHandle GET_TARGET = null;
+
+ private void initializeGetTarget() {
+ // Android-changed: moved from static initializer for
+ // GET_TARGET to avoid issues with running early. Called from
+ // constructors. CallSite creation is not performance critical.
+ synchronized (CallSite.class) {
+ if (GET_TARGET == null) {
+ try {
+ GET_TARGET = IMPL_LOOKUP.
+ findVirtual(CallSite.class, "getTarget",
+ MethodType.methodType(MethodHandle.class));
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ }
+ }
+ }
+
+ // Android-changed: not used.
+ // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
+ // /*package-private*/
+ // static Empty uninitializedCallSite() {
+ // throw new IllegalStateException("uninitialized call site");
+ // }
+
+ // unsafe stuff:
+ private static final long TARGET_OFFSET;
+ static {
+ try {
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ } catch (Exception ex) { throw new Error(ex); }
+ }
+
+ /*package-private*/
+ void setTargetNormal(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
+ target = newTarget;
+ }
+ /*package-private*/
+ MethodHandle getTargetVolatile() {
+ return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
+ }
+ /*package-private*/
+ void setTargetVolatile(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
+ UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
+ }
+
+ // Android-changed: not used.
+ // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
+ // static CallSite makeSite(MethodHandle bootstrapMethod,
+ // // Callee information:
+ // String name, MethodType type,
+ // // Extra arguments for BSM, if any:
+ // Object info,
+ // // Caller information:
+ // Class<?> callerClass) {
+ // MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+ // CallSite site;
+ // try {
+ // Object binding;
+ // info = maybeReBox(info);
+ // if (info == null) {
+ // binding = bootstrapMethod.invoke(caller, name, type);
+ // } else if (!info.getClass().isArray()) {
+ // binding = bootstrapMethod.invoke(caller, name, type, info);
+ // } else {
+ // Object[] argv = (Object[]) info;
+ // maybeReBoxElements(argv);
+ // switch (argv.length) {
+ // case 0:
+ // binding = bootstrapMethod.invoke(caller, name, type);
+ // break;
+ // case 1:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0]);
+ // break;
+ // case 2:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0], argv[1]);
+ // break;
+ // case 3:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0], argv[1], argv[2]);
+ // break;
+ // case 4:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0], argv[1], argv[2], argv[3]);
+ // break;
+ // case 5:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0], argv[1], argv[2], argv[3], argv[4]);
+ // break;
+ // case 6:
+ // binding = bootstrapMethod.invoke(caller, name, type,
+ // argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+ // break;
+ // default:
+ // final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
+ // if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
+ // throw new BootstrapMethodError("too many bootstrap method arguments");
+ // MethodType bsmType = bootstrapMethod.type();
+ // MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
+ // MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
+ // MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+ // binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv);
+ // }
+ // }
+ // //System.out.println("BSM for "+name+type+" => "+binding);
+ // if (binding instanceof CallSite) {
+ // site = (CallSite) binding;
+ // } else {
+ // throw new ClassCastException("bootstrap method failed to produce a CallSite");
+ // }
+ // if (!site.getTarget().type().equals(type))
+ // throw wrongTargetType(site.getTarget(), type);
+ // } catch (Throwable ex) {
+ // BootstrapMethodError bex;
+ // if (ex instanceof BootstrapMethodError)
+ // bex = (BootstrapMethodError) ex;
+ // else
+ // bex = new BootstrapMethodError("call site initialization exception", ex);
+ // throw bex;
+ // }
+ // return site;
+ // }
+
+ // private static Object maybeReBox(Object x) {
+ // if (x instanceof Integer) {
+ // int xi = (int) x;
+ // if (xi == (byte) xi)
+ // x = xi; // must rebox; see JLS 5.1.7
+ // }
+ // return x;
+ // }
+ // private static void maybeReBoxElements(Object[] xa) {
+ // for (int i = 0; i < xa.length; i++) {
+ // xa[i] = maybeReBox(xa[i]);
+ // }
+ // }
}
diff --git a/java/lang/invoke/FieldVarHandle.java b/java/lang/invoke/FieldVarHandle.java
deleted file mode 100644
index 0921040d..00000000
--- a/java/lang/invoke/FieldVarHandle.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.lang.invoke;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
-/**
- * A VarHandle that's associated with an ArtField.
- * @hide
- */
-final class FieldVarHandle extends VarHandle {
- private final long artField;
-
- private FieldVarHandle(Field field, Class<?> declaringClass) {
- super(field.getType(), Modifier.isFinal(field.getModifiers()), declaringClass);
- artField = field.getArtField();
- }
-
- private FieldVarHandle(Field field) {
- super(field.getType(), Modifier.isFinal(field.getModifiers()));
- artField = field.getArtField();
- }
-
- static FieldVarHandle create(Field field) {
- if (Modifier.isStatic(field.getModifiers())) {
- return new FieldVarHandle(field);
- } else {
- return new FieldVarHandle(field, field.getDeclaringClass());
- }
- }
-}
diff --git a/java/lang/invoke/MethodHandle.java b/java/lang/invoke/MethodHandle.java
index 159f9dd7..af3db103 100644
--- a/java/lang/invoke/MethodHandle.java
+++ b/java/lang/invoke/MethodHandle.java
@@ -25,28 +25,1353 @@
package java.lang.invoke;
+
+import dalvik.system.EmulatedStackFrame;
+
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/**
+ * A method handle is a typed, directly executable reference to an underlying method,
+ * constructor, field, or similar low-level operation, with optional
+ * transformations of arguments or return values.
+ * These transformations are quite general, and include such patterns as
+ * {@linkplain #asType conversion},
+ * {@linkplain #bindTo insertion},
+ * {@linkplain java.lang.invoke.MethodHandles#dropArguments deletion},
+ * and {@linkplain java.lang.invoke.MethodHandles#filterArguments substitution}.
+ *
+ * <h1>Method handle contents</h1>
+ * Method handles are dynamically and strongly typed according to their parameter and return types.
+ * They are not distinguished by the name or the defining class of their underlying methods.
+ * A method handle must be invoked using a symbolic type descriptor which matches
+ * the method handle's own {@linkplain #type type descriptor}.
+ * <p>
+ * Every method handle reports its type descriptor via the {@link #type type} accessor.
+ * This type descriptor is a {@link java.lang.invoke.MethodType MethodType} object,
+ * whose structure is a series of classes, one of which is
+ * the return type of the method (or {@code void.class} if none).
+ * <p>
+ * A method handle's type controls the types of invocations it accepts,
+ * and the kinds of transformations that apply to it.
+ * <p>
+ * A method handle contains a pair of special invoker methods
+ * called {@link #invokeExact invokeExact} and {@link #invoke invoke}.
+ * Both invoker methods provide direct access to the method handle's
+ * underlying method, constructor, field, or other operation,
+ * as modified by transformations of arguments and return values.
+ * Both invokers accept calls which exactly match the method handle's own type.
+ * The plain, inexact invoker also accepts a range of other call types.
+ * <p>
+ * Method handles are immutable and have no visible state.
+ * Of course, they can be bound to underlying methods or data which exhibit state.
+ * With respect to the Java Memory Model, any method handle will behave
+ * as if all of its (internal) fields are final variables. This means that any method
+ * handle made visible to the application will always be fully formed.
+ * This is true even if the method handle is published through a shared
+ * variable in a data race.
+ * <p>
+ * Method handles cannot be subclassed by the user.
+ * Implementations may (or may not) create internal subclasses of {@code MethodHandle}
+ * which may be visible via the {@link java.lang.Object#getClass Object.getClass}
+ * operation. The programmer should not draw conclusions about a method handle
+ * from its specific class, as the method handle class hierarchy (if any)
+ * may change from time to time or across implementations from different vendors.
+ *
+ * <h1>Method handle compilation</h1>
+ * A Java method call expression naming {@code invokeExact} or {@code invoke}
+ * can invoke a method handle from Java source code.
+ * From the viewpoint of source code, these methods can take any arguments
+ * and their result can be cast to any return type.
+ * Formally this is accomplished by giving the invoker methods
+ * {@code Object} return types and variable arity {@code Object} arguments,
+ * but they have an additional quality called <em>signature polymorphism</em>
+ * which connects this freedom of invocation directly to the JVM execution stack.
+ * <p>
+ * As is usual with virtual methods, source-level calls to {@code invokeExact}
+ * and {@code invoke} compile to an {@code invokevirtual} instruction.
+ * More unusually, the compiler must record the actual argument types,
+ * and may not perform method invocation conversions on the arguments.
+ * Instead, it must push them on the stack according to their own unconverted types.
+ * The method handle object itself is pushed on the stack before the arguments.
+ * The compiler then calls the method handle with a symbolic type descriptor which
+ * describes the argument and return types.
+ * <p>
+ * To issue a complete symbolic type descriptor, the compiler must also determine
+ * the return type. This is based on a cast on the method invocation expression,
+ * if there is one, or else {@code Object} if the invocation is an expression
+ * or else {@code void} if the invocation is a statement.
+ * The cast may be to a primitive type (but not {@code void}).
+ * <p>
+ * As a corner case, an uncasted {@code null} argument is given
+ * a symbolic type descriptor of {@code java.lang.Void}.
+ * The ambiguity with the type {@code Void} is harmless, since there are no references of type
+ * {@code Void} except the null reference.
+ *
+ * <h1>Method handle invocation</h1>
+ * The first time a {@code invokevirtual} instruction is executed
+ * it is linked, by symbolically resolving the names in the instruction
+ * and verifying that the method call is statically legal.
+ * This is true of calls to {@code invokeExact} and {@code invoke}.
+ * In this case, the symbolic type descriptor emitted by the compiler is checked for
+ * correct syntax and names it contains are resolved.
+ * Thus, an {@code invokevirtual} instruction which invokes
+ * a method handle will always link, as long
+ * as the symbolic type descriptor is syntactically well-formed
+ * and the types exist.
+ * <p>
+ * When the {@code invokevirtual} is executed after linking,
+ * the receiving method handle's type is first checked by the JVM
+ * to ensure that it matches the symbolic type descriptor.
+ * If the type match fails, it means that the method which the
+ * caller is invoking is not present on the individual
+ * method handle being invoked.
+ * <p>
+ * In the case of {@code invokeExact}, the type descriptor of the invocation
+ * (after resolving symbolic type names) must exactly match the method type
+ * of the receiving method handle.
+ * In the case of plain, inexact {@code invoke}, the resolved type descriptor
+ * must be a valid argument to the receiver's {@link #asType asType} method.
+ * Thus, plain {@code invoke} is more permissive than {@code invokeExact}.
+ * <p>
+ * After type matching, a call to {@code invokeExact} directly
+ * and immediately invoke the method handle's underlying method
+ * (or other behavior, as the case may be).
+ * <p>
+ * A call to plain {@code invoke} works the same as a call to
+ * {@code invokeExact}, if the symbolic type descriptor specified by the caller
+ * exactly matches the method handle's own type.
+ * If there is a type mismatch, {@code invoke} attempts
+ * to adjust the type of the receiving method handle,
+ * as if by a call to {@link #asType asType},
+ * to obtain an exactly invokable method handle {@code M2}.
+ * This allows a more powerful negotiation of method type
+ * between caller and callee.
+ * <p>
+ * (<em>Note:</em> The adjusted method handle {@code M2} is not directly observable,
+ * and implementations are therefore not required to materialize it.)
+ *
+ * <h1>Invocation checking</h1>
+ * In typical programs, method handle type matching will usually succeed.
+ * But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
+ * either directly (in the case of {@code invokeExact}) or indirectly as if
+ * by a failed call to {@code asType} (in the case of {@code invoke}).
+ * <p>
+ * Thus, a method type mismatch which might show up as a linkage error
+ * in a statically typed program can show up as
+ * a dynamic {@code WrongMethodTypeException}
+ * in a program which uses method handles.
+ * <p>
+ * Because method types contain "live" {@code Class} objects,
+ * method type matching takes into account both types names and class loaders.
+ * Thus, even if a method handle {@code M} is created in one
+ * class loader {@code L1} and used in another {@code L2},
+ * method handle calls are type-safe, because the caller's symbolic type
+ * descriptor, as resolved in {@code L2},
+ * is matched against the original callee method's symbolic type descriptor,
+ * as resolved in {@code L1}.
+ * The resolution in {@code L1} happens when {@code M} is created
+ * and its type is assigned, while the resolution in {@code L2} happens
+ * when the {@code invokevirtual} instruction is linked.
+ * <p>
+ * Apart from the checking of type descriptors,
+ * a method handle's capability to call its underlying method is unrestricted.
+ * If a method handle is formed on a non-public method by a class
+ * that has access to that method, the resulting handle can be used
+ * in any place by any caller who receives a reference to it.
+ * <p>
+ * Unlike with the Core Reflection API, where access is checked every time
+ * a reflective method is invoked,
+ * method handle access checking is performed
+ * <a href="MethodHandles.Lookup.html#access">when the method handle is created</a>.
+ * In the case of {@code ldc} (see below), access checking is performed as part of linking
+ * the constant pool entry underlying the constant method handle.
+ * <p>
+ * Thus, handles to non-public methods, or to methods in non-public classes,
+ * should generally be kept secret.
+ * They should not be passed to untrusted code unless their use from
+ * the untrusted code would be harmless.
+ *
+ * <h1>Method handle creation</h1>
+ * Java code can create a method handle that directly accesses
+ * any method, constructor, or field that is accessible to that code.
+ * This is done via a reflective, capability-based API called
+ * {@link java.lang.invoke.MethodHandles.Lookup MethodHandles.Lookup}
+ * For example, a static method handle can be obtained
+ * from {@link java.lang.invoke.MethodHandles.Lookup#findStatic Lookup.findStatic}.
+ * There are also conversion methods from Core Reflection API objects,
+ * such as {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}.
+ * <p>
+ * Like classes and strings, method handles that correspond to accessible
+ * fields, methods, and constructors can also be represented directly
+ * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
+ * A new type of constant pool entry, {@code CONSTANT_MethodHandle},
+ * refers directly to an associated {@code CONSTANT_Methodref},
+ * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
+ * constant pool entry.
+ * (For full details on method handle constants,
+ * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
+ * <p>
+ * Method handles produced by lookups or constant loads from methods or
+ * constructors with the variable arity modifier bit ({@code 0x0080})
+ * have a corresponding variable arity, as if they were defined with
+ * the help of {@link #asVarargsCollector asVarargsCollector}.
+ * <p>
+ * A method reference may refer either to a static or non-static method.
+ * In the non-static case, the method handle type includes an explicit
+ * receiver argument, prepended before any other arguments.
+ * In the method handle's type, the initial receiver argument is typed
+ * according to the class under which the method was initially requested.
+ * (E.g., if a non-static method handle is obtained via {@code ldc},
+ * the type of the receiver is the class named in the constant pool entry.)
+ * <p>
+ * Method handle constants are subject to the same link-time access checks
+ * their corresponding bytecode instructions, and the {@code ldc} instruction
+ * will throw corresponding linkage errors if the bytecode behaviors would
+ * throw such errors.
+ * <p>
+ * As a corollary of this, access to protected members is restricted
+ * to receivers only of the accessing class, or one of its subclasses,
+ * and the accessing class must in turn be a subclass (or package sibling)
+ * of the protected member's defining class.
+ * If a method reference refers to a protected non-static method or field
+ * of a class outside the current package, the receiver argument will
+ * be narrowed to the type of the accessing class.
+ * <p>
+ * When a method handle to a virtual method is invoked, the method is
+ * always looked up in the receiver (that is, the first argument).
+ * <p>
+ * A non-virtual method handle to a specific virtual method implementation
+ * can also be created. These do not perform virtual lookup based on
+ * receiver type. Such a method handle simulates the effect of
+ * an {@code invokespecial} instruction to the same method.
+ *
+ * <h1>Usage examples</h1>
+ * Here are some examples of usage:
+ * <blockquote><pre>{@code
+Object x, y; String s; int i;
+MethodType mt; MethodHandle mh;
+MethodHandles.Lookup lookup = MethodHandles.lookup();
+// mt is (char,char)String
+mt = MethodType.methodType(String.class, char.class, char.class);
+mh = lookup.findVirtual(String.class, "replace", mt);
+s = (String) mh.invokeExact("daddy",'d','n');
+// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
+assertEquals(s, "nanny");
+// weakly typed invocation (using MHs.invoke)
+s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
+assertEquals(s, "savvy");
+// mt is (Object[])List
+mt = MethodType.methodType(java.util.List.class, Object[].class);
+mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
+assert(mh.isVarargsCollector());
+x = mh.invoke("one", "two");
+// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+assertEquals(x, java.util.Arrays.asList("one","two"));
+// mt is (Object,Object,Object)Object
+mt = MethodType.genericMethodType(3);
+mh = mh.asType(mt);
+x = mh.invokeExact((Object)1, (Object)2, (Object)3);
+// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+assertEquals(x, java.util.Arrays.asList(1,2,3));
+// mt is ()int
+mt = MethodType.methodType(int.class);
+mh = lookup.findVirtual(java.util.List.class, "size", mt);
+i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
+// invokeExact(Ljava/util/List;)I
+assert(i == 3);
+mt = MethodType.methodType(void.class, String.class);
+mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
+mh.invokeExact(System.out, "Hello, world.");
+// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
+ * }</pre></blockquote>
+ * Each of the above calls to {@code invokeExact} or plain {@code invoke}
+ * generates a single invokevirtual instruction with
+ * the symbolic type descriptor indicated in the following comment.
+ * In these examples, the helper method {@code assertEquals} is assumed to
+ * be a method which calls {@link java.util.Objects#equals(Object,Object) Objects.equals}
+ * on its arguments, and asserts that the result is true.
+ *
+ * <h1>Exceptions</h1>
+ * The methods {@code invokeExact} and {@code invoke} are declared
+ * to throw {@link java.lang.Throwable Throwable},
+ * which is to say that there is no static restriction on what a method handle
+ * can throw. Since the JVM does not distinguish between checked
+ * and unchecked exceptions (other than by their class, of course),
+ * there is no particular effect on bytecode shape from ascribing
+ * checked exceptions to method handle invocations. But in Java source
+ * code, methods which perform method handle calls must either explicitly
+ * throw {@code Throwable}, or else must catch all
+ * throwables locally, rethrowing only those which are legal in the context,
+ * and wrapping ones which are illegal.
+ *
+ * <h1><a name="sigpoly"></a>Signature polymorphism</h1>
+ * The unusual compilation and linkage behavior of
+ * {@code invokeExact} and plain {@code invoke}
+ * is referenced by the term <em>signature polymorphism</em>.
+ * As defined in the Java Language Specification,
+ * a signature polymorphic method is one which can operate with
+ * any of a wide range of call signatures and return types.
+ * <p>
+ * In source code, a call to a signature polymorphic method will
+ * compile, regardless of the requested symbolic type descriptor.
+ * As usual, the Java compiler emits an {@code invokevirtual}
+ * instruction with the given symbolic type descriptor against the named method.
+ * The unusual part is that the symbolic type descriptor is derived from
+ * the actual argument and return types, not from the method declaration.
+ * <p>
+ * When the JVM processes bytecode containing signature polymorphic calls,
+ * it will successfully link any such call, regardless of its symbolic type descriptor.
+ * (In order to retain type safety, the JVM will guard such calls with suitable
+ * dynamic type checks, as described elsewhere.)
+ * <p>
+ * Bytecode generators, including the compiler back end, are required to emit
+ * untransformed symbolic type descriptors for these methods.
+ * Tools which determine symbolic linkage are required to accept such
+ * untransformed descriptors, without reporting linkage errors.
+ *
+ * <h1>Interoperation between method handles and the Core Reflection API</h1>
+ * Using factory methods in the {@link java.lang.invoke.MethodHandles.Lookup Lookup} API,
+ * any class member represented by a Core Reflection API object
+ * can be converted to a behaviorally equivalent method handle.
+ * For example, a reflective {@link java.lang.reflect.Method Method} can
+ * be converted to a method handle using
+ * {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}.
+ * The resulting method handles generally provide more direct and efficient
+ * access to the underlying class members.
+ * <p>
+ * As a special case,
+ * when the Core Reflection API is used to view the signature polymorphic
+ * methods {@code invokeExact} or plain {@code invoke} in this class,
+ * they appear as ordinary non-polymorphic methods.
+ * Their reflective appearance, as viewed by
+ * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
+ * is unaffected by their special status in this API.
+ * For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers}
+ * will report exactly those modifier bits required for any similarly
+ * declared method, including in this case {@code native} and {@code varargs} bits.
+ * <p>
+ * As with any reflected method, these methods (when reflected) may be
+ * invoked via {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.
+ * However, such reflective calls do not result in method handle invocations.
+ * Such a call, if passed the required argument
+ * (a single one, of type {@code Object[]}), will ignore the argument and
+ * will throw an {@code UnsupportedOperationException}.
+ * <p>
+ * Since {@code invokevirtual} instructions can natively
+ * invoke method handles under any symbolic type descriptor, this reflective view conflicts
+ * with the normal presentation of these methods via bytecodes.
+ * Thus, these two native methods, when reflectively viewed by
+ * {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
+ * <p>
+ * In order to obtain an invoker method for a particular type descriptor,
+ * use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker},
+ * or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}.
+ * The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
+ * API is also able to return a method handle
+ * to call {@code invokeExact} or plain {@code invoke},
+ * for any specified type descriptor .
+ *
+ * <h1>Interoperation between method handles and Java generics</h1>
+ * A method handle can be obtained on a method, constructor, or field
+ * which is declared with Java generic types.
+ * As with the Core Reflection API, the type of the method handle
+ * will constructed from the erasure of the source-level type.
+ * When a method handle is invoked, the types of its arguments
+ * or the return value cast type may be generic types or type instances.
+ * If this occurs, the compiler will replace those
+ * types by their erasures when it constructs the symbolic type descriptor
+ * for the {@code invokevirtual} instruction.
+ * <p>
+ * Method handles do not represent
+ * their function-like types in terms of Java parameterized (generic) types,
+ * because there are three mismatches between function-like types and parameterized
+ * Java types.
+ * <ul>
+ * <li>Method types range over all possible arities,
+ * from no arguments to up to the <a href="MethodHandle.html#maxarity">maximum number</a> of allowed arguments.
+ * Generics are not variadic, and so cannot represent this.</li>
+ * <li>Method types can specify arguments of primitive types,
+ * which Java generic types cannot range over.</li>
+ * <li>Higher order functions over method handles (combinators) are
+ * often generic across a wide range of function types, including
+ * those of multiple arities. It is impossible to represent such
+ * genericity with a Java type parameter.</li>
+ * </ul>
+ *
+ * <h1><a name="maxarity"></a>Arity limits</h1>
+ * The JVM imposes on all methods and constructors of any kind an absolute
+ * limit of 255 stacked arguments. This limit can appear more restrictive
+ * in certain cases:
+ * <ul>
+ * <li>A {@code long} or {@code double} argument counts (for purposes of arity limits) as two argument slots.
+ * <li>A non-static method consumes an extra argument for the object on which the method is called.
+ * <li>A constructor consumes an extra argument for the object which is being constructed.
+ * <li>Since a method handle&rsquo;s {@code invoke} method (or other signature-polymorphic method) is non-virtual,
+ * it consumes an extra argument for the method handle itself, in addition to any non-virtual receiver object.
+ * </ul>
+ * These limits imply that certain method handles cannot be created, solely because of the JVM limit on stacked arguments.
+ * For example, if a static JVM method accepts exactly 255 arguments, a method handle cannot be created for it.
+ * Attempts to create method handles with impossible method types lead to an {@link IllegalArgumentException}.
+ * In particular, a method handle&rsquo;s type must not have an arity of the exact maximum 255.
+ *
+ * @see MethodType
+ * @see MethodHandles
+ * @author John Rose, JSR 292 EG
+ */
public abstract class MethodHandle {
+ // Android-changed:
+ //
+ // static { MethodHandleImpl.initStatics(); }
+ //
+ // LambdaForm and customizationCount are currently unused in our implementation
+ // and will be substituted with appropriate implementation / delegate classes.
+ //
+ // /*private*/ final LambdaForm form;
+ // form is not private so that invokers can easily fetch it
+ // /*non-public*/ byte customizationCount;
+ // customizationCount should be accessible from invokers
+
+
+ /**
+ * Internal marker interface which distinguishes (to the Java compiler)
+ * those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
+ *
+ * @hide
+ */
+ @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+ public @interface PolymorphicSignature { }
+
+ /**
+ * The type of this method handle, this corresponds to the exact type of the method
+ * being invoked.
+ */
+ private final MethodType type;
+
+ /**
+ * The nominal type of this method handle, will be non-null if a method handle declares
+ * a different type from its "real" type, which is either the type of the method being invoked
+ * or the type of the emulated stackframe expected by an underyling adapter.
+ */
+ private MethodType nominalType;
+
+ /**
+ * The spread invoker associated with this type with zero trailing arguments.
+ * This is used to speed up invokeWithArguments.
+ */
+ private MethodHandle cachedSpreadInvoker;
+
+ /**
+ * The INVOKE* constants and SGET/SPUT and IGET/IPUT constants specify the behaviour of this
+ * method handle with respect to the ArtField* or the ArtMethod* that it operates on. These
+ * behaviours are equivalent to the dex bytecode behaviour on the respective method_id or
+ * field_id in the equivalent instruction.
+ *
+ * INVOKE_TRANSFORM is a special type of handle which doesn't encode any dex bytecode behaviour,
+ * instead it transforms the list of input arguments or performs other higher order operations
+ * before (optionally) delegating to another method handle.
+ *
+ * INVOKE_CALLSITE_TRANSFORM is a variation on INVOKE_TRANSFORM where the method type of
+ * a MethodHandle dynamically varies based on the callsite. This is used by
+ * the VarargsCollector implementation which places any number of trailing arguments
+ * into an array before invoking an arity method. The "any number of trailing arguments" means
+ * it would otherwise generate WrongMethodTypeExceptions as the callsite method type and
+ * VarargsCollector method type appear incompatible.
+ */
+
+ /** @hide */ public static final int INVOKE_VIRTUAL = 0;
+ /** @hide */ public static final int INVOKE_SUPER = 1;
+ /** @hide */ public static final int INVOKE_DIRECT = 2;
+ /** @hide */ public static final int INVOKE_STATIC = 3;
+ /** @hide */ public static final int INVOKE_INTERFACE = 4;
+ /** @hide */ public static final int INVOKE_TRANSFORM = 5;
+ /** @hide */ public static final int INVOKE_CALLSITE_TRANSFORM = 6;
+ /** @hide */ public static final int IGET = 7;
+ /** @hide */ public static final int IPUT = 8;
+ /** @hide */ public static final int SGET = 9;
+ /** @hide */ public static final int SPUT = 10;
+
+ // The kind of this method handle (used by the runtime). This is one of the INVOKE_*
+ // constants or SGET/SPUT, IGET/IPUT.
+ /** @hide */ protected final int handleKind;
+
+ // The ArtMethod* or ArtField* associated with this method handle (used by the runtime).
+ /** @hide */ protected final long artFieldOrMethod;
+
+ /** @hide */
+ protected MethodHandle(long artFieldOrMethod, int handleKind, MethodType type) {
+ this.artFieldOrMethod = artFieldOrMethod;
+ this.handleKind = handleKind;
+ this.type = type;
+ }
+
+ /**
+ * Reports the type of this method handle.
+ * Every invocation of this method handle via {@code invokeExact} must exactly match this type.
+ * @return the method handle type
+ */
+ public MethodType type() {
+ if (nominalType != null) {
+ return nominalType;
+ }
+
+ return type;
+ }
+
+ /**
+ * Invokes the method handle, allowing any caller type descriptor, but requiring an exact type match.
+ * The symbolic type descriptor at the call site of {@code invokeExact} must
+ * exactly match this method handle's {@link #type type}.
+ * No conversions are allowed on arguments or return values.
+ * <p>
+ * When this method is observed via the Core Reflection API,
+ * it will appear as a single native method, taking an object array and returning an object.
+ * If this native method is invoked directly via
+ * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
+ * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
+ * it will throw an {@code UnsupportedOperationException}.
+ * @param args the signature-polymorphic parameter list, statically represented using varargs
+ * @return the signature-polymorphic result, statically represented using {@code Object}
+ * @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor
+ * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
+ */
+ public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
+
+ /**
+ * Invokes the method handle, allowing any caller type descriptor,
+ * and optionally performing conversions on arguments and return values.
+ * <p>
+ * If the call site's symbolic type descriptor exactly matches this method handle's {@link #type type},
+ * the call proceeds as if by {@link #invokeExact invokeExact}.
+ * <p>
+ * Otherwise, the call proceeds as if this method handle were first
+ * adjusted by calling {@link #asType asType} to adjust this method handle
+ * to the required type, and then the call proceeds as if by
+ * {@link #invokeExact invokeExact} on the adjusted method handle.
+ * <p>
+ * There is no guarantee that the {@code asType} call is actually made.
+ * If the JVM can predict the results of making the call, it may perform
+ * adaptations directly on the caller's arguments,
+ * and call the target method handle according to its own exact type.
+ * <p>
+ * The resolved type descriptor at the call site of {@code invoke} must
+ * be a valid argument to the receivers {@code asType} method.
+ * In particular, the caller must specify the same argument arity
+ * as the callee's type,
+ * if the callee is not a {@linkplain #asVarargsCollector variable arity collector}.
+ * <p>
+ * When this method is observed via the Core Reflection API,
+ * it will appear as a single native method, taking an object array and returning an object.
+ * If this native method is invoked directly via
+ * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
+ * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
+ * it will throw an {@code UnsupportedOperationException}.
+ * @param args the signature-polymorphic parameter list, statically represented using varargs
+ * @return the signature-polymorphic result, statically represented using {@code Object}
+ * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's symbolic type descriptor
+ * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
+ * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
+ */
+ public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
+
+ // Android-changed: Removed implementation details.
+ //
+ // /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args)
+ // /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args)
+ // /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args)
+ // /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args)
+ // /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args)
+
+ /**
+ * Performs a variable arity invocation, passing the arguments in the given list
+ * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
+ * which mentions only the type {@code Object}, and whose arity is the length
+ * of the argument list.
+ * <p>
+ * Specifically, execution proceeds as if by the following steps,
+ * although the methods are not guaranteed to be called if the JVM
+ * can predict their effects.
+ * <ul>
+ * <li>Determine the length of the argument array as {@code N}.
+ * For a null reference, {@code N=0}. </li>
+ * <li>Determine the general type {@code TN} of {@code N} arguments as
+ * as {@code TN=MethodType.genericMethodType(N)}.</li>
+ * <li>Force the original target method handle {@code MH0} to the
+ * required type, as {@code MH1 = MH0.asType(TN)}. </li>
+ * <li>Spread the array into {@code N} separate arguments {@code A0, ...}. </li>
+ * <li>Invoke the type-adjusted method handle on the unpacked arguments:
+ * MH1.invokeExact(A0, ...). </li>
+ * <li>Take the return value as an {@code Object} reference. </li>
+ * </ul>
+ * <p>
+ * Because of the action of the {@code asType} step, the following argument
+ * conversions are applied as necessary:
+ * <ul>
+ * <li>reference casting
+ * <li>unboxing
+ * <li>widening primitive conversions
+ * </ul>
+ * <p>
+ * The result returned by the call is boxed if it is a primitive,
+ * or forced to null if the return type is void.
+ * <p>
+ * This call is equivalent to the following code:
+ * <blockquote><pre>{@code
+ * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
+ * Object result = invoker.invokeExact(this, arguments);
+ * }</pre></blockquote>
+ * <p>
+ * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
+ * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
+ * It can therefore be used as a bridge between native or reflective code and method handles.
+ *
+ * @param arguments the arguments to pass to the target
+ * @return the result returned by the target
+ * @throws ClassCastException if an argument cannot be converted by reference casting
+ * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
+ * @throws Throwable anything thrown by the target method invocation
+ * @see MethodHandles#spreadInvoker
+ */
+ public Object invokeWithArguments(Object... arguments) throws Throwable {
+ MethodHandle invoker = null;
+ synchronized (this) {
+ if (cachedSpreadInvoker == null) {
+ cachedSpreadInvoker = MethodHandles.spreadInvoker(this.type(), 0);
+ }
+
+ invoker = cachedSpreadInvoker;
+ }
+
+ return invoker.invoke(this, arguments);
+ }
+
+ /**
+ * Performs a variable arity invocation, passing the arguments in the given array
+ * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
+ * which mentions only the type {@code Object}, and whose arity is the length
+ * of the argument array.
+ * <p>
+ * This method is also equivalent to the following code:
+ * <blockquote><pre>{@code
+ * invokeWithArguments(arguments.toArray()
+ * }</pre></blockquote>
+ *
+ * @param arguments the arguments to pass to the target
+ * @return the result returned by the target
+ * @throws NullPointerException if {@code arguments} is a null reference
+ * @throws ClassCastException if an argument cannot be converted by reference casting
+ * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
+ * @throws Throwable anything thrown by the target method invocation
+ */
+ public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
+ return invokeWithArguments(arguments.toArray());
+ }
+
+ /**
+ * Produces an adapter method handle which adapts the type of the
+ * current method handle to a new type.
+ * The resulting method handle is guaranteed to report a type
+ * which is equal to the desired new type.
+ * <p>
+ * If the original type and new type are equal, returns {@code this}.
+ * <p>
+ * The new method handle, when invoked, will perform the following
+ * steps:
+ * <ul>
+ * <li>Convert the incoming argument list to match the original
+ * method handle's argument list.
+ * <li>Invoke the original method handle on the converted argument list.
+ * <li>Convert any result returned by the original method handle
+ * to the return type of new method handle.
+ * </ul>
+ * <p>
+ * This method provides the crucial behavioral difference between
+ * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}.
+ * The two methods
+ * perform the same steps when the caller's type descriptor exactly m atches
+ * the callee's, but when the types differ, plain {@link #invoke invoke}
+ * also calls {@code asType} (or some internal equivalent) in order
+ * to match up the caller's and callee's types.
+ * <p>
+ * If the current method is a variable arity method handle
+ * argument list conversion may involve the conversion and collection
+ * of several arguments into an array, as
+ * {@linkplain #asVarargsCollector described elsewhere}.
+ * In every other case, all conversions are applied <em>pairwise</em>,
+ * which means that each argument or return value is converted to
+ * exactly one argument or return value (or no return value).
+ * The applied conversions are defined by consulting the
+ * the corresponding component types of the old and new
+ * method handle types.
+ * <p>
+ * Let <em>T0</em> and <em>T1</em> be corresponding new and old parameter types,
+ * or old and new return types. Specifically, for some valid index {@code i}, let
+ * <em>T0</em>{@code =newType.parameterType(i)} and <em>T1</em>{@code =this.type().parameterType(i)}.
+ * Or else, going the other way for return values, let
+ * <em>T0</em>{@code =this.type().returnType()} and <em>T1</em>{@code =newType.returnType()}.
+ * If the types are the same, the new method handle makes no change
+ * to the corresponding argument or return value (if any).
+ * Otherwise, one of the following conversions is applied
+ * if possible:
+ * <ul>
+ * <li>If <em>T0</em> and <em>T1</em> are references, then a cast to <em>T1</em> is applied.
+ * (The types do not need to be related in any particular way.
+ * This is because a dynamic value of null can convert to any reference type.)
+ * <li>If <em>T0</em> and <em>T1</em> are primitives, then a Java method invocation
+ * conversion (JLS 5.3) is applied, if one exists.
+ * (Specifically, <em>T0</em> must convert to <em>T1</em> by a widening primitive conversion.)
+ * <li>If <em>T0</em> is a primitive and <em>T1</em> a reference,
+ * a Java casting conversion (JLS 5.5) is applied if one exists.
+ * (Specifically, the value is boxed from <em>T0</em> to its wrapper class,
+ * which is then widened as needed to <em>T1</em>.)
+ * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
+ * conversion will be applied at runtime, possibly followed
+ * by a Java method invocation conversion (JLS 5.3)
+ * on the primitive value. (These are the primitive widening conversions.)
+ * <em>T0</em> must be a wrapper class or a supertype of one.
+ * (In the case where <em>T0</em> is Object, these are the conversions
+ * allowed by {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.)
+ * The unboxing conversion must have a possibility of success, which means that
+ * if <em>T0</em> is not itself a wrapper class, there must exist at least one
+ * wrapper class <em>TW</em> which is a subtype of <em>T0</em> and whose unboxed
+ * primitive value can be widened to <em>T1</em>.
+ * <li>If the return type <em>T1</em> is marked as void, any returned value is discarded
+ * <li>If the return type <em>T0</em> is void and <em>T1</em> a reference, a null value is introduced.
+ * <li>If the return type <em>T0</em> is void and <em>T1</em> a primitive,
+ * a zero value is introduced.
+ * </ul>
+ * (<em>Note:</em> Both <em>T0</em> and <em>T1</em> may be regarded as static types,
+ * because neither corresponds specifically to the <em>dynamic type</em> of any
+ * actual argument or return value.)
+ * <p>
+ * The method handle conversion cannot be made if any one of the required
+ * pairwise conversions cannot be made.
+ * <p>
+ * At runtime, the conversions applied to reference arguments
+ * or return values may require additional runtime checks which can fail.
+ * An unboxing operation may fail because the original reference is null,
+ * causing a {@link java.lang.NullPointerException NullPointerException}.
+ * An unboxing operation or a reference cast may also fail on a reference
+ * to an object of the wrong type,
+ * causing a {@link java.lang.ClassCastException ClassCastException}.
+ * Although an unboxing operation may accept several kinds of wrappers,
+ * if none are available, a {@code ClassCastException} will be thrown.
+ *
+ * @param newType the expected type of the new method handle
+ * @return a method handle which delegates to {@code this} after performing
+ * any necessary argument conversions, and arranges for any
+ * necessary return value conversions
+ * @throws NullPointerException if {@code newType} is a null reference
+ * @throws WrongMethodTypeException if the conversion cannot be made
+ * @see MethodHandles#explicitCastArguments
+ */
+ public MethodHandle asType(MethodType newType) {
+ // Fast path alternative to a heavyweight {@code asType} call.
+ // Return 'this' if the conversion will be a no-op.
+ if (newType == type) {
+ return this;
+ }
+
+ if (!type.isConvertibleTo(newType)) {
+ throw new WrongMethodTypeException("cannot convert " + this + " to " + newType);
+ }
+
+ MethodHandle mh = duplicate();
+ mh.nominalType = newType;
+ return mh;
+ }
+
+ /**
+ * Makes an <em>array-spreading</em> method handle, which accepts a trailing array argument
+ * and spreads its elements as positional arguments.
+ * The new method handle adapts, as its <i>target</i>,
+ * the current method handle. The type of the adapter will be
+ * the same as the type of the target, except that the final
+ * {@code arrayLength} parameters of the target's type are replaced
+ * by a single array parameter of type {@code arrayType}.
+ * <p>
+ * If the array element type differs from any of the corresponding
+ * argument types on the original target,
+ * the original target is adapted to take the array elements directly,
+ * as if by a call to {@link #asType asType}.
+ * <p>
+ * When called, the adapter replaces a trailing array argument
+ * by the array's elements, each as its own argument to the target.
+ * (The order of the arguments is preserved.)
+ * They are converted pairwise by casting and/or unboxing
+ * to the types of the trailing parameters of the target.
+ * Finally the target is called.
+ * What the target eventually returns is returned unchanged by the adapter.
+ * <p>
+ * Before calling the target, the adapter verifies that the array
+ * contains exactly enough elements to provide a correct argument count
+ * to the target method handle.
+ * (The array may also be null when zero elements are required.)
+ * <p>
+ * If, when the adapter is called, the supplied array argument does
+ * not have the correct number of elements, the adapter will throw
+ * an {@link IllegalArgumentException} instead of invoking the target.
+ * <p>
+ * Here are some simple examples of array-spreading method handles:
+ * <blockquote><pre>{@code
+MethodHandle equals = publicLookup()
+ .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
+assert( (boolean) equals.invokeExact("me", (Object)"me"));
+assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
+// spread both arguments from a 2-array:
+MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
+assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
+assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
+// try to spread from anything but a 2-array:
+for (int n = 0; n <= 10; n++) {
+ Object[] badArityArgs = (n == 2 ? null : new Object[n]);
+ try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
+ catch (IllegalArgumentException ex) { } // OK
+}
+// spread both arguments from a String array:
+MethodHandle eq2s = equals.asSpreader(String[].class, 2);
+assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
+assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
+// spread second arguments from a 1-array:
+MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
+assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
+assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
+// spread no arguments from a 0-array or null:
+MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
+assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
+assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
+// asSpreader and asCollector are approximate inverses:
+for (int n = 0; n <= 2; n++) {
+ for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
+ MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
+ assert( (boolean) equals2.invokeWithArguments("me", "me"));
+ assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
+ }
+}
+MethodHandle caToString = publicLookup()
+ .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
+assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
+MethodHandle caString3 = caToString.asCollector(char[].class, 3);
+assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
+MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
+assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
+ * }</pre></blockquote>
+ * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
+ * @param arrayLength the number of arguments to spread from an incoming array argument
+ * @return a new method handle which spreads its final array argument,
+ * before calling the original method handle
+ * @throws NullPointerException if {@code arrayType} is a null reference
+ * @throws IllegalArgumentException if {@code arrayType} is not an array type,
+ * or if target does not have at least
+ * {@code arrayLength} parameter types,
+ * or if {@code arrayLength} is negative,
+ * or if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ * @throws WrongMethodTypeException if the implied {@code asType} call fails
+ * @see #asCollector
+ */
+ public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
+ MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
+
+ final int targetParamCount = postSpreadType.parameterCount();
+ MethodType dropArrayArgs = postSpreadType.dropParameterTypes(
+ (targetParamCount - arrayLength), targetParamCount);
+ MethodType adapterType = dropArrayArgs.appendParameterTypes(arrayType);
+
+ return new Transformers.Spreader(this, adapterType, arrayLength);
+ }
+
+ /**
+ * See if {@code asSpreader} can be validly called with the given arguments.
+ * Return the type of the method handle call after spreading but before conversions.
+ */
+ private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+ spreadArrayChecks(arrayType, arrayLength);
+ int nargs = type().parameterCount();
+ if (nargs < arrayLength || arrayLength < 0)
+ throw newIllegalArgumentException("bad spread array length");
+ Class<?> arrayElement = arrayType.getComponentType();
+ MethodType mtype = type();
+ boolean match = true, fail = false;
+ for (int i = nargs - arrayLength; i < nargs; i++) {
+ Class<?> ptype = mtype.parameterType(i);
+ if (ptype != arrayElement) {
+ match = false;
+ if (!MethodType.canConvert(arrayElement, ptype)) {
+ fail = true;
+ break;
+ }
+ }
+ }
+ if (match) return mtype;
+ MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
+ if (!fail) return needType;
+ // elicit an error:
+ this.asType(needType);
+ throw newInternalError("should not return", null);
+ }
+
+ private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
+ Class<?> arrayElement = arrayType.getComponentType();
+ if (arrayElement == null)
+ throw newIllegalArgumentException("not an array type", arrayType);
+ if ((arrayLength & 0x7F) != arrayLength) {
+ if ((arrayLength & 0xFF) != arrayLength)
+ throw newIllegalArgumentException("array length is not legal", arrayLength);
+ assert(arrayLength >= 128);
+ if (arrayElement == long.class ||
+ arrayElement == double.class)
+ throw newIllegalArgumentException("array length is not legal for long[] or double[]", arrayLength);
+ }
+ }
+
+ /**
+ * Makes an <em>array-collecting</em> method handle, which accepts a given number of trailing
+ * positional arguments and collects them into an array argument.
+ * The new method handle adapts, as its <i>target</i>,
+ * the current method handle. The type of the adapter will be
+ * the same as the type of the target, except that a single trailing
+ * parameter (usually of type {@code arrayType}) is replaced by
+ * {@code arrayLength} parameters whose type is element type of {@code arrayType}.
+ * <p>
+ * If the array type differs from the final argument type on the original target,
+ * the original target is adapted to take the array type directly,
+ * as if by a call to {@link #asType asType}.
+ * <p>
+ * When called, the adapter replaces its trailing {@code arrayLength}
+ * arguments by a single new array of type {@code arrayType}, whose elements
+ * comprise (in order) the replaced arguments.
+ * Finally the target is called.
+ * What the target eventually returns is returned unchanged by the adapter.
+ * <p>
+ * (The array may also be a shared constant when {@code arrayLength} is zero.)
+ * <p>
+ * (<em>Note:</em> The {@code arrayType} is often identical to the last
+ * parameter type of the original target.
+ * It is an explicit argument for symmetry with {@code asSpreader}, and also
+ * to allow the target to use a simple {@code Object} as its last parameter type.)
+ * <p>
+ * In order to create a collecting adapter which is not restricted to a particular
+ * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead.
+ * <p>
+ * Here are some examples of array-collecting method handles:
+ * <blockquote><pre>{@code
+MethodHandle deepToString = publicLookup()
+ .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"}));
+MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
+assertEquals(methodType(String.class, Object.class), ts1.type());
+//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL
+assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
+// arrayType can be a subtype of Object[]
+MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
+assertEquals(methodType(String.class, String.class, String.class), ts2.type());
+assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
+MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
+assertEquals("[]", (String) ts0.invokeExact());
+// collectors can be nested, Lisp-style
+MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
+assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
+// arrayType can be any primitive array type
+MethodHandle bytesToString = publicLookup()
+ .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
+ .asCollector(byte[].class, 3);
+assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
+MethodHandle longsToString = publicLookup()
+ .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
+ .asCollector(long[].class, 1);
+assertEquals("[123]", (String) longsToString.invokeExact((long)123));
+ * }</pre></blockquote>
+ * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
+ * @param arrayLength the number of arguments to collect into a new array argument
+ * @return a new method handle which collects some trailing argument
+ * into an array, before calling the original method handle
+ * @throws NullPointerException if {@code arrayType} is a null reference
+ * @throws IllegalArgumentException if {@code arrayType} is not an array type
+ * or {@code arrayType} is not assignable to this method handle's trailing parameter type,
+ * or {@code arrayLength} is not a legal array size,
+ * or the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ * @throws WrongMethodTypeException if the implied {@code asType} call fails
+ * @see #asSpreader
+ * @see #asVarargsCollector
+ */
+ public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+ asCollectorChecks(arrayType, arrayLength);
+
+ return new Transformers.Collector(this, arrayType, arrayLength);
+ }
+
+ /**
+ * See if {@code asCollector} can be validly called with the given arguments.
+ * Return false if the last parameter is not an exact match to arrayType.
+ */
+ /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
+ spreadArrayChecks(arrayType, arrayLength);
+ int nargs = type().parameterCount();
+ if (nargs != 0) {
+ Class<?> lastParam = type().parameterType(nargs-1);
+ if (lastParam == arrayType) return true;
+ if (lastParam.isAssignableFrom(arrayType)) return false;
+ }
+ throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
+ }
+
+ /**
+ * Makes a <em>variable arity</em> adapter which is able to accept
+ * any number of trailing positional arguments and collect them
+ * into an array argument.
+ * <p>
+ * The type and behavior of the adapter will be the same as
+ * the type and behavior of the target, except that certain
+ * {@code invoke} and {@code asType} requests can lead to
+ * trailing positional arguments being collected into target's
+ * trailing parameter.
+ * Also, the last parameter type of the adapter will be
+ * {@code arrayType}, even if the target has a different
+ * last parameter type.
+ * <p>
+ * This transformation may return {@code this} if the method handle is
+ * already of variable arity and its trailing parameter type
+ * is identical to {@code arrayType}.
+ * <p>
+ * When called with {@link #invokeExact invokeExact}, the adapter invokes
+ * the target with no argument changes.
+ * (<em>Note:</em> This behavior is different from a
+ * {@linkplain #asCollector fixed arity collector},
+ * since it accepts a whole array of indeterminate length,
+ * rather than a fixed number of arguments.)
+ * <p>
+ * When called with plain, inexact {@link #invoke invoke}, if the caller
+ * type is the same as the adapter, the adapter invokes the target as with
+ * {@code invokeExact}.
+ * (This is the normal behavior for {@code invoke} when types match.)
+ * <p>
+ * Otherwise, if the caller and adapter arity are the same, and the
+ * trailing parameter type of the caller is a reference type identical to
+ * or assignable to the trailing parameter type of the adapter,
+ * the arguments and return values are converted pairwise,
+ * as if by {@link #asType asType} on a fixed arity
+ * method handle.
+ * <p>
+ * Otherwise, the arities differ, or the adapter's trailing parameter
+ * type is not assignable from the corresponding caller type.
+ * In this case, the adapter replaces all trailing arguments from
+ * the original trailing argument position onward, by
+ * a new array of type {@code arrayType}, whose elements
+ * comprise (in order) the replaced arguments.
+ * <p>
+ * The caller type must provides as least enough arguments,
+ * and of the correct type, to satisfy the target's requirement for
+ * positional arguments before the trailing array argument.
+ * Thus, the caller must supply, at a minimum, {@code N-1} arguments,
+ * where {@code N} is the arity of the target.
+ * Also, there must exist conversions from the incoming arguments
+ * to the target's arguments.
+ * As with other uses of plain {@code invoke}, if these basic
+ * requirements are not fulfilled, a {@code WrongMethodTypeException}
+ * may be thrown.
+ * <p>
+ * In all cases, what the target eventually returns is returned unchanged by the adapter.
+ * <p>
+ * In the final case, it is exactly as if the target method handle were
+ * temporarily adapted with a {@linkplain #asCollector fixed arity collector}
+ * to the arity required by the caller type.
+ * (As with {@code asCollector}, if the array length is zero,
+ * a shared constant may be used instead of a new array.
+ * If the implied call to {@code asCollector} would throw
+ * an {@code IllegalArgumentException} or {@code WrongMethodTypeException},
+ * the call to the variable arity adapter must throw
+ * {@code WrongMethodTypeException}.)
+ * <p>
+ * The behavior of {@link #asType asType} is also specialized for
+ * variable arity adapters, to maintain the invariant that
+ * plain, inexact {@code invoke} is always equivalent to an {@code asType}
+ * call to adjust the target type, followed by {@code invokeExact}.
+ * Therefore, a variable arity adapter responds
+ * to an {@code asType} request by building a fixed arity collector,
+ * if and only if the adapter and requested type differ either
+ * in arity or trailing argument type.
+ * The resulting fixed arity collector has its type further adjusted
+ * (if necessary) to the requested type by pairwise conversion,
+ * as if by another application of {@code asType}.
+ * <p>
+ * When a method handle is obtained by executing an {@code ldc} instruction
+ * of a {@code CONSTANT_MethodHandle} constant, and the target method is marked
+ * as a variable arity method (with the modifier bit {@code 0x0080}),
+ * the method handle will accept multiple arities, as if the method handle
+ * constant were created by means of a call to {@code asVarargsCollector}.
+ * <p>
+ * In order to create a collecting adapter which collects a predetermined
+ * number of arguments, and whose type reflects this predetermined number,
+ * use {@link #asCollector asCollector} instead.
+ * <p>
+ * No method handle transformations produce new method handles with
+ * variable arity, unless they are documented as doing so.
+ * Therefore, besides {@code asVarargsCollector},
+ * all methods in {@code MethodHandle} and {@code MethodHandles}
+ * will return a method handle with fixed arity,
+ * except in the cases where they are specified to return their original
+ * operand (e.g., {@code asType} of the method handle's own type).
+ * <p>
+ * Calling {@code asVarargsCollector} on a method handle which is already
+ * of variable arity will produce a method handle with the same type and behavior.
+ * It may (or may not) return the original variable arity method handle.
+ * <p>
+ * Here is an example, of a list-making variable arity method handle:
+ * <blockquote><pre>{@code
+MethodHandle deepToString = publicLookup()
+ .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
+assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"}));
+assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"}));
+assertEquals("[won]", (String) ts1.invoke( "won" ));
+assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
+// findStatic of Arrays.asList(...) produces a variable arity method handle:
+MethodHandle asList = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
+assertEquals(methodType(List.class, Object[].class), asList.type());
+assert(asList.isVarargsCollector());
+assertEquals("[]", asList.invoke().toString());
+assertEquals("[1]", asList.invoke(1).toString());
+assertEquals("[two, too]", asList.invoke("two", "too").toString());
+String[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
+List ls = (List) asList.invoke((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+ * }</pre></blockquote>
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * These rules are designed as a dynamically-typed variation
+ * of the Java rules for variable arity methods.
+ * In both cases, callers to a variable arity method or method handle
+ * can either pass zero or more positional arguments, or else pass
+ * pre-collected arrays of any length. Users should be aware of the
+ * special role of the final argument, and of the effect of a
+ * type match on that final argument, which determines whether
+ * or not a single trailing argument is interpreted as a whole
+ * array or a single element of an array to be collected.
+ * Note that the dynamic type of the trailing argument has no
+ * effect on this decision, only a comparison between the symbolic
+ * type descriptor of the call site and the type descriptor of the method handle.)
+ *
+ * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
+ * @return a new method handle which can collect any number of trailing arguments
+ * into an array, before calling the original method handle
+ * @throws NullPointerException if {@code arrayType} is a null reference
+ * @throws IllegalArgumentException if {@code arrayType} is not an array type
+ * or {@code arrayType} is not assignable to this method handle's trailing parameter type
+ * @see #asCollector
+ * @see #isVarargsCollector
+ * @see #asFixedArity
+ */
+ public MethodHandle asVarargsCollector(Class<?> arrayType) {
+ arrayType.getClass(); // explicit NPE
+ boolean lastMatch = asCollectorChecks(arrayType, 0);
+ if (isVarargsCollector() && lastMatch)
+ return this;
- public MethodType type() { return null; }
+ return new Transformers.VarargsCollector(this);
+ }
- public final Object invokeExact(Object... args) throws Throwable { return null; }
+ /**
+ * Determines if this method handle
+ * supports {@linkplain #asVarargsCollector variable arity} calls.
+ * Such method handles arise from the following sources:
+ * <ul>
+ * <li>a call to {@linkplain #asVarargsCollector asVarargsCollector}
+ * <li>a call to a {@linkplain java.lang.invoke.MethodHandles.Lookup lookup method}
+ * which resolves to a variable arity Java method or constructor
+ * <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
+ * which resolves to a variable arity Java method or constructor
+ * </ul>
+ * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls
+ * @see #asVarargsCollector
+ * @see #asFixedArity
+ */
+ public boolean isVarargsCollector() {
+ return false;
+ }
- public final Object invoke(Object... args) throws Throwable { return null; }
+ /**
+ * Makes a <em>fixed arity</em> method handle which is otherwise
+ * equivalent to the current method handle.
+ * <p>
+ * If the current method handle is not of
+ * {@linkplain #asVarargsCollector variable arity},
+ * the current method handle is returned.
+ * This is true even if the current method handle
+ * could not be a valid input to {@code asVarargsCollector}.
+ * <p>
+ * Otherwise, the resulting fixed-arity method handle has the same
+ * type and behavior of the current method handle,
+ * except that {@link #isVarargsCollector isVarargsCollector}
+ * will be false.
+ * The fixed-arity method handle may (or may not) be the
+ * a previous argument to {@code asVarargsCollector}.
+ * <p>
+ * Here is an example, of a list-making variable arity method handle:
+ * <blockquote><pre>{@code
+MethodHandle asListVar = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+ .asVarargsCollector(Object[].class);
+MethodHandle asListFix = asListVar.asFixedArity();
+assertEquals("[1]", asListVar.invoke(1).toString());
+Exception caught = null;
+try { asListFix.invoke((Object)1); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof ClassCastException);
+assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
+try { asListFix.invoke("two", "too"); }
+catch (Exception ex) { caught = ex; }
+assert(caught instanceof WrongMethodTypeException);
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
+assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
+assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
+assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
+ * }</pre></blockquote>
+ *
+ * @return a new method handle which accepts only a fixed number of arguments
+ * @see #asVarargsCollector
+ * @see #isVarargsCollector
+ */
+ public MethodHandle asFixedArity() {
+ // Android-changed: implementation specific.
+ MethodHandle mh = this;
+ if (mh.isVarargsCollector()) {
+ mh = ((Transformers.VarargsCollector) mh).asFixedArity();
+ }
+ assert(!mh.isVarargsCollector());
+ return mh;
+ }
- public Object invokeWithArguments(Object... arguments) throws Throwable { return null; }
+ /**
+ * Binds a value {@code x} to the first argument of a method handle, without invoking it.
+ * The new method handle adapts, as its <i>target</i>,
+ * the current method handle by binding it to the given argument.
+ * The type of the bound handle will be
+ * the same as the type of the target, except that a single leading
+ * reference parameter will be omitted.
+ * <p>
+ * When called, the bound handle inserts the given value {@code x}
+ * as a new leading argument to the target. The other arguments are
+ * also passed unchanged.
+ * What the target eventually returns is returned unchanged by the bound handle.
+ * <p>
+ * The reference {@code x} must be convertible to the first parameter
+ * type of the target.
+ * <p>
+ * (<em>Note:</em> Because method handles are immutable, the target method handle
+ * retains its original type and behavior.)
+ * @param x the value to bind to the first argument of the target
+ * @return a new method handle which prepends the given value to the incoming
+ * argument list, before calling the original method handle
+ * @throws IllegalArgumentException if the target does not have a
+ * leading parameter type that is a reference type
+ * @throws ClassCastException if {@code x} cannot be converted
+ * to the leading parameter type of the target
+ * @see MethodHandles#insertArguments
+ */
+ public MethodHandle bindTo(Object x) {
+ x = type.leadingReferenceParameter().cast(x); // throw CCE if needed
- public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable { return null; }
+ return new Transformers.BindTo(this, x);
+ }
- public MethodHandle asType(MethodType newType) { return null; }
+ /**
+ * Returns a string representation of the method handle,
+ * starting with the string {@code "MethodHandle"} and
+ * ending with the string representation of the method handle's type.
+ * In other words, this method returns a string equal to the value of:
+ * <blockquote><pre>{@code
+ * "MethodHandle" + type().toString()
+ * }</pre></blockquote>
+ * <p>
+ * (<em>Note:</em> Future releases of this API may add further information
+ * to the string representation.
+ * Therefore, the present syntax should not be parsed by applications.)
+ *
+ * @return a string representation of the method handle
+ */
+ @Override
+ public String toString() {
+ // Android-changed: Removed debugging support.
+ return "MethodHandle"+type;
+ }
- public MethodHandle asCollector(Class<?> arrayType, int arrayLength) { return null; }
+ /** @hide */
+ public int getHandleKind() {
+ return handleKind;
+ }
- public MethodHandle asVarargsCollector(Class<?> arrayType) { return null; }
+ /** @hide */
+ protected void transform(EmulatedStackFrame arguments) throws Throwable {
+ throw new AssertionError("MethodHandle.transform should never be called.");
+ }
- public boolean isVarargsCollector() { return false; }
+ /**
+ * Creates a copy of this method handle, copying all relevant data.
+ *
+ * @hide
+ */
+ protected MethodHandle duplicate() {
+ try {
+ return (MethodHandle) this.clone();
+ } catch (CloneNotSupportedException cnse) {
+ throw new AssertionError("Subclass of Transformer is not cloneable");
+ }
+ }
- public MethodHandle asFixedArity() { return null; }
- public MethodHandle bindTo(Object x) { return null; }
+ /**
+ * This is the entry point for all transform calls, and dispatches to the protected
+ * transform method. This layer of indirection exists purely for convenience, because
+ * we can invoke-direct on a fixed ArtMethod for all transform variants.
+ *
+ * NOTE: If this extra layer of indirection proves to be a problem, we can get rid
+ * of this layer of indirection at the cost of some additional ugliness.
+ */
+ private void transformInternal(EmulatedStackFrame arguments) throws Throwable {
+ transform(arguments);
+ }
+ // Android-changed: Removed implementation details :
+ //
+ // String standardString();
+ // String debugString();
+ //
+ //// Implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+ //
+ // Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch
+ //
+ // BoundMethodHandle bindArgumentL(int pos, Object value);
+ // /*non-public*/ MethodHandle setVarargs(MemberName member);
+ // /*non-public*/ MethodHandle viewAsType(MethodType newType, boolean strict);
+ // /*non-public*/ boolean viewAsTypeChecks(MethodType newType, boolean strict);
+ //
+ // Decoding
+ //
+ // /*non-public*/ LambdaForm internalForm();
+ // /*non-public*/ MemberName internalMemberName();
+ // /*non-public*/ Class<?> internalCallerClass();
+ // /*non-public*/ MethodHandleImpl.Intrinsic intrinsicName();
+ // /*non-public*/ MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial);
+ // /*non-public*/ boolean isInvokeSpecial();
+ // /*non-public*/ Object internalValues();
+ // /*non-public*/ Object internalProperties();
+ //
+ //// Method handle implementation methods.
+ //// Sub-classes can override these default implementations.
+ //// All these methods assume arguments are already validated.
+ //
+ // /*non-public*/ abstract MethodHandle copyWith(MethodType mt, LambdaForm lf);
+ // abstract BoundMethodHandle rebind();
+ // /*non-public*/ void updateForm(LambdaForm newForm);
+ // /*non-public*/ void customize();
+ // private static final long FORM_OFFSET;
}
diff --git a/java/lang/invoke/MethodHandles.java b/java/lang/invoke/MethodHandles.java
index f27ad988..88ce6e08 100644
--- a/java/lang/invoke/MethodHandles.java
+++ b/java/lang/invoke/MethodHandles.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,129 +25,3410 @@
package java.lang.invoke;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
+import java.lang.reflect.*;
+import java.nio.ByteOrder;
import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import dalvik.system.VMStack;
+import sun.invoke.util.VerifyAccess;
+import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/**
+ * This class consists exclusively of static methods that operate on or return
+ * method handles. They fall into several categories:
+ * <ul>
+ * <li>Lookup methods which help create method handles for methods and fields.
+ * <li>Combinator methods, which combine or transform pre-existing method handles into new ones.
+ * <li>Other factory methods to create method handles that emulate other common JVM operations or control flow patterns.
+ * </ul>
+ * <p>
+ * @author John Rose, JSR 292 EG
+ * @since 1.7
+ */
public class MethodHandles {
- public static Lookup lookup() { return null; }
+ private MethodHandles() { } // do not instantiate
+
+ // BEGIN Android-added: unsupported() helper function.
+ // TODO(b/65872996): Remove when complete.
+ private static void unsupported(String msg) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException(msg);
+ }
+ // END Android-added: unsupported() helper function.
+
+ // Android-changed: We do not use MemberName / MethodHandleImpl.
+ //
+ // private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
+ // static { MethodHandleImpl.initStatics(); }
+ // See IMPL_LOOKUP below.
- public static Lookup publicLookup() { return null; }
+ //// Method handle creation from ordinary methods.
+ /**
+ * Returns a {@link Lookup lookup object} with
+ * full capabilities to emulate all supported bytecode behaviors of the caller.
+ * These capabilities include <a href="MethodHandles.Lookup.html#privacc">private access</a> to the caller.
+ * Factory methods on the lookup object can create
+ * <a href="MethodHandleInfo.html#directmh">direct method handles</a>
+ * for any member that the caller has access to via bytecodes,
+ * including protected and private fields and methods.
+ * This lookup object is a <em>capability</em> which may be delegated to trusted agents.
+ * Do not store it in place where untrusted code can access it.
+ * <p>
+ * This method is caller sensitive, which means that it may return different
+ * values to different callers.
+ * <p>
+ * For any given caller class {@code C}, the lookup object returned by this call
+ * has equivalent capabilities to any lookup object
+ * supplied by the JVM to the bootstrap method of an
+ * <a href="package-summary.html#indyinsn">invokedynamic instruction</a>
+ * executing in the same caller class {@code C}.
+ * @return a lookup object for the caller of this method, with private access
+ */
+ // Android-changed: Remove caller sensitive.
+ // @CallerSensitive
+ public static Lookup lookup() {
+ // Android-changed: Do not use Reflection.getCallerClass().
+ return new Lookup(VMStack.getStackClass1());
+ }
+
+ /**
+ * Returns a {@link Lookup lookup object} which is trusted minimally.
+ * It can only be used to create method handles to
+ * publicly accessible fields and methods.
+ * <p>
+ * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
+ * of this lookup object will be {@link java.lang.Object}.
+ *
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * The lookup class can be changed to any other class {@code C} using an expression of the form
+ * {@link Lookup#in publicLookup().in(C.class)}.
+ * Since all classes have equal access to public names,
+ * such a change would confer no new access rights.
+ * A public lookup object is always subject to
+ * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>.
+ * Also, it cannot access
+ * <a href="MethodHandles.Lookup.html#callsens">caller sensitive methods</a>.
+ * @return a lookup object which is trusted minimally
+ */
+ public static Lookup publicLookup() {
+ return Lookup.PUBLIC_LOOKUP;
+ }
+
+ /**
+ * Performs an unchecked "crack" of a
+ * <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
+ * The result is as if the user had obtained a lookup object capable enough
+ * to crack the target method handle, called
+ * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
+ * on the target to obtain its symbolic reference, and then called
+ * {@link java.lang.invoke.MethodHandleInfo#reflectAs MethodHandleInfo.reflectAs}
+ * to resolve the symbolic reference to a member.
+ * <p>
+ * If there is a security manager, its {@code checkPermission} method
+ * is called with a {@code ReflectPermission("suppressAccessChecks")} permission.
+ * @param <T> the desired type of the result, either {@link Member} or a subtype
+ * @param target a direct method handle to crack into symbolic reference components
+ * @param expected a class object representing the desired result type {@code T}
+ * @return a reference to the method, constructor, or field object
+ * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
+ * @exception NullPointerException if either argument is {@code null}
+ * @exception IllegalArgumentException if the target is not a direct method handle
+ * @exception ClassCastException if the member is not of the expected type
+ * @since 1.8
+ */
public static <T extends Member> T
- reflectAs(Class<T> expected, MethodHandle target) { return null; }
+ reflectAs(Class<T> expected, MethodHandle target) {
+ MethodHandleImpl directTarget = getMethodHandleImpl(target);
+ // Given that this is specified to be an "unchecked" crack, we can directly allocate
+ // a member from the underlying ArtField / Method and bypass all associated access checks.
+ return expected.cast(directTarget.getMemberInternal());
+ }
+ /**
+ * A <em>lookup object</em> is a factory for creating method handles,
+ * when the creation requires access checking.
+ * Method handles do not perform
+ * access checks when they are called, but rather when they are created.
+ * Therefore, method handle access
+ * restrictions must be enforced when a method handle is created.
+ * The caller class against which those restrictions are enforced
+ * is known as the {@linkplain #lookupClass lookup class}.
+ * <p>
+ * A lookup class which needs to create method handles will call
+ * {@link #lookup MethodHandles.lookup} to create a factory for itself.
+ * When the {@code Lookup} factory object is created, the identity of the lookup class is
+ * determined, and securely stored in the {@code Lookup} object.
+ * The lookup class (or its delegates) may then use factory methods
+ * on the {@code Lookup} object to create method handles for access-checked members.
+ * This includes all methods, constructors, and fields which are allowed to the lookup class,
+ * even private ones.
+ *
+ * <h1><a name="lookups"></a>Lookup Factory Methods</h1>
+ * The factory methods on a {@code Lookup} object correspond to all major
+ * use cases for methods, constructors, and fields.
+ * Each method handle created by a factory method is the functional
+ * equivalent of a particular <em>bytecode behavior</em>.
+ * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
+ * Here is a summary of the correspondence between these factory methods and
+ * the behavior the resulting method handles:
+ * <table border=1 cellpadding=5 summary="lookup method behaviors">
+ * <tr>
+ * <th><a name="equiv"></a>lookup expression</th>
+ * <th>member</th>
+ * <th>bytecode behavior</th>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}</td>
+ * <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findSetter lookup.findSetter(C.class,"f",FT.class)}</td>
+ * <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findStaticSetter lookup.findStaticSetter(C.class,"f",FT.class)}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findVirtual lookup.findVirtual(C.class,"m",MT)}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findStatic lookup.findStatic(C.class,"m",MT)}</td>
+ * <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findSpecial lookup.findSpecial(C.class,"m",MT,this.class)}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#findConstructor lookup.findConstructor(C.class,MT)}</td>
+ * <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter lookup.unreflectGetter(aField)}</td>
+ * <td>({@code static})?<br>{@code FT f;}</td><td>{@code (FT) aField.get(thisOrNull);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter lookup.unreflectSetter(aField)}</td>
+ * <td>({@code static})?<br>{@code FT f;}</td><td>{@code aField.set(thisOrNull, arg);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
+ * <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor lookup.unreflectConstructor(aConstructor)}</td>
+ * <td>{@code C(A*);}</td><td>{@code (C) aConstructor.newInstance(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
+ * <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
+ * </tr>
+ * </table>
+ *
+ * Here, the type {@code C} is the class or interface being searched for a member,
+ * documented as a parameter named {@code refc} in the lookup methods.
+ * The method type {@code MT} is composed from the return type {@code T}
+ * and the sequence of argument types {@code A*}.
+ * The constructor also has a sequence of argument types {@code A*} and
+ * is deemed to return the newly-created object of type {@code C}.
+ * Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
+ * The formal parameter {@code this} stands for the self-reference of type {@code C};
+ * if it is present, it is always the leading argument to the method handle invocation.
+ * (In the case of some {@code protected} members, {@code this} may be
+ * restricted in type to the lookup class; see below.)
+ * The name {@code arg} stands for all the other method handle arguments.
+ * In the code examples for the Core Reflection API, the name {@code thisOrNull}
+ * stands for a null reference if the accessed method or field is static,
+ * and {@code this} otherwise.
+ * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
+ * for reflective objects corresponding to the given members.
+ * <p>
+ * In cases where the given member is of variable arity (i.e., a method or constructor)
+ * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
+ * In all other cases, the returned method handle will be of fixed arity.
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * The equivalence between looked-up method handles and underlying
+ * class members and bytecode behaviors
+ * can break down in a few ways:
+ * <ul style="font-size:smaller;">
+ * <li>If {@code C} is not symbolically accessible from the lookup class's loader,
+ * the lookup can still succeed, even when there is no equivalent
+ * Java expression or bytecoded constant.
+ * <li>Likewise, if {@code T} or {@code MT}
+ * is not symbolically accessible from the lookup class's loader,
+ * the lookup can still succeed.
+ * For example, lookups for {@code MethodHandle.invokeExact} and
+ * {@code MethodHandle.invoke} will always succeed, regardless of requested type.
+ * <li>If there is a security manager installed, it can forbid the lookup
+ * on various grounds (<a href="MethodHandles.Lookup.html#secmgr">see below</a>).
+ * By contrast, the {@code ldc} instruction on a {@code CONSTANT_MethodHandle}
+ * constant is not subject to security manager checks.
+ * <li>If the looked-up method has a
+ * <a href="MethodHandle.html#maxarity">very large arity</a>,
+ * the method handle creation may fail, due to the method handle
+ * type having too many parameters.
+ * </ul>
+ *
+ * <h1><a name="access"></a>Access checking</h1>
+ * Access checks are applied in the factory methods of {@code Lookup},
+ * when a method handle is created.
+ * This is a key difference from the Core Reflection API, since
+ * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
+ * performs access checking against every caller, on every call.
+ * <p>
+ * All access checks start from a {@code Lookup} object, which
+ * compares its recorded lookup class against all requests to
+ * create method handles.
+ * A single {@code Lookup} object can be used to create any number
+ * of access-checked method handles, all checked against a single
+ * lookup class.
+ * <p>
+ * A {@code Lookup} object can be shared with other trusted code,
+ * such as a metaobject protocol.
+ * A shared {@code Lookup} object delegates the capability
+ * to create method handles on private members of the lookup class.
+ * Even if privileged code uses the {@code Lookup} object,
+ * the access checking is confined to the privileges of the
+ * original lookup class.
+ * <p>
+ * A lookup can fail, because
+ * the containing class is not accessible to the lookup class, or
+ * because the desired class member is missing, or because the
+ * desired class member is not accessible to the lookup class, or
+ * because the lookup object is not trusted enough to access the member.
+ * In any of these cases, a {@code ReflectiveOperationException} will be
+ * thrown from the attempted lookup. The exact class will be one of
+ * the following:
+ * <ul>
+ * <li>NoSuchMethodException &mdash; if a method is requested but does not exist
+ * <li>NoSuchFieldException &mdash; if a field is requested but does not exist
+ * <li>IllegalAccessException &mdash; if the member exists but an access check fails
+ * </ul>
+ * <p>
+ * In general, the conditions under which a method handle may be
+ * looked up for a method {@code M} are no more restrictive than the conditions
+ * under which the lookup class could have compiled, verified, and resolved a call to {@code M}.
+ * Where the JVM would raise exceptions like {@code NoSuchMethodError},
+ * a method handle lookup will generally raise a corresponding
+ * checked exception, such as {@code NoSuchMethodException}.
+ * And the effect of invoking the method handle resulting from the lookup
+ * is <a href="MethodHandles.Lookup.html#equiv">exactly equivalent</a>
+ * to executing the compiled, verified, and resolved call to {@code M}.
+ * The same point is true of fields and constructors.
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * Access checks only apply to named and reflected methods,
+ * constructors, and fields.
+ * Other method handle creation methods, such as
+ * {@link MethodHandle#asType MethodHandle.asType},
+ * do not require any access checks, and are used
+ * independently of any {@code Lookup} object.
+ * <p>
+ * If the desired member is {@code protected}, the usual JVM rules apply,
+ * including the requirement that the lookup class must be either be in the
+ * same package as the desired member, or must inherit that member.
+ * (See the Java Virtual Machine Specification, sections 4.9.2, 5.4.3.5, and 6.4.)
+ * In addition, if the desired member is a non-static field or method
+ * in a different package, the resulting method handle may only be applied
+ * to objects of the lookup class or one of its subclasses.
+ * This requirement is enforced by narrowing the type of the leading
+ * {@code this} parameter from {@code C}
+ * (which will necessarily be a superclass of the lookup class)
+ * to the lookup class itself.
+ * <p>
+ * The JVM imposes a similar requirement on {@code invokespecial} instruction,
+ * that the receiver argument must match both the resolved method <em>and</em>
+ * the current class. Again, this requirement is enforced by narrowing the
+ * type of the leading parameter to the resulting method handle.
+ * (See the Java Virtual Machine Specification, section 4.10.1.9.)
+ * <p>
+ * The JVM represents constructors and static initializer blocks as internal methods
+ * with special names ({@code "<init>"} and {@code "<clinit>"}).
+ * The internal syntax of invocation instructions allows them to refer to such internal
+ * methods as if they were normal methods, but the JVM bytecode verifier rejects them.
+ * A lookup of such an internal method will produce a {@code NoSuchMethodException}.
+ * <p>
+ * In some cases, access between nested classes is obtained by the Java compiler by creating
+ * an wrapper method to access a private method of another class
+ * in the same top-level declaration.
+ * For example, a nested class {@code C.D}
+ * can access private members within other related classes such as
+ * {@code C}, {@code C.D.E}, or {@code C.B},
+ * but the Java compiler may need to generate wrapper methods in
+ * those related classes. In such cases, a {@code Lookup} object on
+ * {@code C.E} would be unable to those private members.
+ * A workaround for this limitation is the {@link Lookup#in Lookup.in} method,
+ * which can transform a lookup on {@code C.E} into one on any of those other
+ * classes, without special elevation of privilege.
+ * <p>
+ * The accesses permitted to a given lookup object may be limited,
+ * according to its set of {@link #lookupModes lookupModes},
+ * to a subset of members normally accessible to the lookup class.
+ * For example, the {@link #publicLookup publicLookup}
+ * method produces a lookup object which is only allowed to access
+ * public members in public classes.
+ * The caller sensitive method {@link #lookup lookup}
+ * produces a lookup object with full capabilities relative to
+ * its caller class, to emulate all supported bytecode behaviors.
+ * Also, the {@link Lookup#in Lookup.in} method may produce a lookup object
+ * with fewer access modes than the original lookup object.
+ *
+ * <p style="font-size:smaller;">
+ * <a name="privacc"></a>
+ * <em>Discussion of private access:</em>
+ * We say that a lookup has <em>private access</em>
+ * if its {@linkplain #lookupModes lookup modes}
+ * include the possibility of accessing {@code private} members.
+ * As documented in the relevant methods elsewhere,
+ * only lookups with private access possess the following capabilities:
+ * <ul style="font-size:smaller;">
+ * <li>access private fields, methods, and constructors of the lookup class
+ * <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods,
+ * such as {@code Class.forName}
+ * <li>create method handles which {@link Lookup#findSpecial emulate invokespecial} instructions
+ * <li>avoid <a href="MethodHandles.Lookup.html#secmgr">package access checks</a>
+ * for classes accessible to the lookup class
+ * <li>create {@link Lookup#in delegated lookup objects} which have private access to other classes
+ * within the same package member
+ * </ul>
+ * <p style="font-size:smaller;">
+ * Each of these permissions is a consequence of the fact that a lookup object
+ * with private access can be securely traced back to an originating class,
+ * whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions
+ * can be reliably determined and emulated by method handles.
+ *
+ * <h1><a name="secmgr"></a>Security manager interactions</h1>
+ * Although bytecode instructions can only refer to classes in
+ * a related class loader, this API can search for methods in any
+ * class, as long as a reference to its {@code Class} object is
+ * available. Such cross-loader references are also possible with the
+ * Core Reflection API, and are impossible to bytecode instructions
+ * such as {@code invokestatic} or {@code getfield}.
+ * There is a {@linkplain java.lang.SecurityManager security manager API}
+ * to allow applications to check such cross-loader references.
+ * These checks apply to both the {@code MethodHandles.Lookup} API
+ * and the Core Reflection API
+ * (as found on {@link java.lang.Class Class}).
+ * <p>
+ * If a security manager is present, member lookups are subject to
+ * additional checks.
+ * From one to three calls are made to the security manager.
+ * Any of these calls can refuse access by throwing a
+ * {@link java.lang.SecurityException SecurityException}.
+ * Define {@code smgr} as the security manager,
+ * {@code lookc} as the lookup class of the current lookup object,
+ * {@code refc} as the containing class in which the member
+ * is being sought, and {@code defc} as the class in which the
+ * member is actually defined.
+ * The value {@code lookc} is defined as <em>not present</em>
+ * if the current lookup object does not have
+ * <a href="MethodHandles.Lookup.html#privacc">private access</a>.
+ * The calls are made according to the following rules:
+ * <ul>
+ * <li><b>Step 1:</b>
+ * If {@code lookc} is not present, or if its class loader is not
+ * the same as or an ancestor of the class loader of {@code refc},
+ * then {@link SecurityManager#checkPackageAccess
+ * smgr.checkPackageAccess(refcPkg)} is called,
+ * where {@code refcPkg} is the package of {@code refc}.
+ * <li><b>Step 2:</b>
+ * If the retrieved member is not public and
+ * {@code lookc} is not present, then
+ * {@link SecurityManager#checkPermission smgr.checkPermission}
+ * with {@code RuntimePermission("accessDeclaredMembers")} is called.
+ * <li><b>Step 3:</b>
+ * If the retrieved member is not public,
+ * and if {@code lookc} is not present,
+ * and if {@code defc} and {@code refc} are different,
+ * then {@link SecurityManager#checkPackageAccess
+ * smgr.checkPackageAccess(defcPkg)} is called,
+ * where {@code defcPkg} is the package of {@code defc}.
+ * </ul>
+ * Security checks are performed after other access checks have passed.
+ * Therefore, the above rules presuppose a member that is public,
+ * or else that is being accessed from a lookup class that has
+ * rights to access the member.
+ *
+ * <h1><a name="callsens"></a>Caller sensitive methods</h1>
+ * A small number of Java methods have a special property called caller sensitivity.
+ * A <em>caller-sensitive</em> method can behave differently depending on the
+ * identity of its immediate caller.
+ * <p>
+ * If a method handle for a caller-sensitive method is requested,
+ * the general rules for <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> apply,
+ * but they take account of the lookup class in a special way.
+ * The resulting method handle behaves as if it were called
+ * from an instruction contained in the lookup class,
+ * so that the caller-sensitive method detects the lookup class.
+ * (By contrast, the invoker of the method handle is disregarded.)
+ * Thus, in the case of caller-sensitive methods,
+ * different lookup classes may give rise to
+ * differently behaving method handles.
+ * <p>
+ * In cases where the lookup object is
+ * {@link #publicLookup publicLookup()},
+ * or some other lookup object without
+ * <a href="MethodHandles.Lookup.html#privacc">private access</a>,
+ * the lookup class is disregarded.
+ * In such cases, no caller-sensitive method handle can be created,
+ * access is forbidden, and the lookup fails with an
+ * {@code IllegalAccessException}.
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * For example, the caller-sensitive method
+ * {@link java.lang.Class#forName(String) Class.forName(x)}
+ * can return varying classes or throw varying exceptions,
+ * depending on the class loader of the class that calls it.
+ * A public lookup of {@code Class.forName} will fail, because
+ * there is no reasonable way to determine its bytecode behavior.
+ * <p style="font-size:smaller;">
+ * If an application caches method handles for broad sharing,
+ * it should use {@code publicLookup()} to create them.
+ * If there is a lookup of {@code Class.forName}, it will fail,
+ * and the application must take appropriate action in that case.
+ * It may be that a later lookup, perhaps during the invocation of a
+ * bootstrap method, can incorporate the specific identity
+ * of the caller, making the method accessible.
+ * <p style="font-size:smaller;">
+ * The function {@code MethodHandles.lookup} is caller sensitive
+ * so that there can be a secure foundation for lookups.
+ * Nearly all other methods in the JSR 292 API rely on lookup
+ * objects to check access requests.
+ */
+ // Android-changed: Change link targets from MethodHandles#[public]Lookup to
+ // #[public]Lookup to work around complaints from javadoc.
public static final
class Lookup {
- public static final int PUBLIC = 0;
+ /** The class on behalf of whom the lookup is being performed. */
+ /* @NonNull */ private final Class<?> lookupClass;
+
+ /** The allowed sorts of members which may be looked up (PUBLIC, etc.). */
+ private final int allowedModes;
+
+ /** A single-bit mask representing {@code public} access,
+ * which may contribute to the result of {@link #lookupModes lookupModes}.
+ * The value, {@code 0x01}, happens to be the same as the value of the
+ * {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}.
+ */
+ public static final int PUBLIC = Modifier.PUBLIC;
+
+ /** A single-bit mask representing {@code private} access,
+ * which may contribute to the result of {@link #lookupModes lookupModes}.
+ * The value, {@code 0x02}, happens to be the same as the value of the
+ * {@code private} {@linkplain java.lang.reflect.Modifier#PRIVATE modifier bit}.
+ */
+ public static final int PRIVATE = Modifier.PRIVATE;
+
+ /** A single-bit mask representing {@code protected} access,
+ * which may contribute to the result of {@link #lookupModes lookupModes}.
+ * The value, {@code 0x04}, happens to be the same as the value of the
+ * {@code protected} {@linkplain java.lang.reflect.Modifier#PROTECTED modifier bit}.
+ */
+ public static final int PROTECTED = Modifier.PROTECTED;
+
+ /** A single-bit mask representing {@code package} access (default access),
+ * which may contribute to the result of {@link #lookupModes lookupModes}.
+ * The value is {@code 0x08}, which does not correspond meaningfully to
+ * any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
+ */
+ public static final int PACKAGE = Modifier.STATIC;
+
+ private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE);
+
+ // Android-note: Android has no notion of a trusted lookup. If required, such lookups
+ // are performed by the runtime. As a result, we always use lookupClass, which will always
+ // be non-null in our implementation.
+ //
+ // private static final int TRUSTED = -1;
+
+ private static int fixmods(int mods) {
+ mods &= (ALL_MODES - PACKAGE);
+ return (mods != 0) ? mods : PACKAGE;
+ }
+
+ /** Tells which class is performing the lookup. It is this class against
+ * which checks are performed for visibility and access permissions.
+ * <p>
+ * The class implies a maximum level of access permission,
+ * but the permissions may be additionally limited by the bitmask
+ * {@link #lookupModes lookupModes}, which controls whether non-public members
+ * can be accessed.
+ * @return the lookup class, on behalf of which this lookup object finds members
+ */
+ public Class<?> lookupClass() {
+ return lookupClass;
+ }
+
+ /** Tells which access-protection classes of members this lookup object can produce.
+ * The result is a bit-mask of the bits
+ * {@linkplain #PUBLIC PUBLIC (0x01)},
+ * {@linkplain #PRIVATE PRIVATE (0x02)},
+ * {@linkplain #PROTECTED PROTECTED (0x04)},
+ * and {@linkplain #PACKAGE PACKAGE (0x08)}.
+ * <p>
+ * A freshly-created lookup object
+ * on the {@linkplain java.lang.invoke.MethodHandles#lookup() caller's class}
+ * has all possible bits set, since the caller class can access all its own members.
+ * A lookup object on a new lookup class
+ * {@linkplain java.lang.invoke.MethodHandles.Lookup#in created from a previous lookup object}
+ * may have some mode bits set to zero.
+ * The purpose of this is to restrict access via the new lookup object,
+ * so that it can access only names which can be reached by the original
+ * lookup object, and also by the new lookup class.
+ * @return the lookup modes, which limit the kinds of access performed by this lookup object
+ */
+ public int lookupModes() {
+ return allowedModes & ALL_MODES;
+ }
+
+ /** Embody the current class (the lookupClass) as a lookup class
+ * for method handle creation.
+ * Must be called by from a method in this package,
+ * which in turn is called by a method not in this package.
+ */
+ Lookup(Class<?> lookupClass) {
+ this(lookupClass, ALL_MODES);
+ // make sure we haven't accidentally picked up a privileged class:
+ checkUnprivilegedlookupClass(lookupClass, ALL_MODES);
+ }
+
+ private Lookup(Class<?> lookupClass, int allowedModes) {
+ this.lookupClass = lookupClass;
+ this.allowedModes = allowedModes;
+ }
- public static final int PRIVATE = 0;
+ /**
+ * Creates a lookup on the specified new lookup class.
+ * The resulting object will report the specified
+ * class as its own {@link #lookupClass lookupClass}.
+ * <p>
+ * However, the resulting {@code Lookup} object is guaranteed
+ * to have no more access capabilities than the original.
+ * In particular, access capabilities can be lost as follows:<ul>
+ * <li>If the new lookup class differs from the old one,
+ * protected members will not be accessible by virtue of inheritance.
+ * (Protected members may continue to be accessible because of package sharing.)
+ * <li>If the new lookup class is in a different package
+ * than the old one, protected and default (package) members will not be accessible.
+ * <li>If the new lookup class is not within the same package member
+ * as the old one, private members will not be accessible.
+ * <li>If the new lookup class is not accessible to the old lookup class,
+ * then no members, not even public members, will be accessible.
+ * (In all other cases, public members will continue to be accessible.)
+ * </ul>
+ *
+ * @param requestedLookupClass the desired lookup class for the new lookup object
+ * @return a lookup object which reports the desired lookup class
+ * @throws NullPointerException if the argument is null
+ */
+ public Lookup in(Class<?> requestedLookupClass) {
+ requestedLookupClass.getClass(); // null check
+ // Android-changed: There's no notion of a trusted lookup.
+ // if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all
+ // return new Lookup(requestedLookupClass, ALL_MODES);
- public static final int PROTECTED = 0;
+ if (requestedLookupClass == this.lookupClass)
+ return this; // keep same capabilities
+ int newModes = (allowedModes & (ALL_MODES & ~PROTECTED));
+ if ((newModes & PACKAGE) != 0
+ && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) {
+ newModes &= ~(PACKAGE|PRIVATE);
+ }
+ // Allow nestmate lookups to be created without special privilege:
+ if ((newModes & PRIVATE) != 0
+ && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
+ newModes &= ~PRIVATE;
+ }
+ if ((newModes & PUBLIC) != 0
+ && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) {
+ // The requested class it not accessible from the lookup class.
+ // No permissions.
+ newModes = 0;
+ }
+ checkUnprivilegedlookupClass(requestedLookupClass, newModes);
+ return new Lookup(requestedLookupClass, newModes);
+ }
- public static final int PACKAGE = 0;
+ // Make sure outer class is initialized first.
+ //
+ // Android-changed: Removed unnecessary reference to IMPL_NAMES.
+ // static { IMPL_NAMES.getClass(); }
- public Class<?> lookupClass() { return null; }
+ /** Version of lookup which is trusted minimally.
+ * It can only be used to create method handles to
+ * publicly accessible members.
+ */
+ static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, PUBLIC);
- public int lookupModes() { return 0; }
+ /** Package-private version of lookup which is trusted. */
+ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, ALL_MODES);
- public Lookup in(Class<?> requestedLookupClass) { return null; }
+ private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) {
+ String name = lookupClass.getName();
+ if (name.startsWith("java.lang.invoke."))
+ throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
+ // For caller-sensitive MethodHandles.lookup()
+ // disallow lookup more restricted packages
+ //
+ // Android-changed: The bootstrap classloader isn't null.
+ if (allowedModes == ALL_MODES &&
+ lookupClass.getClassLoader() == Object.class.getClassLoader()) {
+ if (name.startsWith("java.") ||
+ (name.startsWith("sun.")
+ && !name.startsWith("sun.invoke.")
+ && !name.equals("sun.reflect.ReflectionFactory"))) {
+ throw newIllegalArgumentException("illegal lookupClass: " + lookupClass);
+ }
+ }
+ }
+
+ /**
+ * Displays the name of the class from which lookups are to be made.
+ * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.)
+ * If there are restrictions on the access permitted to this lookup,
+ * this is indicated by adding a suffix to the class name, consisting
+ * of a slash and a keyword. The keyword represents the strongest
+ * allowed access, and is chosen as follows:
+ * <ul>
+ * <li>If no access is allowed, the suffix is "/noaccess".
+ * <li>If only public access is allowed, the suffix is "/public".
+ * <li>If only public and package access are allowed, the suffix is "/package".
+ * <li>If only public, package, and private access are allowed, the suffix is "/private".
+ * </ul>
+ * If none of the above cases apply, it is the case that full
+ * access (public, package, private, and protected) is allowed.
+ * In this case, no suffix is added.
+ * This is true only of an object obtained originally from
+ * {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
+ * Objects created by {@link java.lang.invoke.MethodHandles.Lookup#in Lookup.in}
+ * always have restricted access, and will display a suffix.
+ * <p>
+ * (It may seem strange that protected access should be
+ * stronger than private access. Viewed independently from
+ * package access, protected access is the first to be lost,
+ * because it requires a direct subclass relationship between
+ * caller and callee.)
+ * @see #in
+ */
+ @Override
+ public String toString() {
+ String cname = lookupClass.getName();
+ switch (allowedModes) {
+ case 0: // no privileges
+ return cname + "/noaccess";
+ case PUBLIC:
+ return cname + "/public";
+ case PUBLIC|PACKAGE:
+ return cname + "/package";
+ case ALL_MODES & ~PROTECTED:
+ return cname + "/private";
+ case ALL_MODES:
+ return cname;
+ // Android-changed: No support for TRUSTED callers.
+ // case TRUSTED:
+ // return "/trusted"; // internal only; not exported
+ default: // Should not happen, but it's a bitfield...
+ cname = cname + "/" + Integer.toHexString(allowedModes);
+ assert(false) : cname;
+ return cname;
+ }
+ }
+
+ /**
+ * Produces a method handle for a static method.
+ * The type of the method handle will be that of the method.
+ * (Since static methods do not take receivers, there is no
+ * additional receiver argument inserted into the method handle type,
+ * as there would be with {@link #findVirtual findVirtual} or {@link #findSpecial findSpecial}.)
+ * The method and all its argument types must be accessible to the lookup object.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * If the returned method handle is invoked, the method's class will
+ * be initialized, if it has not already been initialized.
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
+ "asList", methodType(List.class, Object[].class));
+assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
+ * }</pre></blockquote>
+ * @param refc the class from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method
+ * @return the desired method handle
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails,
+ * or if the method is not {@code static},
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
public
- MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
+ MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+ Method method = refc.getDeclaredMethod(name, type.ptypes());
+ final int modifiers = method.getModifiers();
+ if (!Modifier.isStatic(modifiers)) {
+ throw new IllegalAccessException("Method" + method + " is not static");
+ }
+ checkReturnType(method, type);
+ checkAccess(refc, method.getDeclaringClass(), modifiers, method.getName());
+ return createMethodHandle(method, MethodHandle.INVOKE_STATIC, type);
+ }
+
+ private MethodHandle findVirtualForMH(String name, MethodType type) {
+ // these names require special lookups because of the implicit MethodType argument
+ if ("invoke".equals(name))
+ return invoker(type);
+ if ("invokeExact".equals(name))
+ return exactInvoker(type);
+ return null;
+ }
+
+ private static MethodHandle createMethodHandle(Method method, int handleKind,
+ MethodType methodType) {
+ MethodHandle mh = new MethodHandleImpl(method.getArtMethod(), handleKind, methodType);
+ if (method.isVarArgs()) {
+ return new Transformers.VarargsCollector(mh);
+ } else {
+ return mh;
+ }
+ }
+
+ /**
+ * Produces a method handle for a virtual method.
+ * The type of the method handle will be that of the method,
+ * with the receiver type (usually {@code refc}) prepended.
+ * The method and all its argument types must be accessible to the lookup object.
+ * <p>
+ * When called, the handle will treat the first argument as a receiver
+ * and dispatch on the receiver's type to determine which method
+ * implementation to enter.
+ * (The dispatching action is identical with that performed by an
+ * {@code invokevirtual} or {@code invokeinterface} instruction.)
+ * <p>
+ * The first argument will be of type {@code refc} if the lookup
+ * class has full privileges to access the member. Otherwise
+ * the member must be {@code protected} and the first argument
+ * will be restricted in type to the lookup class.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * Because of the general <a href="MethodHandles.Lookup.html#equiv">equivalence</a> between {@code invokevirtual}
+ * instructions and method handles produced by {@code findVirtual},
+ * if the class is {@code MethodHandle} and the name string is
+ * {@code invokeExact} or {@code invoke}, the resulting
+ * method handle is equivalent to one produced by
+ * {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
+ * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
+ * with the same {@code type} argument.
+ *
+ * <b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_concat = publicLookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
+ "hashCode", methodType(int.class));
+MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
+ "hashCode", methodType(int.class));
+assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
+assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
+assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
+// interface method:
+MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
+ "subSequence", methodType(CharSequence.class, int.class, int.class));
+assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
+// constructor "internal method" must be accessed differently:
+MethodType MT_newString = methodType(void.class); //()V for new String()
+try { assertEquals("impossible", lookup()
+ .findVirtual(String.class, "<init>", MT_newString));
+ } catch (NoSuchMethodException ex) { } // OK
+MethodHandle MH_newString = publicLookup()
+ .findConstructor(String.class, MT_newString);
+assertEquals("", (String) MH_newString.invokeExact());
+ * }</pre></blockquote>
+ *
+ * @param refc the class or interface from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails,
+ * or if the method is {@code static}
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+ // Special case : when we're looking up a virtual method on the MethodHandles class
+ // itself, we can return one of our specialized invokers.
+ if (refc == MethodHandle.class) {
+ MethodHandle mh = findVirtualForMH(name, type);
+ if (mh != null) {
+ return mh;
+ }
+ }
+ // BEGIN Android-changed: Added VarHandle case here.
+ // Implementation to follow. TODO(b/65872996)
+ if (refc == VarHandle.class) {
+ unsupported("MethodHandles.findVirtual with refc == VarHandle.class");
+ return null;
+ }
+ // END Android-changed: Added VarHandle handling here.
+
+ Method method = refc.getInstanceMethod(name, type.ptypes());
+ if (method == null) {
+ // This is pretty ugly and a consequence of the MethodHandles API. We have to throw
+ // an IAE and not an NSME if the method exists but is static (even though the RI's
+ // IAE has a message that says "no such method"). We confine the ugliness and
+ // slowness to the failure case, and allow getInstanceMethod to remain fairly
+ // general.
+ try {
+ Method m = refc.getDeclaredMethod(name, type.ptypes());
+ if (Modifier.isStatic(m.getModifiers())) {
+ throw new IllegalAccessException("Method" + m + " is static");
+ }
+ } catch (NoSuchMethodException ignored) {
+ }
+
+ throw new NoSuchMethodException(name + " " + Arrays.toString(type.ptypes()));
+ }
+ checkReturnType(method, type);
+
+ // We have a valid method, perform access checks.
+ checkAccess(refc, method.getDeclaringClass(), method.getModifiers(), method.getName());
+
+ // Insert the leading reference parameter.
+ MethodType handleType = type.insertParameterTypes(0, refc);
+ return createMethodHandle(method, MethodHandle.INVOKE_VIRTUAL, handleType);
+ }
+
+ /**
+ * Produces a method handle which creates an object and initializes it, using
+ * the constructor of the specified type.
+ * The parameter types of the method handle will be those of the constructor,
+ * while the return type will be a reference to the constructor's class.
+ * The constructor and all its argument types must be accessible to the lookup object.
+ * <p>
+ * The requested type must have a return type of {@code void}.
+ * (This is consistent with the JVM's treatment of constructor type descriptors.)
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * If the returned method handle is invoked, the constructor's class will
+ * be initialized, if it has not already been initialized.
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_newArrayList = publicLookup().findConstructor(
+ ArrayList.class, methodType(void.class, Collection.class));
+Collection orig = Arrays.asList("x", "y");
+Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
+assert(orig != copy);
+assertEquals(orig, copy);
+// a variable-arity constructor:
+MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
+ ProcessBuilder.class, methodType(void.class, String[].class));
+ProcessBuilder pb = (ProcessBuilder)
+ MH_newProcessBuilder.invoke("x", "y", "z");
+assertEquals("[x, y, z]", pb.command().toString());
+ * }</pre></blockquote>
+ * @param refc the class or interface from which the method is accessed
+ * @param type the type of the method, with the receiver argument omitted, and a void return type
+ * @return the desired method handle
+ * @throws NoSuchMethodException if the constructor does not exist
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+ if (refc.isArray()) {
+ throw new NoSuchMethodException("no constructor for array class: " + refc.getName());
+ }
+ // The queried |type| is (PT1,PT2,..)V
+ Constructor constructor = refc.getDeclaredConstructor(type.ptypes());
+ if (constructor == null) {
+ throw new NoSuchMethodException(
+ "No constructor for " + constructor.getDeclaringClass() + " matching " + type);
+ }
+ checkAccess(refc, constructor.getDeclaringClass(), constructor.getModifiers(),
+ constructor.getName());
+
+ return createMethodHandleForConstructor(constructor);
+ }
- public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
+ private MethodHandle createMethodHandleForConstructor(Constructor constructor) {
+ Class<?> refc = constructor.getDeclaringClass();
+ MethodType constructorType =
+ MethodType.methodType(refc, constructor.getParameterTypes());
+ MethodHandle mh;
+ if (refc == String.class) {
+ // String constructors have optimized StringFactory methods
+ // that matches returned type. These factory methods combine the
+ // memory allocation and initialization calls for String objects.
+ mh = new MethodHandleImpl(constructor.getArtMethod(), MethodHandle.INVOKE_DIRECT,
+ constructorType);
+ } else {
+ // Constructors for all other classes use a Construct transformer to perform
+ // their memory allocation and call to <init>.
+ MethodType initType = initMethodType(constructorType);
+ MethodHandle initHandle = new MethodHandleImpl(
+ constructor.getArtMethod(), MethodHandle.INVOKE_DIRECT, initType);
+ mh = new Transformers.Construct(initHandle, constructorType);
+ }
- public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
+ if (constructor.isVarArgs()) {
+ mh = new Transformers.VarargsCollector(mh);
+ }
+ return mh;
+ }
+ private static MethodType initMethodType(MethodType constructorType) {
+ // Returns a MethodType appropriate for class <init>
+ // methods. Constructor MethodTypes have the form
+ // (PT1,PT2,...)C and class <init> MethodTypes have the
+ // form (C,PT1,PT2,...)V.
+ assert constructorType.rtype() != void.class;
+
+ // Insert constructorType C as the first parameter type in
+ // the MethodType for <init>.
+ Class<?> [] initPtypes = new Class<?> [constructorType.ptypes().length + 1];
+ initPtypes[0] = constructorType.rtype();
+ System.arraycopy(constructorType.ptypes(), 0, initPtypes, 1,
+ constructorType.ptypes().length);
+
+ // Set the return type for the <init> MethodType to be void.
+ return MethodType.methodType(void.class, initPtypes);
+ }
+
+ /**
+ * Produces an early-bound method handle for a virtual method.
+ * It will bypass checks for overriding methods on the receiver,
+ * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
+ * instruction from within the explicitly specified {@code specialCaller}.
+ * The type of the method handle will be that of the method,
+ * with a suitably restricted receiver type prepended.
+ * (The receiver type will be {@code specialCaller} or a subtype.)
+ * The method and all its argument types must be accessible
+ * to the lookup object.
+ * <p>
+ * Before method resolution,
+ * if the explicitly specified caller class is not identical with the
+ * lookup class, or if this lookup object does not have
+ * <a href="MethodHandles.Lookup.html#privacc">private access</a>
+ * privileges, the access fails.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p style="font-size:smaller;">
+ * <em>(Note: JVM internal methods named {@code "<init>"} are not visible to this API,
+ * even though the {@code invokespecial} instruction can refer to them
+ * in special circumstances. Use {@link #findConstructor findConstructor}
+ * to access instance initialization methods in a safe manner.)</em>
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+static class Listie extends ArrayList {
+ public String toString() { return "[wee Listie]"; }
+ static Lookup lookup() { return MethodHandles.lookup(); }
+}
+...
+// no access to constructor via invokeSpecial:
+MethodHandle MH_newListie = Listie.lookup()
+ .findConstructor(Listie.class, methodType(void.class));
+Listie l = (Listie) MH_newListie.invokeExact();
+try { assertEquals("impossible", Listie.lookup().findSpecial(
+ Listie.class, "<init>", methodType(void.class), Listie.class));
+ } catch (NoSuchMethodException ex) { } // OK
+// access to super and self methods via invokeSpecial:
+MethodHandle MH_super = Listie.lookup().findSpecial(
+ ArrayList.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_this = Listie.lookup().findSpecial(
+ Listie.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_duper = Listie.lookup().findSpecial(
+ Object.class, "toString" , methodType(String.class), Listie.class);
+assertEquals("[]", (String) MH_super.invokeExact(l));
+assertEquals(""+l, (String) MH_this.invokeExact(l));
+assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
+try { assertEquals("inaccessible", Listie.lookup().findSpecial(
+ String.class, "toString", methodType(String.class), Listie.class));
+ } catch (IllegalAccessException ex) { } // OK
+Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
+assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
+ * }</pre></blockquote>
+ *
+ * @param refc the class or interface from which the method is accessed
+ * @param name the name of the method (which must not be "&lt;init&gt;")
+ * @param type the type of the method, with the receiver argument omitted
+ * @param specialCaller the proposed calling class to perform the {@code invokespecial}
+ * @return the desired method handle
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
- Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException { return null; }
+ Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
+ if (specialCaller == null) {
+ throw new NullPointerException("specialCaller == null");
+ }
+
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ if (refc == null) {
+ throw new NullPointerException("ref == null");
+ }
+
+ // Make sure that the special caller is identical to the lookup class or that we have
+ // private access.
+ checkSpecialCaller(specialCaller);
+
+ // Even though constructors are invoked using a "special" invoke, handles to them can't
+ // be created using findSpecial. Callers must use findConstructor instead. Similarly,
+ // there is no path for calling static class initializers.
+ if (name.startsWith("<")) {
+ throw new NoSuchMethodException(name + " is not a valid method name.");
+ }
+
+ Method method = refc.getDeclaredMethod(name, type.ptypes());
+ checkReturnType(method, type);
+ return findSpecial(method, type, refc, specialCaller);
+ }
+
+ private MethodHandle findSpecial(Method method, MethodType type,
+ Class<?> refc, Class<?> specialCaller)
+ throws IllegalAccessException {
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalAccessException("expected a non-static method:" + method);
+ }
+
+ if (Modifier.isPrivate(method.getModifiers())) {
+ // Since this is a private method, we'll need to also make sure that the
+ // lookup class is the same as the refering class. We've already checked that
+ // the specialCaller is the same as the special lookup class, both of these must
+ // be the same as the declaring class(*) in order to access the private method.
+ //
+ // (*) Well, this isn't true for nested classes but OpenJDK doesn't support those
+ // either.
+ if (refc != lookupClass()) {
+ throw new IllegalAccessException("no private access for invokespecial : "
+ + refc + ", from" + this);
+ }
+
+ // This is a private method, so there's nothing special to do.
+ MethodType handleType = type.insertParameterTypes(0, refc);
+ return createMethodHandle(method, MethodHandle.INVOKE_DIRECT, handleType);
+ }
+
+ // This is a public, protected or package-private method, which means we're expecting
+ // invoke-super semantics. We'll have to restrict the receiver type appropriately on the
+ // handle once we check that there really is a "super" relationship between them.
+ if (!method.getDeclaringClass().isAssignableFrom(specialCaller)) {
+ throw new IllegalAccessException(refc + "is not assignable from " + specialCaller);
+ }
+
+ // Note that we restrict the receiver to "specialCaller" instances.
+ MethodType handleType = type.insertParameterTypes(0, specialCaller);
+ return createMethodHandle(method, MethodHandle.INVOKE_SUPER, handleType);
+ }
+
+ /**
+ * Produces a method handle giving read access to a non-static field.
+ * The type of the method handle will have a return type of the field's
+ * value type.
+ * The method handle's single argument will be the instance containing
+ * the field.
+ * Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
+ * @param name the field's name
+ * @param type the field's type
+ * @return a method handle which can load values from the field
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ return findAccessor(refc, name, type, MethodHandle.IGET);
+ }
+
+ private MethodHandle findAccessor(Class<?> refc, String name, Class<?> type, int kind)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = refc.getDeclaredField(name);
+ final Class<?> fieldType = field.getType();
+ if (fieldType != type) {
+ throw new NoSuchFieldException(
+ "Field has wrong type: " + fieldType + " != " + type);
+ }
+
+ return findAccessor(field, refc, type, kind, true /* performAccessChecks */);
+ }
+
+ private MethodHandle findAccessor(Field field, Class<?> refc, Class<?> fieldType, int kind,
+ boolean performAccessChecks)
+ throws IllegalAccessException {
+ if (!performAccessChecks) {
+ checkAccess(refc, field.getDeclaringClass(), field.getModifiers(), field.getName());
+ }
+
+ final boolean isStaticKind = kind == MethodHandle.SGET || kind == MethodHandle.SPUT;
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) != isStaticKind) {
+ String reason = "Field " + field + " is " +
+ (isStaticKind ? "not " : "") + "static";
+ throw new IllegalAccessException(reason);
+ }
+
+ final boolean isSetterKind = kind == MethodHandle.IPUT || kind == MethodHandle.SPUT;
+ if (Modifier.isFinal(modifiers) && isSetterKind) {
+ throw new IllegalAccessException("Field " + field + " is final");
+ }
+
+ final MethodType methodType;
+ switch (kind) {
+ case MethodHandle.SGET:
+ methodType = MethodType.methodType(fieldType);
+ break;
+ case MethodHandle.SPUT:
+ methodType = MethodType.methodType(void.class, fieldType);
+ break;
+ case MethodHandle.IGET:
+ methodType = MethodType.methodType(fieldType, refc);
+ break;
+ case MethodHandle.IPUT:
+ methodType = MethodType.methodType(void.class, refc, fieldType);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid kind " + kind);
+ }
+ return new MethodHandleImpl(field.getArtField(), kind, methodType);
+ }
+
+ /**
+ * Produces a method handle giving write access to a non-static field.
+ * The type of the method handle will have a void return type.
+ * The method handle will take two arguments, the instance containing
+ * the field, and the value to be stored.
+ * The second argument will be of the field's value type.
+ * Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
+ * @param name the field's name
+ * @param type the field's type
+ * @return a method handle which can store values into the field
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ return findAccessor(refc, name, type, MethodHandle.IPUT);
+ }
+
+ // BEGIN Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+ /**
+ * Produces a VarHandle giving access to a non-static field {@code name}
+ * of type {@code type} declared in a class of type {@code recv}.
+ * The VarHandle's variable type is {@code type} and it has one
+ * coordinate type, {@code recv}.
+ * <p>
+ * Access checking is performed immediately on behalf of the lookup
+ * class.
+ * <p>
+ * Certain access modes of the returned VarHandle are unsupported under
+ * the following conditions:
+ * <ul>
+ * <li>if the field is declared {@code final}, then the write, atomic
+ * update, numeric atomic update, and bitwise atomic update access
+ * modes are unsupported.
+ * <li>if the field type is anything other than {@code byte},
+ * {@code short}, {@code char}, {@code int}, {@code long},
+ * {@code float}, or {@code double} then numeric atomic update
+ * access modes are unsupported.
+ * <li>if the field type is anything other than {@code boolean},
+ * {@code byte}, {@code short}, {@code char}, {@code int} or
+ * {@code long} then bitwise atomic update access modes are
+ * unsupported.
+ * </ul>
+ * <p>
+ * If the field is declared {@code volatile} then the returned VarHandle
+ * will override access to the field (effectively ignore the
+ * {@code volatile} declaration) in accordance to its specified
+ * access modes.
+ * <p>
+ * If the field type is {@code float} or {@code double} then numeric
+ * and atomic update access modes compare values using their bitwise
+ * representation (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @apiNote
+ * Bitwise comparison of {@code float} values or {@code double} values,
+ * as performed by the numeric and atomic update access modes, differ
+ * from the primitive {@code ==} operator and the {@link Float#equals}
+ * and {@link Double#equals} methods, specifically with respect to
+ * comparing NaN values or comparing {@code -0.0} with {@code +0.0}.
+ * Care should be taken when performing a compare and set or a compare
+ * and exchange operation with such values since the operation may
+ * unexpectedly fail.
+ * There are many possible NaN values that are considered to be
+ * {@code NaN} in Java, although no IEEE 754 floating-point operation
+ * provided by Java can distinguish between them. Operation failure can
+ * occur if the expected or witness value is a NaN value and it is
+ * transformed (perhaps in a platform specific manner) into another NaN
+ * value, and thus has a different bitwise representation (see
+ * {@link Float#intBitsToFloat} or {@link Double#longBitsToDouble} for more
+ * details).
+ * The values {@code -0.0} and {@code +0.0} have different bitwise
+ * representations but are considered equal when using the primitive
+ * {@code ==} operator. Operation failure can occur if, for example, a
+ * numeric algorithm computes an expected value to be say {@code -0.0}
+ * and previously computed the witness value to be say {@code +0.0}.
+ * @param recv the receiver class, of type {@code R}, that declares the
+ * non-static field
+ * @param name the field's name
+ * @param type the field's type, of type {@code T}
+ * @return a VarHandle giving access to non-static fields.
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ * @since 9
+ * @hide
+ */
+ public VarHandle findVarHandle(Class<?> recv, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ unsupported("MethodHandles.Lookup.findVarHandle()"); // TODO(b/65872996)
+ return null;
+ }
+ // END Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+
+ /**
+ * Produces a method handle giving read access to a static field.
+ * The type of the method handle will have a return type of the field's
+ * value type.
+ * The method handle will take no arguments.
+ * Access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * If the returned method handle is invoked, the field's class will
+ * be initialized, if it has not already been initialized.
+ * @param refc the class or interface from which the method is accessed
+ * @param name the field's name
+ * @param type the field's type
+ * @return a method handle which can load values from the field
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ return findAccessor(refc, name, type, MethodHandle.SGET);
+ }
+
+ /**
+ * Produces a method handle giving write access to a static field.
+ * The type of the method handle will have a void return type.
+ * The method handle will take a single
+ * argument, of the field's value type, the value to be stored.
+ * Access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * If the returned method handle is invoked, the field's class will
+ * be initialized, if it has not already been initialized.
+ * @param refc the class or interface from which the method is accessed
+ * @param name the field's name
+ * @param type the field's type
+ * @return a method handle which can store values into the field
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ return findAccessor(refc, name, type, MethodHandle.SPUT);
+ }
+
+ // BEGIN Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+ /**
+ * Produces a VarHandle giving access to a static field {@code name} of
+ * type {@code type} declared in a class of type {@code decl}.
+ * The VarHandle's variable type is {@code type} and it has no
+ * coordinate types.
+ * <p>
+ * Access checking is performed immediately on behalf of the lookup
+ * class.
+ * <p>
+ * If the returned VarHandle is operated on, the declaring class will be
+ * initialized, if it has not already been initialized.
+ * <p>
+ * Certain access modes of the returned VarHandle are unsupported under
+ * the following conditions:
+ * <ul>
+ * <li>if the field is declared {@code final}, then the write, atomic
+ * update, numeric atomic update, and bitwise atomic update access
+ * modes are unsupported.
+ * <li>if the field type is anything other than {@code byte},
+ * {@code short}, {@code char}, {@code int}, {@code long},
+ * {@code float}, or {@code double}, then numeric atomic update
+ * access modes are unsupported.
+ * <li>if the field type is anything other than {@code boolean},
+ * {@code byte}, {@code short}, {@code char}, {@code int} or
+ * {@code long} then bitwise atomic update access modes are
+ * unsupported.
+ * </ul>
+ * <p>
+ * If the field is declared {@code volatile} then the returned VarHandle
+ * will override access to the field (effectively ignore the
+ * {@code volatile} declaration) in accordance to its specified
+ * access modes.
+ * <p>
+ * If the field type is {@code float} or {@code double} then numeric
+ * and atomic update access modes compare values using their bitwise
+ * representation (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @apiNote
+ * Bitwise comparison of {@code float} values or {@code double} values,
+ * as performed by the numeric and atomic update access modes, differ
+ * from the primitive {@code ==} operator and the {@link Float#equals}
+ * and {@link Double#equals} methods, specifically with respect to
+ * comparing NaN values or comparing {@code -0.0} with {@code +0.0}.
+ * Care should be taken when performing a compare and set or a compare
+ * and exchange operation with such values since the operation may
+ * unexpectedly fail.
+ * There are many possible NaN values that are considered to be
+ * {@code NaN} in Java, although no IEEE 754 floating-point operation
+ * provided by Java can distinguish between them. Operation failure can
+ * occur if the expected or witness value is a NaN value and it is
+ * transformed (perhaps in a platform specific manner) into another NaN
+ * value, and thus has a different bitwise representation (see
+ * {@link Float#intBitsToFloat} or {@link Double#longBitsToDouble} for more
+ * details).
+ * The values {@code -0.0} and {@code +0.0} have different bitwise
+ * representations but are considered equal when using the primitive
+ * {@code ==} operator. Operation failure can occur if, for example, a
+ * numeric algorithm computes an expected value to be say {@code -0.0}
+ * and previously computed the witness value to be say {@code +0.0}.
+ * @param decl the class that declares the static field
+ * @param name the field's name
+ * @param type the field's type, of type {@code T}
+ * @return a VarHandle giving access to a static field
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ * @since 9
+ * @hide
+ */
+ public VarHandle findStaticVarHandle(Class<?> decl, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+ unsupported("MethodHandles.Lookup.findStaticVarHandle()"); // TODO(b/65872996)
+ return null;
+ }
+ // END Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+
+ /**
+ * Produces an early-bound method handle for a non-static method.
+ * The receiver must have a supertype {@code defc} in which a method
+ * of the given name and type is accessible to the lookup class.
+ * The method and all its argument types must be accessible to the lookup object.
+ * The type of the method handle will be that of the method,
+ * without any insertion of an additional receiver parameter.
+ * The given receiver will be bound into the method handle,
+ * so that every call to the method handle will invoke the
+ * requested method on the given receiver.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set
+ * <em>and</em> the trailing array argument is not the only argument.
+ * (If the trailing array argument is the only argument,
+ * the given receiver value will be bound to it.)
+ * <p>
+ * This is equivalent to the following code:
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle mh0 = lookup().findVirtual(defc, name, type);
+MethodHandle mh1 = mh0.bindTo(receiver);
+MethodType mt1 = mh1.type();
+if (mh0.isVarargsCollector())
+ mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
+return mh1;
+ * }</pre></blockquote>
+ * where {@code defc} is either {@code receiver.getClass()} or a super
+ * type of that class, in which the requested method is accessible
+ * to the lookup class.
+ * (Note that {@code bindTo} does not preserve variable arity.)
+ * @param receiver the object from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
+ * @see MethodHandle#bindTo
+ * @see #findVirtual
+ */
+ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+ MethodHandle handle = findVirtual(receiver.getClass(), name, type);
+ MethodHandle adapter = handle.bindTo(receiver);
+ MethodType adapterType = adapter.type();
+ if (handle.isVarargsCollector()) {
+ adapter = adapter.asVarargsCollector(
+ adapterType.parameterType(adapterType.parameterCount() - 1));
+ }
+
+ return adapter;
+ }
+
+ /**
+ * Makes a <a href="MethodHandleInfo.html#directmh">direct method handle</a>
+ * to <i>m</i>, if the lookup class has permission.
+ * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
+ * If <i>m</i> is virtual, overriding is respected on every call.
+ * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped.
+ * The type of the method handle will be that of the method,
+ * with the receiver type prepended (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * If <i>m</i> is static, and
+ * if the returned method handle is invoked, the method's class will
+ * be initialized, if it has not already been initialized.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @throws NullPointerException if the argument is null
+ */
+ public MethodHandle unreflect(Method m) throws IllegalAccessException {
+ if (m == null) {
+ throw new NullPointerException("m == null");
+ }
+
+ MethodType methodType = MethodType.methodType(m.getReturnType(),
+ m.getParameterTypes());
+
+ // We should only perform access checks if setAccessible hasn't been called yet.
+ if (!m.isAccessible()) {
+ checkAccess(m.getDeclaringClass(), m.getDeclaringClass(), m.getModifiers(),
+ m.getName());
+ }
+
+ if (Modifier.isStatic(m.getModifiers())) {
+ return createMethodHandle(m, MethodHandle.INVOKE_STATIC, methodType);
+ } else {
+ methodType = methodType.insertParameterTypes(0, m.getDeclaringClass());
+ return createMethodHandle(m, MethodHandle.INVOKE_VIRTUAL, methodType);
+ }
+ }
+
+ /**
+ * Produces a method handle for a reflected method.
+ * It will bypass checks for overriding methods on the receiver,
+ * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
+ * instruction from within the explicitly specified {@code specialCaller}.
+ * The type of the method handle will be that of the method,
+ * with a suitably restricted receiver type prepended.
+ * (The receiver type will be {@code specialCaller} or a subtype.)
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class,
+ * as if {@code invokespecial} instruction were being linked.
+ * <p>
+ * Before method resolution,
+ * if the explicitly specified caller class is not identical with the
+ * lookup class, or if this lookup object does not have
+ * <a href="MethodHandles.Lookup.html#privacc">private access</a>
+ * privileges, the access fails.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * @param m the reflected method
+ * @param specialCaller the class nominally calling the method
+ * @return a method handle which can invoke the reflected method
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @throws NullPointerException if any argument is null
+ */
+ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
+ if (m == null) {
+ throw new NullPointerException("m == null");
+ }
+
+ if (specialCaller == null) {
+ throw new NullPointerException("specialCaller == null");
+ }
+
+ if (!m.isAccessible()) {
+ checkSpecialCaller(specialCaller);
+ }
+
+ final MethodType methodType = MethodType.methodType(m.getReturnType(),
+ m.getParameterTypes());
+ return findSpecial(m, methodType, m.getDeclaringClass() /* refc */, specialCaller);
+ }
+
+ /**
+ * Produces a method handle for a reflected constructor.
+ * The type of the method handle will be that of the constructor,
+ * with the return type changed to the declaring class.
+ * The method handle will perform a {@code newInstance} operation,
+ * creating a new instance of the constructor's class on the
+ * arguments passed to the method handle.
+ * <p>
+ * If the constructor's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * If the returned method handle is invoked, the constructor's class will
+ * be initialized, if it has not already been initialized.
+ * @param c the reflected constructor
+ * @return a method handle which can invoke the reflected constructor
+ * @throws IllegalAccessException if access checking fails
+ * or if the method's variable arity modifier bit
+ * is set and {@code asVarargsCollector} fails
+ * @throws NullPointerException if the argument is null
+ */
+ public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException {
+ if (c == null) {
+ throw new NullPointerException("c == null");
+ }
+
+ if (!c.isAccessible()) {
+ checkAccess(c.getDeclaringClass(), c.getDeclaringClass(), c.getModifiers(),
+ c.getName());
+ }
+
+ return createMethodHandleForConstructor(c);
+ }
+
+ /**
+ * Produces a method handle giving read access to a reflected field.
+ * The type of the method handle will have a return type of the field's
+ * value type.
+ * If the field is static, the method handle will take no arguments.
+ * Otherwise, its single argument will be the instance containing
+ * the field.
+ * If the field's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * If the field is static, and
+ * if the returned method handle is invoked, the field's class will
+ * be initialized, if it has not already been initialized.
+ * @param f the reflected field
+ * @return a method handle which can load values from the reflected field
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
+ */
+ public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
+ return findAccessor(f, f.getDeclaringClass(), f.getType(),
+ Modifier.isStatic(f.getModifiers()) ? MethodHandle.SGET : MethodHandle.IGET,
+ f.isAccessible() /* performAccessChecks */);
+ }
+
+ /**
+ * Produces a method handle giving write access to a reflected field.
+ * The type of the method handle will have a void return type.
+ * If the field is static, the method handle will take a single
+ * argument, of the field's value type, the value to be stored.
+ * Otherwise, the two arguments will be the instance containing
+ * the field, and the value to be stored.
+ * If the field's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * If the field is static, and
+ * if the returned method handle is invoked, the field's class will
+ * be initialized, if it has not already been initialized.
+ * @param f the reflected field
+ * @return a method handle which can store values into the reflected field
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
+ */
+ public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
+ return findAccessor(f, f.getDeclaringClass(), f.getType(),
+ Modifier.isStatic(f.getModifiers()) ? MethodHandle.SPUT : MethodHandle.IPUT,
+ f.isAccessible() /* performAccessChecks */);
+ }
+
+ // BEGIN Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+ /**
+ * Produces a VarHandle giving access to a reflected field {@code f}
+ * of type {@code T} declared in a class of type {@code R}.
+ * The VarHandle's variable type is {@code T}.
+ * If the field is non-static the VarHandle has one coordinate type,
+ * {@code R}. Otherwise, the field is static, and the VarHandle has no
+ * coordinate types.
+ * <p>
+ * Access checking is performed immediately on behalf of the lookup
+ * class, regardless of the value of the field's {@code accessible}
+ * flag.
+ * <p>
+ * If the field is static, and if the returned VarHandle is operated
+ * on, the field's declaring class will be initialized, if it has not
+ * already been initialized.
+ * <p>
+ * Certain access modes of the returned VarHandle are unsupported under
+ * the following conditions:
+ * <ul>
+ * <li>if the field is declared {@code final}, then the write, atomic
+ * update, numeric atomic update, and bitwise atomic update access
+ * modes are unsupported.
+ * <li>if the field type is anything other than {@code byte},
+ * {@code short}, {@code char}, {@code int}, {@code long},
+ * {@code float}, or {@code double} then numeric atomic update
+ * access modes are unsupported.
+ * <li>if the field type is anything other than {@code boolean},
+ * {@code byte}, {@code short}, {@code char}, {@code int} or
+ * {@code long} then bitwise atomic update access modes are
+ * unsupported.
+ * </ul>
+ * <p>
+ * If the field is declared {@code volatile} then the returned VarHandle
+ * will override access to the field (effectively ignore the
+ * {@code volatile} declaration) in accordance to its specified
+ * access modes.
+ * <p>
+ * If the field type is {@code float} or {@code double} then numeric
+ * and atomic update access modes compare values using their bitwise
+ * representation (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @apiNote
+ * Bitwise comparison of {@code float} values or {@code double} values,
+ * as performed by the numeric and atomic update access modes, differ
+ * from the primitive {@code ==} operator and the {@link Float#equals}
+ * and {@link Double#equals} methods, specifically with respect to
+ * comparing NaN values or comparing {@code -0.0} with {@code +0.0}.
+ * Care should be taken when performing a compare and set or a compare
+ * and exchange operation with such values since the operation may
+ * unexpectedly fail.
+ * There are many possible NaN values that are considered to be
+ * {@code NaN} in Java, although no IEEE 754 floating-point operation
+ * provided by Java can distinguish between them. Operation failure can
+ * occur if the expected or witness value is a NaN value and it is
+ * transformed (perhaps in a platform specific manner) into another NaN
+ * value, and thus has a different bitwise representation (see
+ * {@link Float#intBitsToFloat} or {@link Double#longBitsToDouble} for more
+ * details).
+ * The values {@code -0.0} and {@code +0.0} have different bitwise
+ * representations but are considered equal when using the primitive
+ * {@code ==} operator. Operation failure can occur if, for example, a
+ * numeric algorithm computes an expected value to be say {@code -0.0}
+ * and previously computed the witness value to be say {@code +0.0}.
+ * @param f the reflected field, with a field of type {@code T}, and
+ * a declaring class of type {@code R}
+ * @return a VarHandle giving access to non-static fields or a static
+ * field
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
+ * @since 9
+ * @hide
+ */
+ public VarHandle unreflectVarHandle(Field f) throws IllegalAccessException {
+ unsupported("MethodHandles.Lookup.unreflectVarHandle()"); // TODO(b/65872996)
+ return null;
+ }
+ // END Android-changed: OpenJDK 9+181 VarHandle API factory method for bring up purposes.
+
+ /**
+ * Cracks a <a href="MethodHandleInfo.html#directmh">direct method handle</a>
+ * created by this lookup object or a similar one.
+ * Security and access checks are performed to ensure that this lookup object
+ * is capable of reproducing the target method handle.
+ * This means that the cracking may fail if target is a direct method handle
+ * but was created by an unrelated lookup object.
+ * This can happen if the method handle is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>
+ * and was created by a lookup object for a different class.
+ * @param target a direct method handle to crack into symbolic reference components
+ * @return a symbolic reference which can be used to reconstruct this method handle from this lookup object
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails
+ * @exception NullPointerException if the target is {@code null}
+ * @see MethodHandleInfo
+ * @since 1.8
+ */
+ public MethodHandleInfo revealDirect(MethodHandle target) {
+ MethodHandleImpl directTarget = getMethodHandleImpl(target);
+ MethodHandleInfo info = directTarget.reveal();
+
+ try {
+ checkAccess(lookupClass(), info.getDeclaringClass(), info.getModifiers(),
+ info.getName());
+ } catch (IllegalAccessException exception) {
+ throw new IllegalArgumentException("Unable to access memeber.", exception);
+ }
+
+ return info;
+ }
+
+ private boolean hasPrivateAccess() {
+ return (allowedModes & PRIVATE) != 0;
+ }
+
+ /** Check public/protected/private bits on the symbolic reference class and its member. */
+ void checkAccess(Class<?> refc, Class<?> defc, int mods, String methName)
+ throws IllegalAccessException {
+ int allowedModes = this.allowedModes;
+
+ if (Modifier.isProtected(mods) &&
+ defc == Object.class &&
+ "clone".equals(methName) &&
+ refc.isArray()) {
+ // The JVM does this hack also.
+ // (See ClassVerifier::verify_invoke_instructions
+ // and LinkResolver::check_method_accessability.)
+ // Because the JVM does not allow separate methods on array types,
+ // there is no separate method for int[].clone.
+ // All arrays simply inherit Object.clone.
+ // But for access checking logic, we make Object.clone
+ // (normally protected) appear to be public.
+ // Later on, when the DirectMethodHandle is created,
+ // its leading argument will be restricted to the
+ // requested array type.
+ // N.B. The return type is not adjusted, because
+ // that is *not* the bytecode behavior.
+ mods ^= Modifier.PROTECTED | Modifier.PUBLIC;
+ }
+
+ if (Modifier.isProtected(mods) && Modifier.isConstructor(mods)) {
+ // cannot "new" a protected ctor in a different package
+ mods ^= Modifier.PROTECTED;
+ }
+
+ if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers()) && allowedModes != 0)
+ return; // common case
+ int requestedModes = fixmods(mods); // adjust 0 => PACKAGE
+ if ((requestedModes & allowedModes) != 0) {
+ if (VerifyAccess.isMemberAccessible(refc, defc, mods, lookupClass(), allowedModes))
+ return;
+ } else {
+ // Protected members can also be checked as if they were package-private.
+ if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0
+ && VerifyAccess.isSamePackage(defc, lookupClass()))
+ return;
+ }
+
+ throwMakeAccessException(accessFailedMessage(refc, defc, mods), this);
+ }
+
+ String accessFailedMessage(Class<?> refc, Class<?> defc, int mods) {
+ // check the class first:
+ boolean classOK = (Modifier.isPublic(defc.getModifiers()) &&
+ (defc == refc ||
+ Modifier.isPublic(refc.getModifiers())));
+ if (!classOK && (allowedModes & PACKAGE) != 0) {
+ classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), ALL_MODES) &&
+ (defc == refc ||
+ VerifyAccess.isClassAccessible(refc, lookupClass(), ALL_MODES)));
+ }
+ if (!classOK)
+ return "class is not public";
+ if (Modifier.isPublic(mods))
+ return "access to public member failed"; // (how?)
+ if (Modifier.isPrivate(mods))
+ return "member is private";
+ if (Modifier.isProtected(mods))
+ return "member is protected";
+ return "member is private to package";
+ }
+
+ // Android-changed: checkSpecialCaller assumes that ALLOW_NESTMATE_ACCESS = false,
+ // as in upstream OpenJDK.
+ //
+ // private static final boolean ALLOW_NESTMATE_ACCESS = false;
- public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
+ private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+ // Android-changed: No support for TRUSTED lookups. Also construct the
+ // IllegalAccessException by hand because the upstream code implicitly assumes
+ // that the lookupClass == specialCaller.
+ //
+ // if (allowedModes == TRUSTED) return;
+ if (!hasPrivateAccess() || (specialCaller != lookupClass())) {
+ throw new IllegalAccessException("no private access for invokespecial : "
+ + specialCaller + ", from" + this);
+ }
+ }
- public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
+ private void throwMakeAccessException(String message, Object from) throws
+ IllegalAccessException{
+ message = message + ": "+ toString();
+ if (from != null) message += ", from " + from;
+ throw new IllegalAccessException(message);
+ }
- public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
+ private void checkReturnType(Method method, MethodType methodType)
+ throws NoSuchMethodException {
+ if (method.getReturnType() != methodType.rtype()) {
+ throw new NoSuchMethodException(method.getName() + methodType);
+ }
+ }
+ }
+
+ /**
+ * "Cracks" {@code target} to reveal the underlying {@code MethodHandleImpl}.
+ */
+ private static MethodHandleImpl getMethodHandleImpl(MethodHandle target) {
+ // Special case : We implement handles to constructors as transformers,
+ // so we must extract the underlying handle from the transformer.
+ if (target instanceof Transformers.Construct) {
+ target = ((Transformers.Construct) target).getConstructorHandle();
+ }
- public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
+ // Special case: Var-args methods are also implemented as Transformers,
+ // so we should get the underlying handle in that case as well.
+ if (target instanceof Transformers.VarargsCollector) {
+ target = target.asFixedArity();
+ }
- public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
+ if (target instanceof MethodHandleImpl) {
+ return (MethodHandleImpl) target;
+ }
- public MethodHandle unreflect(Method m) throws IllegalAccessException { return null; }
+ throw new IllegalArgumentException(target + " is not a direct handle");
+ }
- public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException { return null; }
+ /**
+ * Produces a method handle giving read access to elements of an array.
+ * The type of the method handle will have a return type of the array's
+ * element type. Its first argument will be the array type,
+ * and the second will be {@code int}.
+ * @param arrayClass an array type
+ * @return a method handle which can load values from the given array type
+ * @throws NullPointerException if the argument is null
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
+ final Class<?> componentType = arrayClass.getComponentType();
+ if (componentType == null) {
+ throw new IllegalArgumentException("Not an array type: " + arrayClass);
+ }
- public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException { return null; }
+ if (componentType.isPrimitive()) {
+ try {
+ return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class,
+ "arrayElementGetter",
+ MethodType.methodType(componentType, arrayClass, int.class));
+ } catch (NoSuchMethodException | IllegalAccessException exception) {
+ throw new AssertionError(exception);
+ }
+ }
- public MethodHandle unreflectGetter(Field f) throws IllegalAccessException { return null; }
+ return new Transformers.ReferenceArrayElementGetter(arrayClass);
+ }
- public MethodHandle unreflectSetter(Field f) throws IllegalAccessException { return null; }
+ /** @hide */ public static byte arrayElementGetter(byte[] array, int i) { return array[i]; }
+ /** @hide */ public static boolean arrayElementGetter(boolean[] array, int i) { return array[i]; }
+ /** @hide */ public static char arrayElementGetter(char[] array, int i) { return array[i]; }
+ /** @hide */ public static short arrayElementGetter(short[] array, int i) { return array[i]; }
+ /** @hide */ public static int arrayElementGetter(int[] array, int i) { return array[i]; }
+ /** @hide */ public static long arrayElementGetter(long[] array, int i) { return array[i]; }
+ /** @hide */ public static float arrayElementGetter(float[] array, int i) { return array[i]; }
+ /** @hide */ public static double arrayElementGetter(double[] array, int i) { return array[i]; }
- public MethodHandleInfo revealDirect(MethodHandle target) { return null; }
+ /**
+ * Produces a method handle giving write access to elements of an array.
+ * The type of the method handle will have a void return type.
+ * Its last argument will be the array's element type.
+ * The first and second arguments will be the array type and int.
+ * @param arrayClass the class of an array
+ * @return a method handle which can store values into the array type
+ * @throws NullPointerException if the argument is null
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
+ final Class<?> componentType = arrayClass.getComponentType();
+ if (componentType == null) {
+ throw new IllegalArgumentException("Not an array type: " + arrayClass);
+ }
+ if (componentType.isPrimitive()) {
+ try {
+ return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class,
+ "arrayElementSetter",
+ MethodType.methodType(void.class, arrayClass, int.class, componentType));
+ } catch (NoSuchMethodException | IllegalAccessException exception) {
+ throw new AssertionError(exception);
+ }
+ }
+
+ return new Transformers.ReferenceArrayElementSetter(arrayClass);
+ }
+
+ /** @hide */
+ public static void arrayElementSetter(byte[] array, int i, byte val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(boolean[] array, int i, boolean val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(char[] array, int i, char val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(short[] array, int i, short val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(int[] array, int i, int val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(long[] array, int i, long val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(float[] array, int i, float val) { array[i] = val; }
+ /** @hide */
+ public static void arrayElementSetter(double[] array, int i, double val) { array[i] = val; }
+
+ // BEGIN Android-changed: OpenJDK 9+181 VarHandle API factory methods for bring up purposes.
+ /**
+ * Produces a VarHandle giving access to elements of an array of type
+ * {@code arrayClass}. The VarHandle's variable type is the component type
+ * of {@code arrayClass} and the list of coordinate types is
+ * {@code (arrayClass, int)}, where the {@code int} coordinate type
+ * corresponds to an argument that is an index into an array.
+ * <p>
+ * Certain access modes of the returned VarHandle are unsupported under
+ * the following conditions:
+ * <ul>
+ * <li>if the component type is anything other than {@code byte},
+ * {@code short}, {@code char}, {@code int}, {@code long},
+ * {@code float}, or {@code double} then numeric atomic update access
+ * modes are unsupported.
+ * <li>if the field type is anything other than {@code boolean},
+ * {@code byte}, {@code short}, {@code char}, {@code int} or
+ * {@code long} then bitwise atomic update access modes are
+ * unsupported.
+ * </ul>
+ * <p>
+ * If the component type is {@code float} or {@code double} then numeric
+ * and atomic update access modes compare values using their bitwise
+ * representation (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @apiNote
+ * Bitwise comparison of {@code float} values or {@code double} values,
+ * as performed by the numeric and atomic update access modes, differ
+ * from the primitive {@code ==} operator and the {@link Float#equals}
+ * and {@link Double#equals} methods, specifically with respect to
+ * comparing NaN values or comparing {@code -0.0} with {@code +0.0}.
+ * Care should be taken when performing a compare and set or a compare
+ * and exchange operation with such values since the operation may
+ * unexpectedly fail.
+ * There are many possible NaN values that are considered to be
+ * {@code NaN} in Java, although no IEEE 754 floating-point operation
+ * provided by Java can distinguish between them. Operation failure can
+ * occur if the expected or witness value is a NaN value and it is
+ * transformed (perhaps in a platform specific manner) into another NaN
+ * value, and thus has a different bitwise representation (see
+ * {@link Float#intBitsToFloat} or {@link Double#longBitsToDouble} for more
+ * details).
+ * The values {@code -0.0} and {@code +0.0} have different bitwise
+ * representations but are considered equal when using the primitive
+ * {@code ==} operator. Operation failure can occur if, for example, a
+ * numeric algorithm computes an expected value to be say {@code -0.0}
+ * and previously computed the witness value to be say {@code +0.0}.
+ * @param arrayClass the class of an array, of type {@code T[]}
+ * @return a VarHandle giving access to elements of an array
+ * @throws NullPointerException if the arrayClass is null
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ * @since 9
+ * @hide
+ */
+ public static
+ VarHandle arrayElementVarHandle(Class<?> arrayClass) throws IllegalArgumentException {
+ unsupported("MethodHandles.arrayElementVarHandle()"); // TODO(b/65872996)
+ return null;
}
+ /**
+ * Produces a VarHandle giving access to elements of a {@code byte[]} array
+ * viewed as if it were a different primitive array type, such as
+ * {@code int[]} or {@code long[]}.
+ * The VarHandle's variable type is the component type of
+ * {@code viewArrayClass} and the list of coordinate types is
+ * {@code (byte[], int)}, where the {@code int} coordinate type
+ * corresponds to an argument that is an index into a {@code byte[]} array.
+ * The returned VarHandle accesses bytes at an index in a {@code byte[]}
+ * array, composing bytes to or from a value of the component type of
+ * {@code viewArrayClass} according to the given endianness.
+ * <p>
+ * The supported component types (variables types) are {@code short},
+ * {@code char}, {@code int}, {@code long}, {@code float} and
+ * {@code double}.
+ * <p>
+ * Access of bytes at a given index will result in an
+ * {@code IndexOutOfBoundsException} if the index is less than {@code 0}
+ * or greater than the {@code byte[]} array length minus the size (in bytes)
+ * of {@code T}.
+ * <p>
+ * Access of bytes at an index may be aligned or misaligned for {@code T},
+ * with respect to the underlying memory address, {@code A} say, associated
+ * with the array and index.
+ * If access is misaligned then access for anything other than the
+ * {@code get} and {@code set} access modes will result in an
+ * {@code IllegalStateException}. In such cases atomic access is only
+ * guaranteed with respect to the largest power of two that divides the GCD
+ * of {@code A} and the size (in bytes) of {@code T}.
+ * If access is aligned then following access modes are supported and are
+ * guaranteed to support atomic access:
+ * <ul>
+ * <li>read write access modes for all {@code T}, with the exception of
+ * access modes {@code get} and {@code set} for {@code long} and
+ * {@code double} on 32-bit platforms.
+ * <li>atomic update access modes for {@code int}, {@code long},
+ * {@code float} or {@code double}.
+ * (Future major platform releases of the JDK may support additional
+ * types for certain currently unsupported access modes.)
+ * <li>numeric atomic update access modes for {@code int} and {@code long}.
+ * (Future major platform releases of the JDK may support additional
+ * numeric types for certain currently unsupported access modes.)
+ * <li>bitwise atomic update access modes for {@code int} and {@code long}.
+ * (Future major platform releases of the JDK may support additional
+ * numeric types for certain currently unsupported access modes.)
+ * </ul>
+ * <p>
+ * Misaligned access, and therefore atomicity guarantees, may be determined
+ * for {@code byte[]} arrays without operating on a specific array. Given
+ * an {@code index}, {@code T} and it's corresponding boxed type,
+ * {@code T_BOX}, misalignment may be determined as follows:
+ * <pre>{@code
+ * int sizeOfT = T_BOX.BYTES; // size in bytes of T
+ * int misalignedAtZeroIndex = ByteBuffer.wrap(new byte[0]).
+ * alignmentOffset(0, sizeOfT);
+ * int misalignedAtIndex = (misalignedAtZeroIndex + index) % sizeOfT;
+ * boolean isMisaligned = misalignedAtIndex != 0;
+ * }</pre>
+ * <p>
+ * If the variable type is {@code float} or {@code double} then atomic
+ * update access modes compare values using their bitwise representation
+ * (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @param viewArrayClass the view array class, with a component type of
+ * type {@code T}
+ * @param byteOrder the endianness of the view array elements, as
+ * stored in the underlying {@code byte} array
+ * @return a VarHandle giving access to elements of a {@code byte[]} array
+ * viewed as if elements corresponding to the components type of the view
+ * array class
+ * @throws NullPointerException if viewArrayClass or byteOrder is null
+ * @throws IllegalArgumentException if viewArrayClass is not an array type
+ * @throws UnsupportedOperationException if the component type of
+ * viewArrayClass is not supported as a variable type
+ * @since 9
+ * @hide
+ */
public static
- MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException { return null; }
+ VarHandle byteArrayViewVarHandle(Class<?> viewArrayClass,
+ ByteOrder byteOrder) throws IllegalArgumentException {
+ unsupported("MethodHandles.byteArrayViewVarHandle()"); // TODO(b/65872996)
+ return null;
+ }
+ /**
+ * Produces a VarHandle giving access to elements of a {@code ByteBuffer}
+ * viewed as if it were an array of elements of a different primitive
+ * component type to that of {@code byte}, such as {@code int[]} or
+ * {@code long[]}.
+ * The VarHandle's variable type is the component type of
+ * {@code viewArrayClass} and the list of coordinate types is
+ * {@code (ByteBuffer, int)}, where the {@code int} coordinate type
+ * corresponds to an argument that is an index into a {@code byte[]} array.
+ * The returned VarHandle accesses bytes at an index in a
+ * {@code ByteBuffer}, composing bytes to or from a value of the component
+ * type of {@code viewArrayClass} according to the given endianness.
+ * <p>
+ * The supported component types (variables types) are {@code short},
+ * {@code char}, {@code int}, {@code long}, {@code float} and
+ * {@code double}.
+ * <p>
+ * Access will result in a {@code ReadOnlyBufferException} for anything
+ * other than the read access modes if the {@code ByteBuffer} is read-only.
+ * <p>
+ * Access of bytes at a given index will result in an
+ * {@code IndexOutOfBoundsException} if the index is less than {@code 0}
+ * or greater than the {@code ByteBuffer} limit minus the size (in bytes) of
+ * {@code T}.
+ * <p>
+ * Access of bytes at an index may be aligned or misaligned for {@code T},
+ * with respect to the underlying memory address, {@code A} say, associated
+ * with the {@code ByteBuffer} and index.
+ * If access is misaligned then access for anything other than the
+ * {@code get} and {@code set} access modes will result in an
+ * {@code IllegalStateException}. In such cases atomic access is only
+ * guaranteed with respect to the largest power of two that divides the GCD
+ * of {@code A} and the size (in bytes) of {@code T}.
+ * If access is aligned then following access modes are supported and are
+ * guaranteed to support atomic access:
+ * <ul>
+ * <li>read write access modes for all {@code T}, with the exception of
+ * access modes {@code get} and {@code set} for {@code long} and
+ * {@code double} on 32-bit platforms.
+ * <li>atomic update access modes for {@code int}, {@code long},
+ * {@code float} or {@code double}.
+ * (Future major platform releases of the JDK may support additional
+ * types for certain currently unsupported access modes.)
+ * <li>numeric atomic update access modes for {@code int} and {@code long}.
+ * (Future major platform releases of the JDK may support additional
+ * numeric types for certain currently unsupported access modes.)
+ * <li>bitwise atomic update access modes for {@code int} and {@code long}.
+ * (Future major platform releases of the JDK may support additional
+ * numeric types for certain currently unsupported access modes.)
+ * </ul>
+ * <p>
+ * Misaligned access, and therefore atomicity guarantees, may be determined
+ * for a {@code ByteBuffer}, {@code bb} (direct or otherwise), an
+ * {@code index}, {@code T} and it's corresponding boxed type,
+ * {@code T_BOX}, as follows:
+ * <pre>{@code
+ * int sizeOfT = T_BOX.BYTES; // size in bytes of T
+ * ByteBuffer bb = ...
+ * int misalignedAtIndex = bb.alignmentOffset(index, sizeOfT);
+ * boolean isMisaligned = misalignedAtIndex != 0;
+ * }</pre>
+ * <p>
+ * If the variable type is {@code float} or {@code double} then atomic
+ * update access modes compare values using their bitwise representation
+ * (see {@link Float#floatToRawIntBits} and
+ * {@link Double#doubleToRawLongBits}, respectively).
+ * @param viewArrayClass the view array class, with a component type of
+ * type {@code T}
+ * @param byteOrder the endianness of the view array elements, as
+ * stored in the underlying {@code ByteBuffer} (Note this overrides the
+ * endianness of a {@code ByteBuffer})
+ * @return a VarHandle giving access to elements of a {@code ByteBuffer}
+ * viewed as if elements corresponding to the components type of the view
+ * array class
+ * @throws NullPointerException if viewArrayClass or byteOrder is null
+ * @throws IllegalArgumentException if viewArrayClass is not an array type
+ * @throws UnsupportedOperationException if the component type of
+ * viewArrayClass is not supported as a variable type
+ * @since 9
+ * @hide
+ */
public static
- MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException { return null; }
+ VarHandle byteBufferViewVarHandle(Class<?> viewArrayClass,
+ ByteOrder byteOrder) throws IllegalArgumentException {
+
+ unsupported("MethodHandles.byteBufferViewVarHandle()"); // TODO(b/65872996)
+ return null;
+ }
+ // END Android-changed: OpenJDK 9+181 VarHandle API factory methods for bring up purposes.
+
+ /// method handle invocation (reflective style)
+
+ /**
+ * Produces a method handle which will invoke any method handle of the
+ * given {@code type}, with a given number of trailing arguments replaced by
+ * a single trailing {@code Object[]} array.
+ * The resulting invoker will be a method handle with the following
+ * arguments:
+ * <ul>
+ * <li>a single {@code MethodHandle} target
+ * <li>zero or more leading values (counted by {@code leadingArgCount})
+ * <li>an {@code Object[]} array containing trailing arguments
+ * </ul>
+ * <p>
+ * The invoker will invoke its target like a call to {@link MethodHandle#invoke invoke} with
+ * the indicated {@code type}.
+ * That is, if the target is exactly of the given {@code type}, it will behave
+ * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
+ * is used to convert the target to the required {@code type}.
+ * <p>
+ * The type of the returned invoker will not be the given {@code type}, but rather
+ * will have all parameters except the first {@code leadingArgCount}
+ * replaced by a single array of type {@code Object[]}, which will be
+ * the final parameter.
+ * <p>
+ * Before invoking its target, the invoker will spread the final array, apply
+ * reference casts as necessary, and unbox and widen primitive arguments.
+ * If, when the invoker is called, the supplied array argument does
+ * not have the correct number of elements, the invoker will throw
+ * an {@link IllegalArgumentException} instead of invoking the target.
+ * <p>
+ * This method is equivalent to the following code (though it may be more efficient):
+ * <blockquote><pre>{@code
+MethodHandle invoker = MethodHandles.invoker(type);
+int spreadArgCount = type.parameterCount() - leadingArgCount;
+invoker = invoker.asSpreader(Object[].class, spreadArgCount);
+return invoker;
+ * }</pre></blockquote>
+ * This method throws no reflective or security exceptions.
+ * @param type the desired target type
+ * @param leadingArgCount number of fixed arguments, to be passed unchanged to the target
+ * @return a method handle suitable for invoking any method handle of the given type
+ * @throws NullPointerException if {@code type} is null
+ * @throws IllegalArgumentException if {@code leadingArgCount} is not in
+ * the range from 0 to {@code type.parameterCount()} inclusive,
+ * or if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ */
+ static public
+ MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
+ if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
+ throw newIllegalArgumentException("bad argument count", leadingArgCount);
+
+ MethodHandle invoker = MethodHandles.invoker(type);
+ int spreadArgCount = type.parameterCount() - leadingArgCount;
+ invoker = invoker.asSpreader(Object[].class, spreadArgCount);
+ return invoker;
+ }
+
+ /**
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
+ * The resulting invoker will have a type which is
+ * exactly equal to the desired type, except that it will accept
+ * an additional leading argument of type {@code MethodHandle}.
+ * <p>
+ * This method is equivalent to the following code (though it may be more efficient):
+ * {@code publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)}
+ *
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * Invoker method handles can be useful when working with variable method handles
+ * of unknown types.
+ * For example, to emulate an {@code invokeExact} call to a variable method
+ * handle {@code M}, extract its type {@code T},
+ * look up the invoker method {@code X} for {@code T},
+ * and call the invoker method, as {@code X.invoke(T, A...)}.
+ * (It would not work to call {@code X.invokeExact}, since the type {@code T}
+ * is unknown.)
+ * If spreading, collecting, or other argument transformations are required,
+ * they can be applied once to the invoker {@code X} and reused on many {@code M}
+ * method handle values, as long as they are compatible with the type of {@code X}.
+ * <p style="font-size:smaller;">
+ * <em>(Note: The invoker method is not available via the Core Reflection API.
+ * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
+ * on the declared {@code invokeExact} or {@code invoke} method will raise an
+ * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
+ * <p>
+ * This method throws no reflective or security exceptions.
+ * @param type the desired target type
+ * @return a method handle suitable for invoking any method handle of the given type
+ * @throws IllegalArgumentException if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ */
+ static public
+ MethodHandle exactInvoker(MethodType type) {
+ return new Transformers.Invoker(type, true /* isExactInvoker */);
+ }
+ /**
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}.
+ * The resulting invoker will have a type which is
+ * exactly equal to the desired type, except that it will accept
+ * an additional leading argument of type {@code MethodHandle}.
+ * <p>
+ * Before invoking its target, if the target differs from the expected type,
+ * the invoker will apply reference casts as
+ * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
+ * Similarly, the return value will be converted as necessary.
+ * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
+ * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
+ * <p>
+ * This method is equivalent to the following code (though it may be more efficient):
+ * {@code publicLookup().findVirtual(MethodHandle.class, "invoke", type)}
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * A {@linkplain MethodType#genericMethodType general method type} is one which
+ * mentions only {@code Object} arguments and return values.
+ * An invoker for such a type is capable of calling any method handle
+ * of the same arity as the general type.
+ * <p style="font-size:smaller;">
+ * <em>(Note: The invoker method is not available via the Core Reflection API.
+ * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
+ * on the declared {@code invokeExact} or {@code invoke} method will raise an
+ * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
+ * <p>
+ * This method throws no reflective or security exceptions.
+ * @param type the desired target type
+ * @return a method handle suitable for invoking any method handle convertible to the given type
+ * @throws IllegalArgumentException if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ */
static public
- MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { return null; }
+ MethodHandle invoker(MethodType type) {
+ return new Transformers.Invoker(type, false /* isExactInvoker */);
+ }
+ /**
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke a signature-polymorphic access mode method on any VarHandle whose
+ * associated access mode type is compatible with the given type.
+ * The resulting invoker will have a type which is exactly equal to the
+ * desired given type, except that it will accept an additional leading
+ * argument of type {@code VarHandle}.
+ *
+ * @param accessMode the VarHandle access mode
+ * @param type the desired target type
+ * @return a method handle suitable for invoking an access mode method of
+ * any VarHandle whose access mode type is of the given type.
+ * @since 9
+ * @hide
+ */
static public
- MethodHandle exactInvoker(MethodType type) { return null; }
+ MethodHandle varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type) {
+ unsupported("MethodHandles.varHandleExactInvoker()"); // TODO(b/65872996)
+ return null;
+ }
+ /**
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke a signature-polymorphic access mode method on any VarHandle whose
+ * associated access mode type is compatible with the given type.
+ * The resulting invoker will have a type which is exactly equal to the
+ * desired given type, except that it will accept an additional leading
+ * argument of type {@code VarHandle}.
+ * <p>
+ * Before invoking its target, if the access mode type differs from the
+ * desired given type, the invoker will apply reference casts as necessary
+ * and box, unbox, or widen primitive values, as if by
+ * {@link MethodHandle#asType asType}. Similarly, the return value will be
+ * converted as necessary.
+ * <p>
+ * This method is equivalent to the following code (though it may be more
+ * efficient): {@code publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)}
+ *
+ * @param accessMode the VarHandle access mode
+ * @param type the desired target type
+ * @return a method handle suitable for invoking an access mode method of
+ * any VarHandle whose access mode type is convertible to the given
+ * type.
+ * @since 9
+ * @hide
+ */
static public
- MethodHandle invoker(MethodType type) { return null; }
+ MethodHandle varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type) {
+ unsupported("MethodHandles.varHandleInvoker()"); // TODO(b/65872996)
+ return null;
+ }
+ // Android-changed: Basic invokers are not supported.
+ //
+ // static /*non-public*/
+ // MethodHandle basicInvoker(MethodType type) {
+ // return type.invokers().basicInvoker();
+ // }
+
+ /// method handle modification (creation from other method handles)
+
+ /**
+ * Produces a method handle which adapts the type of the
+ * given method handle to a new type by pairwise argument and return type conversion.
+ * The original type and new type must have the same number of arguments.
+ * The resulting method handle is guaranteed to report a type
+ * which is equal to the desired new type.
+ * <p>
+ * If the original type and new type are equal, returns target.
+ * <p>
+ * The same conversions are allowed as for {@link MethodHandle#asType MethodHandle.asType},
+ * and some additional conversions are also applied if those conversions fail.
+ * Given types <em>T0</em>, <em>T1</em>, one of the following conversions is applied
+ * if possible, before or instead of any conversions done by {@code asType}:
+ * <ul>
+ * <li>If <em>T0</em> and <em>T1</em> are references, and <em>T1</em> is an interface type,
+ * then the value of type <em>T0</em> is passed as a <em>T1</em> without a cast.
+ * (This treatment of interfaces follows the usage of the bytecode verifier.)
+ * <li>If <em>T0</em> is boolean and <em>T1</em> is another primitive,
+ * the boolean is converted to a byte value, 1 for true, 0 for false.
+ * (This treatment follows the usage of the bytecode verifier.)
+ * <li>If <em>T1</em> is boolean and <em>T0</em> is another primitive,
+ * <em>T0</em> is converted to byte via Java casting conversion (JLS 5.5),
+ * and the low order bit of the result is tested, as if by {@code (x & 1) != 0}.
+ * <li>If <em>T0</em> and <em>T1</em> are primitives other than boolean,
+ * then a Java casting conversion (JLS 5.5) is applied.
+ * (Specifically, <em>T0</em> will convert to <em>T1</em> by
+ * widening and/or narrowing.)
+ * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
+ * conversion will be applied at runtime, possibly followed
+ * by a Java casting conversion (JLS 5.5) on the primitive value,
+ * possibly followed by a conversion from byte to boolean by testing
+ * the low-order bit.
+ * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive,
+ * and if the reference is null at runtime, a zero value is introduced.
+ * </ul>
+ * @param target the method handle to invoke after arguments are retyped
+ * @param newType the expected type of the new method handle
+ * @return a method handle which delegates to the target after performing
+ * any necessary argument conversions, and arranges for any
+ * necessary return value conversions
+ * @throws NullPointerException if either argument is null
+ * @throws WrongMethodTypeException if the conversion cannot be made
+ * @see MethodHandle#asType
+ */
public static
- MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { return null; }
+ MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
+ explicitCastArgumentsChecks(target, newType);
+ // use the asTypeCache when possible:
+ MethodType oldType = target.type();
+ if (oldType == newType) return target;
+ if (oldType.explicitCastEquivalentToAsType(newType)) {
+ return target.asFixedArity().asType(newType);
+ }
+ return new Transformers.ExplicitCastArguments(target, newType);
+ }
+
+ private static void explicitCastArgumentsChecks(MethodHandle target, MethodType newType) {
+ if (target.type().parameterCount() != newType.parameterCount()) {
+ throw new WrongMethodTypeException("cannot explicitly cast " + target + " to " + newType);
+ }
+ }
+
+ /**
+ * Produces a method handle which adapts the calling sequence of the
+ * given method handle to a new type, by reordering the arguments.
+ * The resulting method handle is guaranteed to report a type
+ * which is equal to the desired new type.
+ * <p>
+ * The given array controls the reordering.
+ * Call {@code #I} the number of incoming parameters (the value
+ * {@code newType.parameterCount()}, and call {@code #O} the number
+ * of outgoing parameters (the value {@code target.type().parameterCount()}).
+ * Then the length of the reordering array must be {@code #O},
+ * and each element must be a non-negative number less than {@code #I}.
+ * For every {@code N} less than {@code #O}, the {@code N}-th
+ * outgoing argument will be taken from the {@code I}-th incoming
+ * argument, where {@code I} is {@code reorder[N]}.
+ * <p>
+ * No argument or return value conversions are applied.
+ * The type of each incoming argument, as determined by {@code newType},
+ * must be identical to the type of the corresponding outgoing parameter
+ * or parameters in the target method handle.
+ * The return type of {@code newType} must be identical to the return
+ * type of the original target.
+ * <p>
+ * The reordering array need not specify an actual permutation.
+ * An incoming argument will be duplicated if its index appears
+ * more than once in the array, and an incoming argument will be dropped
+ * if its index does not appear in the array.
+ * As in the case of {@link #dropArguments(MethodHandle,int,List) dropArguments},
+ * incoming arguments which are not mentioned in the reordering array
+ * are may be any type, as determined only by {@code newType}.
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodType intfn1 = methodType(int.class, int.class);
+MethodType intfn2 = methodType(int.class, int.class, int.class);
+MethodHandle sub = ... (int x, int y) -> (x-y) ...;
+assert(sub.type().equals(intfn2));
+MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
+MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
+assert((int)rsub.invokeExact(1, 100) == 99);
+MethodHandle add = ... (int x, int y) -> (x+y) ...;
+assert(add.type().equals(intfn2));
+MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
+assert(twice.type().equals(intfn1));
+assert((int)twice.invokeExact(21) == 42);
+ * }</pre></blockquote>
+ * @param target the method handle to invoke after arguments are reordered
+ * @param newType the expected type of the new method handle
+ * @param reorder an index array which controls the reordering
+ * @return a method handle which delegates to the target after it
+ * drops unused arguments and moves and/or duplicates the other arguments
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if the index array length is not equal to
+ * the arity of the target, or if any index array element
+ * not a valid index for a parameter of {@code newType},
+ * or if two corresponding parameter types in
+ * {@code target.type()} and {@code newType} are not identical,
+ */
public static
- MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { return null; }
+ MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
+ reorder = reorder.clone(); // get a private copy
+ MethodType oldType = target.type();
+ permuteArgumentChecks(reorder, newType, oldType);
+
+ return new Transformers.PermuteArguments(newType, target, reorder);
+ }
+ // Android-changed: findFirstDupOrDrop is unused and removed.
+ // private static int findFirstDupOrDrop(int[] reorder, int newArity);
+
+ private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
+ if (newType.returnType() != oldType.returnType())
+ throw newIllegalArgumentException("return types do not match",
+ oldType, newType);
+ if (reorder.length == oldType.parameterCount()) {
+ int limit = newType.parameterCount();
+ boolean bad = false;
+ for (int j = 0; j < reorder.length; j++) {
+ int i = reorder[j];
+ if (i < 0 || i >= limit) {
+ bad = true; break;
+ }
+ Class<?> src = newType.parameterType(i);
+ Class<?> dst = oldType.parameterType(j);
+ if (src != dst)
+ throw newIllegalArgumentException("parameter types do not match after reorder",
+ oldType, newType);
+ }
+ if (!bad) return true;
+ }
+ throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
+ }
+
+ /**
+ * Produces a method handle of the requested return type which returns the given
+ * constant value every time it is invoked.
+ * <p>
+ * Before the method handle is returned, the passed-in value is converted to the requested type.
+ * If the requested type is primitive, widening primitive conversions are attempted,
+ * else reference conversions are attempted.
+ * <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)}.
+ * @param type the return type of the desired method handle
+ * @param value the value to return
+ * @return a method handle of the given return type and no arguments, which always returns the given value
+ * @throws NullPointerException if the {@code type} argument is null
+ * @throws ClassCastException if the value cannot be converted to the required return type
+ * @throws IllegalArgumentException if the given type is {@code void.class}
+ */
public static
- MethodHandle constant(Class<?> type, Object value) { return null; }
+ MethodHandle constant(Class<?> type, Object value) {
+ if (type.isPrimitive()) {
+ if (type == void.class)
+ throw newIllegalArgumentException("void type");
+ Wrapper w = Wrapper.forPrimitiveType(type);
+ value = w.convert(value, type);
+ }
+ return new Transformers.Constant(type, value);
+ }
+
+ /**
+ * Produces a method handle which returns its sole argument when invoked.
+ * @param type the type of the sole parameter and return value of the desired method handle
+ * @return a unary method handle which accepts and returns the given type
+ * @throws NullPointerException if the argument is null
+ * @throws IllegalArgumentException if the given type is {@code void.class}
+ */
public static
- MethodHandle identity(Class<?> type) { return null; }
+ MethodHandle identity(Class<?> type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ if (type.isPrimitive()) {
+ try {
+ return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class, "identity",
+ MethodType.methodType(type, type));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
+ }
+ return new Transformers.ReferenceIdentity(type);
+ }
+
+ /** @hide */ public static byte identity(byte val) { return val; }
+ /** @hide */ public static boolean identity(boolean val) { return val; }
+ /** @hide */ public static char identity(char val) { return val; }
+ /** @hide */ public static short identity(short val) { return val; }
+ /** @hide */ public static int identity(int val) { return val; }
+ /** @hide */ public static long identity(long val) { return val; }
+ /** @hide */ public static float identity(float val) { return val; }
+ /** @hide */ public static double identity(double val) { return val; }
+
+ /**
+ * Provides a target method handle with one or more <em>bound arguments</em>
+ * in advance of the method handle's invocation.
+ * The formal parameters to the target corresponding to the bound
+ * arguments are called <em>bound parameters</em>.
+ * Returns a new method handle which saves away the bound arguments.
+ * When it is invoked, it receives arguments for any non-bound parameters,
+ * binds the saved arguments to their corresponding parameters,
+ * and calls the original target.
+ * <p>
+ * The type of the new method handle will drop the types for the bound
+ * parameters from the original target type, since the new method handle
+ * will no longer require those arguments to be supplied by its callers.
+ * <p>
+ * Each given argument object must match the corresponding bound parameter type.
+ * If a bound parameter type is a primitive, the argument object
+ * must be a wrapper, and will be unboxed to produce the primitive value.
+ * <p>
+ * The {@code pos} argument selects which parameters are to be bound.
+ * It may range between zero and <i>N-L</i> (inclusively),
+ * where <i>N</i> is the arity of the target method handle
+ * and <i>L</i> is the length of the values array.
+ * @param target the method handle to invoke after the argument is inserted
+ * @param pos where to insert the argument (zero for the first)
+ * @param values the series of arguments to insert
+ * @return a method handle which inserts an additional argument,
+ * before calling the original method handle
+ * @throws NullPointerException if the target or the {@code values} array is null
+ * @see MethodHandle#bindTo
+ */
public static
- MethodHandle insertArguments(MethodHandle target, int pos, Object... values) { return null; }
+ MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
+ int insCount = values.length;
+ Class<?>[] ptypes = insertArgumentsChecks(target, insCount, pos);
+ if (insCount == 0) {
+ return target;
+ }
+ // Throw ClassCastExceptions early if we can't cast any of the provided values
+ // to the required type.
+ for (int i = 0; i < insCount; i++) {
+ final Class<?> ptype = ptypes[pos + i];
+ if (!ptype.isPrimitive()) {
+ ptypes[pos + i].cast(values[i]);
+ } else {
+ // Will throw a ClassCastException if something terrible happens.
+ values[i] = Wrapper.forPrimitiveType(ptype).convert(values[i], ptype);
+ }
+ }
+
+ return new Transformers.InsertArguments(target, pos, values);
+ }
+
+ // Android-changed: insertArgumentPrimitive is unused.
+ //
+ // private static BoundMethodHandle insertArgumentPrimitive(BoundMethodHandle result, int pos,
+ // Class<?> ptype, Object value) {
+ // Wrapper w = Wrapper.forPrimitiveType(ptype);
+ // // perform unboxing and/or primitive conversion
+ // value = w.convert(value, ptype);
+ // switch (w) {
+ // case INT: return result.bindArgumentI(pos, (int)value);
+ // case LONG: return result.bindArgumentJ(pos, (long)value);
+ // case FLOAT: return result.bindArgumentF(pos, (float)value);
+ // case DOUBLE: return result.bindArgumentD(pos, (double)value);
+ // default: return result.bindArgumentI(pos, ValueConversions.widenSubword(value));
+ // }
+ // }
+
+ private static Class<?>[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException {
+ MethodType oldType = target.type();
+ int outargs = oldType.parameterCount();
+ int inargs = outargs - insCount;
+ if (inargs < 0)
+ throw newIllegalArgumentException("too many values to insert");
+ if (pos < 0 || pos > inargs)
+ throw newIllegalArgumentException("no argument type to append");
+ return oldType.ptypes();
+ }
+
+ /**
+ * Produces a method handle which will discard some dummy arguments
+ * before calling some other specified <i>target</i> method handle.
+ * The type of the new method handle will be the same as the target's type,
+ * except it will also include the dummy argument types,
+ * at some given position.
+ * <p>
+ * The {@code pos} argument may range between zero and <i>N</i>,
+ * where <i>N</i> is the arity of the target.
+ * If {@code pos} is zero, the dummy arguments will precede
+ * the target's real arguments; if {@code pos} is <i>N</i>
+ * they will come after.
+ * <p>
+ * <b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+assertEquals("xy", (String) cat.invokeExact("x", "y"));
+MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
+MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
+assertEquals(bigType, d0.type());
+assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
+ * }</pre></blockquote>
+ * <p>
+ * This method is also equivalent to the following code:
+ * <blockquote><pre>
+ * {@link #dropArguments(MethodHandle,int,Class...) dropArguments}{@code (target, pos, valueTypes.toArray(new Class[0]))}
+ * </pre></blockquote>
+ * @param target the method handle to invoke after the arguments are dropped
+ * @param valueTypes the type(s) of the argument(s) to drop
+ * @param pos position of first argument to drop (zero for the leftmost)
+ * @return a method handle which drops arguments of the given types,
+ * before calling the original method handle
+ * @throws NullPointerException if the target is null,
+ * or if the {@code valueTypes} list or any of its elements is null
+ * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
+ * or if {@code pos} is negative or greater than the arity of the target,
+ * or if the new method handle's type would have too many parameters
+ */
public static
- MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) { return null; }
+ MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
+ valueTypes = copyTypes(valueTypes);
+ MethodType oldType = target.type(); // get NPE
+ int dropped = dropArgumentChecks(oldType, pos, valueTypes);
+
+ MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
+ if (dropped == 0) {
+ return target;
+ }
+
+ return new Transformers.DropArguments(newType, target, pos, valueTypes.size());
+ }
+
+ private static List<Class<?>> copyTypes(List<Class<?>> types) {
+ Object[] a = types.toArray();
+ return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
+ }
+
+ private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
+ int dropped = valueTypes.size();
+ MethodType.checkSlotCount(dropped);
+ int outargs = oldType.parameterCount();
+ int inargs = outargs + dropped;
+ if (pos < 0 || pos > outargs)
+ throw newIllegalArgumentException("no argument type to remove"
+ + Arrays.asList(oldType, pos, valueTypes, inargs, outargs)
+ );
+ return dropped;
+ }
+ /**
+ * Produces a method handle which will discard some dummy arguments
+ * before calling some other specified <i>target</i> method handle.
+ * The type of the new method handle will be the same as the target's type,
+ * except it will also include the dummy argument types,
+ * at some given position.
+ * <p>
+ * The {@code pos} argument may range between zero and <i>N</i>,
+ * where <i>N</i> is the arity of the target.
+ * If {@code pos} is zero, the dummy arguments will precede
+ * the target's real arguments; if {@code pos} is <i>N</i>
+ * they will come after.
+ * <p>
+ * <b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+assertEquals("xy", (String) cat.invokeExact("x", "y"));
+MethodHandle d0 = dropArguments(cat, 0, String.class);
+assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
+MethodHandle d1 = dropArguments(cat, 1, String.class);
+assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
+MethodHandle d2 = dropArguments(cat, 2, String.class);
+assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
+MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
+assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
+ * }</pre></blockquote>
+ * <p>
+ * This method is also equivalent to the following code:
+ * <blockquote><pre>
+ * {@link #dropArguments(MethodHandle,int,List) dropArguments}{@code (target, pos, Arrays.asList(valueTypes))}
+ * </pre></blockquote>
+ * @param target the method handle to invoke after the arguments are dropped
+ * @param valueTypes the type(s) of the argument(s) to drop
+ * @param pos position of first argument to drop (zero for the leftmost)
+ * @return a method handle which drops arguments of the given types,
+ * before calling the original method handle
+ * @throws NullPointerException if the target is null,
+ * or if the {@code valueTypes} array or any of its elements is null
+ * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
+ * or if {@code pos} is negative or greater than the arity of the target,
+ * or if the new method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ */
public static
- MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) { return null; }
+ MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
+ return dropArguments(target, pos, Arrays.asList(valueTypes));
+ }
+ /**
+ * Adapts a target method handle by pre-processing
+ * one or more of its arguments, each with its own unary filter function,
+ * and then calling the target with each pre-processed argument
+ * replaced by the result of its corresponding filter function.
+ * <p>
+ * The pre-processing is performed by one or more method handles,
+ * specified in the elements of the {@code filters} array.
+ * The first element of the filter array corresponds to the {@code pos}
+ * argument of the target, and so on in sequence.
+ * <p>
+ * Null arguments in the array are treated as identity functions,
+ * and the corresponding arguments left unchanged.
+ * (If there are no non-null elements in the array, the original target is returned.)
+ * Each filter is applied to the corresponding argument of the adapter.
+ * <p>
+ * If a filter {@code F} applies to the {@code N}th argument of
+ * the target, then {@code F} must be a method handle which
+ * takes exactly one argument. The type of {@code F}'s sole argument
+ * replaces the corresponding argument type of the target
+ * in the resulting adapted method handle.
+ * The return type of {@code F} must be identical to the corresponding
+ * parameter type of the target.
+ * <p>
+ * It is an error if there are elements of {@code filters}
+ * (null or not)
+ * which do not correspond to argument positions in the target.
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle upcase = lookup().findVirtual(String.class,
+ "toUpperCase", methodType(String.class));
+assertEquals("xy", (String) cat.invokeExact("x", "y"));
+MethodHandle f0 = filterArguments(cat, 0, upcase);
+assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
+MethodHandle f1 = filterArguments(cat, 1, upcase);
+assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
+MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
+assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
+ * }</pre></blockquote>
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * V target(P... p, A[i]... a[i], B... b);
+ * A[i] filter[i](V[i]);
+ * T adapter(P... p, V[i]... v[i], B... b) {
+ * return target(p..., f[i](v[i])..., b...);
+ * }
+ * }</pre></blockquote>
+ *
+ * @param target the method handle to invoke after arguments are filtered
+ * @param pos the position of the first argument to filter
+ * @param filters method handles to call initially on filtered arguments
+ * @return method handle which incorporates the specified argument filtering logic
+ * @throws NullPointerException if the target is null
+ * or if the {@code filters} array is null
+ * @throws IllegalArgumentException if a non-null element of {@code filters}
+ * does not match a corresponding argument type of target as described above,
+ * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()},
+ * or if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ */
public static
- MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { return null; }
+ MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
+ filterArgumentsCheckArity(target, pos, filters);
+
+ for (int i = 0; i < filters.length; ++i) {
+ filterArgumentChecks(target, i + pos, filters[i]);
+ }
+
+ return new Transformers.FilterArguments(target, pos, filters);
+ }
+
+ private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
+ MethodType targetType = target.type();
+ int maxPos = targetType.parameterCount();
+ if (pos + filters.length > maxPos)
+ throw newIllegalArgumentException("too many filters");
+ }
+
+ private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
+ MethodType targetType = target.type();
+ MethodType filterType = filter.type();
+ if (filterType.parameterCount() != 1
+ || filterType.returnType() != targetType.parameterType(pos))
+ throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+ }
+
+ /**
+ * Adapts a target method handle by pre-processing
+ * a sub-sequence of its arguments with a filter (another method handle).
+ * The pre-processed arguments are replaced by the result (if any) of the
+ * filter function.
+ * The target is then called on the modified (usually shortened) argument list.
+ * <p>
+ * If the filter returns a value, the target must accept that value as
+ * its argument in position {@code pos}, preceded and/or followed by
+ * any arguments not passed to the filter.
+ * If the filter returns void, the target must accept all arguments
+ * not passed to the filter.
+ * No arguments are reordered, and a result returned from the filter
+ * replaces (in order) the whole subsequence of arguments originally
+ * passed to the adapter.
+ * <p>
+ * The argument types (if any) of the filter
+ * replace zero or one argument types of the target, at position {@code pos},
+ * in the resulting adapted method handle.
+ * The return type of the filter (if any) must be identical to the
+ * argument type of the target at position {@code pos}, and that target argument
+ * is supplied by the return value of the filter.
+ * <p>
+ * In all cases, {@code pos} must be greater than or equal to zero, and
+ * {@code pos} must also be less than or equal to the target's arity.
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle deepToString = publicLookup()
+ .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
+
+MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
+assertEquals("[strange]", (String) ts1.invokeExact("strange"));
+
+MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
+assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
+MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
+MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
+assertEquals("[top, [up, down], strange]",
+ (String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
+
+MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
+assertEquals("[top, [up, down], [strange]]",
+ (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
+
+MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
+assertEquals("[top, [[up, down, strange], charm], bottom]",
+ (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
+ * }</pre></blockquote>
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * T target(A...,V,C...);
+ * V filter(B...);
+ * T adapter(A... a,B... b,C... c) {
+ * V v = filter(b...);
+ * return target(a...,v,c...);
+ * }
+ * // and if the filter has no arguments:
+ * T target2(A...,V,C...);
+ * V filter2();
+ * T adapter2(A... a,C... c) {
+ * V v = filter2();
+ * return target2(a...,v,c...);
+ * }
+ * // and if the filter has a void return:
+ * T target3(A...,C...);
+ * void filter3(B...);
+ * void adapter3(A... a,B... b,C... c) {
+ * filter3(b...);
+ * return target3(a...,c...);
+ * }
+ * }</pre></blockquote>
+ * <p>
+ * A collection adapter {@code collectArguments(mh, 0, coll)} is equivalent to
+ * one which first "folds" the affected arguments, and then drops them, in separate
+ * steps as follows:
+ * <blockquote><pre>{@code
+ * mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //step 2
+ * mh = MethodHandles.foldArguments(mh, coll); //step 1
+ * }</pre></blockquote>
+ * If the target method handle consumes no arguments besides than the result
+ * (if any) of the filter {@code coll}, then {@code collectArguments(mh, 0, coll)}
+ * is equivalent to {@code filterReturnValue(coll, mh)}.
+ * If the filter method handle {@code coll} consumes one argument and produces
+ * a non-void result, then {@code collectArguments(mh, N, coll)}
+ * is equivalent to {@code filterArguments(mh, N, coll)}.
+ * Other equivalences are possible but would require argument permutation.
+ *
+ * @param target the method handle to invoke after filtering the subsequence of arguments
+ * @param pos the position of the first adapter argument to pass to the filter,
+ * and/or the target argument which receives the result of the filter
+ * @param filter method handle to call on the subsequence of arguments
+ * @return method handle which incorporates the specified argument subsequence filtering logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if the return type of {@code filter}
+ * is non-void and is not the same as the {@code pos} argument of the target,
+ * or if {@code pos} is not between 0 and the target's arity, inclusive,
+ * or if the resulting method handle's type would have
+ * <a href="MethodHandle.html#maxarity">too many parameters</a>
+ * @see MethodHandles#foldArguments
+ * @see MethodHandles#filterArguments
+ * @see MethodHandles#filterReturnValue
+ */
public static
- MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) { return null; }
+ MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) {
+ MethodType newType = collectArgumentsChecks(target, pos, filter);
+ return new Transformers.CollectArguments(target, filter, pos, newType);
+ }
+ private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
+ MethodType targetType = target.type();
+ MethodType filterType = filter.type();
+ Class<?> rtype = filterType.returnType();
+ List<Class<?>> filterArgs = filterType.parameterList();
+ if (rtype == void.class) {
+ return targetType.insertParameterTypes(pos, filterArgs);
+ }
+ if (rtype != targetType.parameterType(pos)) {
+ throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+ }
+ return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs);
+ }
+
+ /**
+ * Adapts a target method handle by post-processing
+ * its return value (if any) with a filter (another method handle).
+ * The result of the filter is returned from the adapter.
+ * <p>
+ * If the target returns a value, the filter must accept that value as
+ * its only argument.
+ * If the target returns void, the filter must accept no arguments.
+ * <p>
+ * The return type of the filter
+ * replaces the return type of the target
+ * in the resulting adapted method handle.
+ * The argument type of the filter (if any) must be identical to the
+ * return type of the target.
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle length = lookup().findVirtual(String.class,
+ "length", methodType(int.class));
+System.out.println((String) cat.invokeExact("x", "y")); // xy
+MethodHandle f0 = filterReturnValue(cat, length);
+System.out.println((int) f0.invokeExact("x", "y")); // 2
+ * }</pre></blockquote>
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * V target(A...);
+ * T filter(V);
+ * T adapter(A... a) {
+ * V v = target(a...);
+ * return filter(v);
+ * }
+ * // and if the target has a void return:
+ * void target2(A...);
+ * T filter2();
+ * T adapter2(A... a) {
+ * target2(a...);
+ * return filter2();
+ * }
+ * // and if the filter has a void return:
+ * V target3(A...);
+ * void filter3(V);
+ * void adapter3(A... a) {
+ * V v = target3(a...);
+ * filter3(v);
+ * }
+ * }</pre></blockquote>
+ * @param target the method handle to invoke before filtering the return value
+ * @param filter method handle to call on the return value
+ * @return method handle which incorporates the specified return value filtering logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if the argument list of {@code filter}
+ * does not match the return type of target as described above
+ */
public static
- MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { return null; }
+ MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
+ MethodType targetType = target.type();
+ MethodType filterType = filter.type();
+ filterReturnValueChecks(targetType, filterType);
+ return new Transformers.FilterReturnValue(target, filter);
+ }
+
+ private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
+ Class<?> rtype = targetType.returnType();
+ int filterValues = filterType.parameterCount();
+ if (filterValues == 0
+ ? (rtype != void.class)
+ : (rtype != filterType.parameterType(0) || filterValues != 1))
+ throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+ }
+
+ /**
+ * Adapts a target method handle by pre-processing
+ * some of its arguments, and then calling the target with
+ * the result of the pre-processing, inserted into the original
+ * sequence of arguments.
+ * <p>
+ * The pre-processing is performed by {@code combiner}, a second method handle.
+ * Of the arguments passed to the adapter, the first {@code N} arguments
+ * are copied to the combiner, which is then called.
+ * (Here, {@code N} is defined as the parameter count of the combiner.)
+ * After this, control passes to the target, with any result
+ * from the combiner inserted before the original {@code N} incoming
+ * arguments.
+ * <p>
+ * If the combiner returns a value, the first parameter type of the target
+ * must be identical with the return type of the combiner, and the next
+ * {@code N} parameter types of the target must exactly match the parameters
+ * of the combiner.
+ * <p>
+ * If the combiner has a void return, no result will be inserted,
+ * and the first {@code N} parameter types of the target
+ * must exactly match the parameters of the combiner.
+ * <p>
+ * The resulting adapter is the same type as the target, except that the
+ * first parameter type is dropped,
+ * if it corresponds to the result of the combiner.
+ * <p>
+ * (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments
+ * that either the combiner or the target does not wish to receive.
+ * If some of the incoming arguments are destined only for the combiner,
+ * consider using {@link MethodHandle#asCollector asCollector} instead, since those
+ * arguments will not need to be live on the stack on entry to the
+ * target.)
+ * <p><b>Example:</b>
+ * <blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+ "println", methodType(void.class, String.class))
+ .bindTo(System.out);
+MethodHandle cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+MethodHandle catTrace = foldArguments(cat, trace);
+// also prints "boo":
+assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+ * }</pre></blockquote>
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * // there are N arguments in A...
+ * T target(V, A[N]..., B...);
+ * V combiner(A...);
+ * T adapter(A... a, B... b) {
+ * V v = combiner(a...);
+ * return target(v, a..., b...);
+ * }
+ * // and if the combiner has a void return:
+ * T target2(A[N]..., B...);
+ * void combiner2(A...);
+ * T adapter2(A... a, B... b) {
+ * combiner2(a...);
+ * return target2(a..., b...);
+ * }
+ * }</pre></blockquote>
+ * @param target the method handle to invoke after arguments are combined
+ * @param combiner method handle to call initially on the incoming arguments
+ * @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if {@code combiner}'s return type
+ * is non-void and not the same as the first argument type of
+ * the target, or if the initial {@code N} argument types
+ * of the target
+ * (skipping one matching the {@code combiner}'s return type)
+ * are not identical with the argument types of {@code combiner}
+ */
public static
- MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { return null; }
+ MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
+ int foldPos = 0;
+ MethodType targetType = target.type();
+ MethodType combinerType = combiner.type();
+ Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
+
+ return new Transformers.FoldArguments(target, combiner);
+ }
+ private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
+ int foldArgs = combinerType.parameterCount();
+ Class<?> rtype = combinerType.returnType();
+ int foldVals = rtype == void.class ? 0 : 1;
+ int afterInsertPos = foldPos + foldVals;
+ boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
+ if (ok && !(combinerType.parameterList()
+ .equals(targetType.parameterList().subList(afterInsertPos,
+ afterInsertPos + foldArgs))))
+ ok = false;
+ if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
+ ok = false;
+ if (!ok)
+ throw misMatchedTypes("target and combiner types", targetType, combinerType);
+ return rtype;
+ }
+
+ /**
+ * Makes a method handle which adapts a target method handle,
+ * by guarding it with a test, a boolean-valued method handle.
+ * If the guard fails, a fallback handle is called instead.
+ * All three method handles must have the same corresponding
+ * argument and return types, except that the return type
+ * of the test must be boolean, and the test is allowed
+ * to have fewer arguments than the other two method handles.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * boolean test(A...);
+ * T target(A...,B...);
+ * T fallback(A...,B...);
+ * T adapter(A... a,B... b) {
+ * if (test(a...))
+ * return target(a..., b...);
+ * else
+ * return fallback(a..., b...);
+ * }
+ * }</pre></blockquote>
+ * Note that the test arguments ({@code a...} in the pseudocode) cannot
+ * be modified by execution of the test, and so are passed unchanged
+ * from the caller to the target or fallback as appropriate.
+ * @param test method handle used for test, must return boolean
+ * @param target method handle to call if test passes
+ * @param fallback method handle to call if test fails
+ * @return method handle which incorporates the specified if/then/else logic
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if {@code test} does not return boolean,
+ * or if all three method types do not match (with the return
+ * type of {@code test} changed to match that of the target).
+ */
public static
MethodHandle guardWithTest(MethodHandle test,
MethodHandle target,
- MethodHandle fallback) { return null; }
+ MethodHandle fallback) {
+ MethodType gtype = test.type();
+ MethodType ttype = target.type();
+ MethodType ftype = fallback.type();
+ if (!ttype.equals(ftype))
+ throw misMatchedTypes("target and fallback types", ttype, ftype);
+ if (gtype.returnType() != boolean.class)
+ throw newIllegalArgumentException("guard type is not a predicate "+gtype);
+ List<Class<?>> targs = ttype.parameterList();
+ List<Class<?>> gargs = gtype.parameterList();
+ if (!targs.equals(gargs)) {
+ int gpc = gargs.size(), tpc = targs.size();
+ if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs))
+ throw misMatchedTypes("target and test types", ttype, gtype);
+ test = dropArguments(test, gpc, targs.subList(gpc, tpc));
+ gtype = test.type();
+ }
+
+ return new Transformers.GuardWithTest(test, target, fallback);
+ }
+
+ static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) {
+ return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
+ }
+ /**
+ * Makes a method handle which adapts a target method handle,
+ * by running it inside an exception handler.
+ * If the target returns normally, the adapter returns that value.
+ * If an exception matching the specified type is thrown, the fallback
+ * handle is called instead on the exception, plus the original arguments.
+ * <p>
+ * The target and handler must have the same corresponding
+ * argument and return types, except that handler may omit trailing arguments
+ * (similarly to the predicate in {@link #guardWithTest guardWithTest}).
+ * Also, the handler must have an extra leading parameter of {@code exType} or a supertype.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>{@code
+ * T target(A..., B...);
+ * T handler(ExType, A...);
+ * T adapter(A... a, B... b) {
+ * try {
+ * return target(a..., b...);
+ * } catch (ExType ex) {
+ * return handler(ex, a...);
+ * }
+ * }
+ * }</pre></blockquote>
+ * Note that the saved arguments ({@code a...} in the pseudocode) cannot
+ * be modified by execution of the target, and so are passed unchanged
+ * from the caller to the handler, if the handler is invoked.
+ * <p>
+ * The target and handler must return the same type, even if the handler
+ * always throws. (This might happen, for instance, because the handler
+ * is simulating a {@code finally} clause).
+ * To create such a throwing handler, compose the handler creation logic
+ * with {@link #throwException throwException},
+ * in order to create a method handle of the correct return type.
+ * @param target method handle to call
+ * @param exType the type of exception which the handler will catch
+ * @param handler method handle to call if a matching exception is thrown
+ * @return method handle which incorporates the specified try/catch logic
+ * @throws NullPointerException if any argument is null
+ * @throws IllegalArgumentException if {@code handler} does not accept
+ * the given exception type, or if the method handle types do
+ * not match in their return types and their
+ * corresponding parameters
+ */
public static
MethodHandle catchException(MethodHandle target,
Class<? extends Throwable> exType,
- MethodHandle handler) { return null; }
+ MethodHandle handler) {
+ MethodType ttype = target.type();
+ MethodType htype = handler.type();
+ if (htype.parameterCount() < 1 ||
+ !htype.parameterType(0).isAssignableFrom(exType))
+ throw newIllegalArgumentException("handler does not accept exception type "+exType);
+ if (htype.returnType() != ttype.returnType())
+ throw misMatchedTypes("target and handler return types", ttype, htype);
+ List<Class<?>> targs = ttype.parameterList();
+ List<Class<?>> hargs = htype.parameterList();
+ hargs = hargs.subList(1, hargs.size()); // omit leading parameter from handler
+ if (!targs.equals(hargs)) {
+ int hpc = hargs.size(), tpc = targs.size();
+ if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs))
+ throw misMatchedTypes("target and handler types", ttype, htype);
+ }
+
+ return new Transformers.CatchException(target, handler, exType);
+ }
+ /**
+ * Produces a method handle which will throw exceptions of the given {@code exType}.
+ * The method handle will accept a single argument of {@code exType},
+ * and immediately throw it as an exception.
+ * The method type will nominally specify a return of {@code returnType}.
+ * The return type may be anything convenient: It doesn't matter to the
+ * method handle's behavior, since it will never return normally.
+ * @param returnType the return type of the desired method handle
+ * @param exType the parameter type of the desired method handle
+ * @return method handle which can throw the given exceptions
+ * @throws NullPointerException if either argument is null
+ */
public static
- MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) { return null; }
+ MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
+ if (!Throwable.class.isAssignableFrom(exType))
+ throw new ClassCastException(exType.getName());
+
+ return new Transformers.AlwaysThrow(returnType, exType);
+ }
}
diff --git a/java/lang/invoke/MethodType.java b/java/lang/invoke/MethodType.java
index 4cb5c226..bfa7ccd5 100644
--- a/java/lang/invoke/MethodType.java
+++ b/java/lang/invoke/MethodType.java
@@ -25,78 +25,1227 @@
package java.lang.invoke;
+import sun.invoke.util.Wrapper;
+import java.lang.ref.WeakReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import sun.invoke.util.BytecodeDescriptor;
+import static java.lang.invoke.MethodHandleStatics.*;
+/**
+ * A method type represents the arguments and return type accepted and
+ * returned by a method handle, or the arguments and return type passed
+ * and expected by a method handle caller. Method types must be properly
+ * matched between a method handle and all its callers,
+ * and the JVM's operations enforce this matching at, specifically
+ * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
+ * and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution
+ * of {@code invokedynamic} instructions.
+ * <p>
+ * The structure is a return type accompanied by any number of parameter types.
+ * The types (primitive, {@code void}, and reference) are represented by {@link Class} objects.
+ * (For ease of exposition, we treat {@code void} as if it were a type.
+ * In fact, it denotes the absence of a return type.)
+ * <p>
+ * All instances of {@code MethodType} are immutable.
+ * Two instances are completely interchangeable if they compare equal.
+ * Equality depends on pairwise correspondence of the return and parameter types and on nothing else.
+ * <p>
+ * This type can be created only by factory methods.
+ * All factory methods may cache values, though caching is not guaranteed.
+ * Some factory methods are static, while others are virtual methods which
+ * modify precursor method types, e.g., by changing a selected parameter.
+ * <p>
+ * Factory methods which operate on groups of parameter types
+ * are systematically presented in two versions, so that both Java arrays and
+ * Java lists can be used to work with groups of parameter types.
+ * The query methods {@code parameterArray} and {@code parameterList}
+ * also provide a choice between arrays and lists.
+ * <p>
+ * {@code MethodType} objects are sometimes derived from bytecode instructions
+ * such as {@code invokedynamic}, specifically from the type descriptor strings associated
+ * with the instructions in a class file's constant pool.
+ * <p>
+ * Like classes and strings, method types can also be represented directly
+ * in a class file's constant pool as constants.
+ * A method type may be loaded by an {@code ldc} instruction which refers
+ * to a suitable {@code CONSTANT_MethodType} constant pool entry.
+ * The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string.
+ * (For full details on method type constants,
+ * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
+ * <p>
+ * When the JVM materializes a {@code MethodType} from a descriptor string,
+ * all classes named in the descriptor must be accessible, and will be loaded.
+ * (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.)
+ * This loading may occur at any time before the {@code MethodType} object is first derived.
+ * @author John Rose, JSR 292 EG
+ */
public final
class MethodType implements java.io.Serializable {
+ private static final long serialVersionUID = 292L; // {rtype, {ptype...}}
+
+ // The rtype and ptypes fields define the structural identity of the method type:
+ private final Class<?> rtype;
+ private final Class<?>[] ptypes;
+
+ // The remaining fields are caches of various sorts:
+ private @Stable MethodTypeForm form; // erased form, plus cached data about primitives
+ private @Stable MethodType wrapAlt; // alternative wrapped/unwrapped version
+ // Android-changed: Remove adapter cache. We're not dynamically generating any
+ // adapters at this point.
+ // private @Stable Invokers invokers; // cache of handy higher-order adapters
+ private @Stable String methodDescriptor; // cache for toMethodDescriptorString
+
+ /**
+ * Check the given parameters for validity and store them into the final fields.
+ */
+ private MethodType(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
+ checkRtype(rtype);
+ checkPtypes(ptypes);
+ this.rtype = rtype;
+ // defensively copy the array passed in by the user
+ this.ptypes = trusted ? ptypes : Arrays.copyOf(ptypes, ptypes.length);
+ }
+
+ /**
+ * Construct a temporary unchecked instance of MethodType for use only as a key to the intern table.
+ * Does not check the given parameters for validity, and must be discarded after it is used as a searching key.
+ * The parameters are reversed for this constructor, so that is is not accidentally used.
+ */
+ private MethodType(Class<?>[] ptypes, Class<?> rtype) {
+ this.rtype = rtype;
+ this.ptypes = ptypes;
+ }
+
+ /*trusted*/ MethodTypeForm form() { return form; }
+ /*trusted*/ /** @hide */ public Class<?> rtype() { return rtype; }
+ /*trusted*/ /** @hide */ public Class<?>[] ptypes() { return ptypes; }
+ // Android-changed: Removed method setForm. It's unused in the JDK and there's no
+ // good reason to allow the form to be set externally.
+ //
+ // void setForm(MethodTypeForm f) { form = f; }
+
+ /** This number, mandated by the JVM spec as 255,
+ * is the maximum number of <em>slots</em>
+ * that any Java method can receive in its argument list.
+ * It limits both JVM signatures and method type objects.
+ * The longest possible invocation will look like
+ * {@code staticMethod(arg1, arg2, ..., arg255)} or
+ * {@code x.virtualMethod(arg1, arg2, ..., arg254)}.
+ */
+ /*non-public*/ static final int MAX_JVM_ARITY = 255; // this is mandated by the JVM spec.
+
+ /** This number is the maximum arity of a method handle, 254.
+ * It is derived from the absolute JVM-imposed arity by subtracting one,
+ * which is the slot occupied by the method handle itself at the
+ * beginning of the argument list used to invoke the method handle.
+ * The longest possible invocation will look like
+ * {@code mh.invoke(arg1, arg2, ..., arg254)}.
+ */
+ // Issue: Should we allow MH.invokeWithArguments to go to the full 255?
+ /*non-public*/ static final int MAX_MH_ARITY = MAX_JVM_ARITY-1; // deduct one for mh receiver
+
+ /** This number is the maximum arity of a method handle invoker, 253.
+ * It is derived from the absolute JVM-imposed arity by subtracting two,
+ * which are the slots occupied by invoke method handle, and the
+ * target method handle, which are both at the beginning of the argument
+ * list used to invoke the target method handle.
+ * The longest possible invocation will look like
+ * {@code invokermh.invoke(targetmh, arg1, arg2, ..., arg253)}.
+ */
+ /*non-public*/ static final int MAX_MH_INVOKER_ARITY = MAX_MH_ARITY-1; // deduct one more for invoker
+
+ private static void checkRtype(Class<?> rtype) {
+ Objects.requireNonNull(rtype);
+ }
+ private static void checkPtype(Class<?> ptype) {
+ Objects.requireNonNull(ptype);
+ if (ptype == void.class)
+ throw newIllegalArgumentException("parameter type cannot be void");
+ }
+ /** Return number of extra slots (count of long/double args). */
+ private static int checkPtypes(Class<?>[] ptypes) {
+ int slots = 0;
+ for (Class<?> ptype : ptypes) {
+ checkPtype(ptype);
+ if (ptype == double.class || ptype == long.class) {
+ slots++;
+ }
+ }
+ checkSlotCount(ptypes.length + slots);
+ return slots;
+ }
+ static void checkSlotCount(int count) {
+ assert((MAX_JVM_ARITY & (MAX_JVM_ARITY+1)) == 0);
+ // MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
+ if ((count & MAX_JVM_ARITY) != count)
+ throw newIllegalArgumentException("bad parameter count "+count);
+ }
+ private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
+ if (num instanceof Integer) num = "bad index: "+num;
+ return new IndexOutOfBoundsException(num.toString());
+ }
+
+ static final ConcurrentWeakInternSet<MethodType> internTable = new ConcurrentWeakInternSet<>();
+
+ static final Class<?>[] NO_PTYPES = {};
+
+ /**
+ * Finds or creates an instance of the given method type.
+ * @param rtype the return type
+ * @param ptypes the parameter types
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
+ */
public static
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
- return null;
+ return makeImpl(rtype, ptypes, false);
}
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param rtype the return type
+ * @param ptypes the parameter types
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
+ */
public static
MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
- return null;
+ boolean notrust = false; // random List impl. could return evil ptypes array
+ return makeImpl(rtype, listToArray(ptypes), notrust);
}
+ private static Class<?>[] listToArray(List<Class<?>> ptypes) {
+ // sanity check the size before the toArray call, since size might be huge
+ checkSlotCount(ptypes.size());
+ return ptypes.toArray(NO_PTYPES);
+ }
+
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The leading parameter type is prepended to the remaining array.
+ * @param rtype the return type
+ * @param ptype0 the first parameter type
+ * @param ptypes the remaining parameter types
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class}
+ */
public static
- MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) { return null; }
+ MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
+ Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
+ ptypes1[0] = ptype0;
+ System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
+ return makeImpl(rtype, ptypes1, true);
+ }
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has no parameter types.
+ * @param rtype the return type
+ * @return a method type with the given return value
+ * @throws NullPointerException if {@code rtype} is null
+ */
public static
- MethodType methodType(Class<?> rtype) { return null; }
+ MethodType methodType(Class<?> rtype) {
+ return makeImpl(rtype, NO_PTYPES, true);
+ }
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has the single given parameter type.
+ * @param rtype the return type
+ * @param ptype0 the parameter type
+ * @return a method type with the given return value and parameter type
+ * @throws NullPointerException if {@code rtype} or {@code ptype0} is null
+ * @throws IllegalArgumentException if {@code ptype0} is {@code void.class}
+ */
public static
- MethodType methodType(Class<?> rtype, Class<?> ptype0) { return null; }
+ MethodType methodType(Class<?> rtype, Class<?> ptype0) {
+ return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
+ }
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has the same parameter types as {@code ptypes},
+ * and the specified return type.
+ * @param rtype the return type
+ * @param ptypes the method type which supplies the parameter types
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} is null
+ */
public static
- MethodType methodType(Class<?> rtype, MethodType ptypes) { return null; }
+ MethodType methodType(Class<?> rtype, MethodType ptypes) {
+ return makeImpl(rtype, ptypes.ptypes, true);
+ }
+
+ /**
+ * Sole factory method to find or create an interned method type.
+ * @param rtype desired return type
+ * @param ptypes desired parameter types
+ * @param trusted whether the ptypes can be used without cloning
+ * @return the unique method type of the desired structure
+ */
+ /*trusted*/ static
+ MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
+ MethodType mt = internTable.get(new MethodType(ptypes, rtype));
+ if (mt != null)
+ return mt;
+ if (ptypes.length == 0) {
+ ptypes = NO_PTYPES; trusted = true;
+ }
+ mt = new MethodType(rtype, ptypes, trusted);
+ // promote the object to the Real Thing, and reprobe
+ mt.form = MethodTypeForm.findForm(mt);
+ return internTable.add(mt);
+ }
+ private static final MethodType[] objectOnlyTypes = new MethodType[20];
+ /**
+ * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * All parameters and the return type will be {@code Object},
+ * except the final array parameter if any, which will be {@code Object[]}.
+ * @param objectArgCount number of parameters (excluding the final array parameter if any)
+ * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
+ * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
+ * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray} is true)
+ * @see #genericMethodType(int)
+ */
public static
- MethodType genericMethodType(int objectArgCount, boolean finalArray) { return null; }
+ MethodType genericMethodType(int objectArgCount, boolean finalArray) {
+ MethodType mt;
+ checkSlotCount(objectArgCount);
+ int ivarargs = (!finalArray ? 0 : 1);
+ int ootIndex = objectArgCount*2 + ivarargs;
+ if (ootIndex < objectOnlyTypes.length) {
+ mt = objectOnlyTypes[ootIndex];
+ if (mt != null) return mt;
+ }
+ Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs];
+ Arrays.fill(ptypes, Object.class);
+ if (ivarargs != 0) ptypes[objectArgCount] = Object[].class;
+ mt = makeImpl(Object.class, ptypes, true);
+ if (ootIndex < objectOnlyTypes.length) {
+ objectOnlyTypes[ootIndex] = mt; // cache it here also!
+ }
+ return mt;
+ }
+ /**
+ * Finds or creates a method type whose components are all {@code Object}.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * All parameters and the return type will be Object.
+ * @param objectArgCount number of parameters
+ * @return a generally applicable method type, for all calls of the given argument count
+ * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
+ * @see #genericMethodType(int, boolean)
+ */
public static
- MethodType genericMethodType(int objectArgCount) { return null; }
+ MethodType genericMethodType(int objectArgCount) {
+ return genericMethodType(objectArgCount, false);
+ }
+
+ /**
+ * Finds or creates a method type with a single different parameter type.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param num the index (zero-based) of the parameter type to change
+ * @param nptype a new parameter type to replace the old one with
+ * @return the same type, except with the selected parameter changed
+ * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
+ * @throws IllegalArgumentException if {@code nptype} is {@code void.class}
+ * @throws NullPointerException if {@code nptype} is null
+ */
+ public MethodType changeParameterType(int num, Class<?> nptype) {
+ if (parameterType(num) == nptype) return this;
+ checkPtype(nptype);
+ Class<?>[] nptypes = ptypes.clone();
+ nptypes[num] = nptype;
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param num the position (zero-based) of the inserted parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) inserted
+ * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
+ int len = ptypes.length;
+ if (num < 0 || num > len)
+ throw newIndexOutOfBoundsException(num);
+ int ins = checkPtypes(ptypesToInsert);
+ checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
+ int ilen = ptypesToInsert.length;
+ if (ilen == 0) return this;
+ Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
+ System.arraycopy(nptypes, num, nptypes, num+ilen, len-num);
+ System.arraycopy(ptypesToInsert, 0, nptypes, num, ilen);
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
+ * @return the same type, except with the selected parameter(s) appended
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
+ return insertParameterTypes(parameterCount(), ptypesToInsert);
+ }
+
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param num the position (zero-based) of the inserted parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) inserted
+ * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
+ return insertParameterTypes(num, listToArray(ptypesToInsert));
+ }
+
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
+ * @return the same type, except with the selected parameter(s) appended
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
+ return insertParameterTypes(parameterCount(), ptypesToInsert);
+ }
+
+ /**
+ * Finds or creates a method type with modified parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param start the position (zero-based) of the first replaced parameter type(s)
+ * @param end the position (zero-based) after the last replaced parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) replaced
+ * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
+ * or if {@code end} is negative or greater than {@code parameterCount()}
+ * or if {@code start} is greater than {@code end}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ /*non-public*/ MethodType replaceParameterTypes(int start, int end, Class<?>... ptypesToInsert) {
+ if (start == end)
+ return insertParameterTypes(start, ptypesToInsert);
+ int len = ptypes.length;
+ if (!(0 <= start && start <= end && end <= len))
+ throw newIndexOutOfBoundsException("start="+start+" end="+end);
+ int ilen = ptypesToInsert.length;
+ if (ilen == 0)
+ return dropParameterTypes(start, end);
+ return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
+ }
+
+ /** Replace the last arrayLength parameter types with the component type of arrayType.
+ * @param arrayType any array type
+ * @param arrayLength the number of parameter types to change
+ * @return the resulting type
+ */
+ /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
+ assert(parameterCount() >= arrayLength);
+ int spreadPos = ptypes.length - arrayLength;
+ if (arrayLength == 0) return this; // nothing to change
+ if (arrayType == Object[].class) {
+ if (isGeneric()) return this; // nothing to change
+ if (spreadPos == 0) {
+ // no leading arguments to preserve; go generic
+ MethodType res = genericMethodType(arrayLength);
+ if (rtype != Object.class) {
+ res = res.changeReturnType(rtype);
+ }
+ return res;
+ }
+ }
+ Class<?> elemType = arrayType.getComponentType();
+ assert(elemType != null);
+ for (int i = spreadPos; i < ptypes.length; i++) {
+ if (ptypes[i] != elemType) {
+ Class<?>[] fixedPtypes = ptypes.clone();
+ Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
+ return methodType(rtype, fixedPtypes);
+ }
+ }
+ return this; // arguments check out; no change
+ }
+
+ /** Return the leading parameter type, which must exist and be a reference.
+ * @return the leading parameter type, after error checks
+ */
+ /*non-public*/ Class<?> leadingReferenceParameter() {
+ Class<?> ptype;
+ if (ptypes.length == 0 ||
+ (ptype = ptypes[0]).isPrimitive())
+ throw newIllegalArgumentException("no leading reference parameter");
+ return ptype;
+ }
+
+ /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
+ * @param arrayType any array type
+ * @param arrayLength the number of parameter types to insert
+ * @return the resulting type
+ */
+ /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
+ assert(parameterCount() >= 1);
+ assert(lastParameterType().isAssignableFrom(arrayType));
+ MethodType res;
+ if (arrayType == Object[].class) {
+ res = genericMethodType(arrayLength);
+ if (rtype != Object.class) {
+ res = res.changeReturnType(rtype);
+ }
+ } else {
+ Class<?> elemType = arrayType.getComponentType();
+ assert(elemType != null);
+ res = methodType(rtype, Collections.nCopies(arrayLength, elemType));
+ }
+ if (ptypes.length == 1) {
+ return res;
+ } else {
+ return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
+ }
+ }
+
+ /**
+ * Finds or creates a method type with some parameter types omitted.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param start the index (zero-based) of the first parameter type to remove
+ * @param end the index (greater than {@code start}) of the first parameter type after not to remove
+ * @return the same type, except with the selected parameter(s) removed
+ * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
+ * or if {@code end} is negative or greater than {@code parameterCount()}
+ * or if {@code start} is greater than {@code end}
+ */
+ public MethodType dropParameterTypes(int start, int end) {
+ int len = ptypes.length;
+ if (!(0 <= start && start <= end && end <= len))
+ throw newIndexOutOfBoundsException("start="+start+" end="+end);
+ if (start == end) return this;
+ Class<?>[] nptypes;
+ if (start == 0) {
+ if (end == len) {
+ // drop all parameters
+ nptypes = NO_PTYPES;
+ } else {
+ // drop initial parameter(s)
+ nptypes = Arrays.copyOfRange(ptypes, end, len);
+ }
+ } else {
+ if (end == len) {
+ // drop trailing parameter(s)
+ nptypes = Arrays.copyOfRange(ptypes, 0, start);
+ } else {
+ int tail = len - end;
+ nptypes = Arrays.copyOfRange(ptypes, 0, start + tail);
+ System.arraycopy(ptypes, end, nptypes, start, tail);
+ }
+ }
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /**
+ * Finds or creates a method type with a different return type.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param nrtype a return parameter type to replace the old one with
+ * @return the same type, except with the return type change
+ * @throws NullPointerException if {@code nrtype} is null
+ */
+ public MethodType changeReturnType(Class<?> nrtype) {
+ if (returnType() == nrtype) return this;
+ return makeImpl(nrtype, ptypes, true);
+ }
+
+ /**
+ * Reports if this type contains a primitive argument or return value.
+ * The return type {@code void} counts as a primitive.
+ * @return true if any of the types are primitives
+ */
+ public boolean hasPrimitives() {
+ return form.hasPrimitives();
+ }
+
+ /**
+ * Reports if this type contains a wrapper argument or return value.
+ * Wrappers are types which box primitive values, such as {@link Integer}.
+ * The reference type {@code java.lang.Void} counts as a wrapper,
+ * if it occurs as a return type.
+ * @return true if any of the types are wrappers
+ */
+ public boolean hasWrappers() {
+ return unwrap() != this;
+ }
+
+ /**
+ * Erases all reference types to {@code Object}.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * All primitive types (including {@code void}) will remain unchanged.
+ * @return a version of the original type with all reference types replaced
+ */
+ public MethodType erase() {
+ return form.erasedType();
+ }
+
+ /**
+ * Erases all reference types to {@code Object}, and all subword types to {@code int}.
+ * This is the reduced type polymorphism used by private methods
+ * such as {@link MethodHandle#invokeBasic invokeBasic}.
+ * @return a version of the original type with all reference and subword types replaced
+ */
+ /*non-public*/ MethodType basicType() {
+ return form.basicType();
+ }
+
+ /**
+ * @return a version of the original type with MethodHandle prepended as the first argument
+ */
+ /*non-public*/ MethodType invokerType() {
+ return insertParameterTypes(0, MethodHandle.class);
+ }
- public MethodType changeParameterType(int num, Class<?> nptype) { return null; }
+ /**
+ * Converts all types, both reference and primitive, to {@code Object}.
+ * Convenience method for {@link #genericMethodType(int) genericMethodType}.
+ * The expression {@code type.wrap().erase()} produces the same value
+ * as {@code type.generic()}.
+ * @return a version of the original type with all types replaced
+ */
+ public MethodType generic() {
+ return genericMethodType(parameterCount());
+ }
- public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) { return null; }
+ /*non-public*/ boolean isGeneric() {
+ return this == erase() && !hasPrimitives();
+ }
- public MethodType appendParameterTypes(Class<?>... ptypesToInsert) { return null; }
+ /**
+ * Converts all primitive types to their corresponding wrapper types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * All reference types (including wrapper types) will remain unchanged.
+ * A {@code void} return type is changed to the type {@code java.lang.Void}.
+ * The expression {@code type.wrap().erase()} produces the same value
+ * as {@code type.generic()}.
+ * @return a version of the original type with all primitive types replaced
+ */
+ public MethodType wrap() {
+ return hasPrimitives() ? wrapWithPrims(this) : this;
+ }
- public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) { return null; }
+ /**
+ * Converts all wrapper types to their corresponding primitive types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * All primitive types (including {@code void}) will remain unchanged.
+ * A return type of {@code java.lang.Void} is changed to {@code void}.
+ * @return a version of the original type with all wrapper types replaced
+ */
+ public MethodType unwrap() {
+ MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
+ return unwrapWithNoPrims(noprims);
+ }
- public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) { return null; }
+ private static MethodType wrapWithPrims(MethodType pt) {
+ assert(pt.hasPrimitives());
+ MethodType wt = pt.wrapAlt;
+ if (wt == null) {
+ // fill in lazily
+ wt = MethodTypeForm.canonicalize(pt, MethodTypeForm.WRAP, MethodTypeForm.WRAP);
+ assert(wt != null);
+ pt.wrapAlt = wt;
+ }
+ return wt;
+ }
- public MethodType dropParameterTypes(int start, int end) { return null; }
+ private static MethodType unwrapWithNoPrims(MethodType wt) {
+ assert(!wt.hasPrimitives());
+ MethodType uwt = wt.wrapAlt;
+ if (uwt == null) {
+ // fill in lazily
+ uwt = MethodTypeForm.canonicalize(wt, MethodTypeForm.UNWRAP, MethodTypeForm.UNWRAP);
+ if (uwt == null)
+ uwt = wt; // type has no wrappers or prims at all
+ wt.wrapAlt = uwt;
+ }
+ return uwt;
+ }
- public MethodType changeReturnType(Class<?> nrtype) { return null; }
+ /**
+ * Returns the parameter type at the specified index, within this method type.
+ * @param num the index (zero-based) of the desired parameter type
+ * @return the selected parameter type
+ * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
+ */
+ public Class<?> parameterType(int num) {
+ return ptypes[num];
+ }
+ /**
+ * Returns the number of parameter types in this method type.
+ * @return the number of parameter types
+ */
+ public int parameterCount() {
+ return ptypes.length;
+ }
+ /**
+ * Returns the return type of this method type.
+ * @return the return type
+ */
+ public Class<?> returnType() {
+ return rtype;
+ }
- public boolean hasPrimitives() { return false; }
+ /**
+ * Presents the parameter types as a list (a convenience method).
+ * The list will be immutable.
+ * @return the parameter types (as an immutable list)
+ */
+ public List<Class<?>> parameterList() {
+ return Collections.unmodifiableList(Arrays.asList(ptypes.clone()));
+ }
- public boolean hasWrappers() { return false; }
+ /*non-public*/ Class<?> lastParameterType() {
+ int len = ptypes.length;
+ return len == 0 ? void.class : ptypes[len-1];
+ }
- public MethodType erase() { return null; }
+ /**
+ * Presents the parameter types as an array (a convenience method).
+ * Changes to the array will not result in changes to the type.
+ * @return the parameter types (as a fresh copy if necessary)
+ */
+ public Class<?>[] parameterArray() {
+ return ptypes.clone();
+ }
- public MethodType generic() { return null; }
+ /**
+ * Compares the specified object with this type for equality.
+ * That is, it returns <tt>true</tt> if and only if the specified object
+ * is also a method type with exactly the same parameters and return type.
+ * @param x object to compare
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object x) {
+ return this == x || x instanceof MethodType && equals((MethodType)x);
+ }
- public MethodType wrap() { return null; }
+ private boolean equals(MethodType that) {
+ return this.rtype == that.rtype
+ && Arrays.equals(this.ptypes, that.ptypes);
+ }
- public MethodType unwrap() { return null; }
+ /**
+ * Returns the hash code value for this method type.
+ * It is defined to be the same as the hashcode of a List
+ * whose elements are the return type followed by the
+ * parameter types.
+ * @return the hash code value for this method type
+ * @see Object#hashCode()
+ * @see #equals(Object)
+ * @see List#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 31 + rtype.hashCode();
+ for (Class<?> ptype : ptypes)
+ hashCode = 31*hashCode + ptype.hashCode();
+ return hashCode;
+ }
- public Class<?> parameterType(int num) { return null; }
+ /**
+ * Returns a string representation of the method type,
+ * of the form {@code "(PT0,PT1...)RT"}.
+ * The string representation of a method type is a
+ * parenthesis enclosed, comma separated list of type names,
+ * followed immediately by the return type.
+ * <p>
+ * Each type is represented by its
+ * {@link java.lang.Class#getSimpleName simple name}.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (int i = 0; i < ptypes.length; i++) {
+ if (i > 0) sb.append(",");
+ sb.append(ptypes[i].getSimpleName());
+ }
+ sb.append(")");
+ sb.append(rtype.getSimpleName());
+ return sb.toString();
+ }
- public int parameterCount() { return 0; }
+ /** True if the old return type can always be viewed (w/o casting) under new return type,
+ * and the new parameters can be viewed (w/o casting) under the old parameter types.
+ */
+ // Android-changed: Removed implementation details.
+ // boolean isViewableAs(MethodType newType, boolean keepInterfaces);
+ // boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces);
+ /*non-public*/
+ boolean isConvertibleTo(MethodType newType) {
+ MethodTypeForm oldForm = this.form();
+ MethodTypeForm newForm = newType.form();
+ if (oldForm == newForm)
+ // same parameter count, same primitive/object mix
+ return true;
+ if (!canConvert(returnType(), newType.returnType()))
+ return false;
+ Class<?>[] srcTypes = newType.ptypes;
+ Class<?>[] dstTypes = ptypes;
+ if (srcTypes == dstTypes)
+ return true;
+ int argc;
+ if ((argc = srcTypes.length) != dstTypes.length)
+ return false;
+ if (argc <= 1) {
+ if (argc == 1 && !canConvert(srcTypes[0], dstTypes[0]))
+ return false;
+ return true;
+ }
+ if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
+ (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
+ // Somewhat complicated test to avoid a loop of 2 or more trips.
+ // If either type has only Object parameters, we know we can convert.
+ assert(canConvertParameters(srcTypes, dstTypes));
+ return true;
+ }
+ return canConvertParameters(srcTypes, dstTypes);
+ }
+
+ /** Returns true if MHs.explicitCastArguments produces the same result as MH.asType.
+ * If the type conversion is impossible for either, the result should be false.
+ */
+ /*non-public*/
+ boolean explicitCastEquivalentToAsType(MethodType newType) {
+ if (this == newType) return true;
+ if (!explicitCastEquivalentToAsType(rtype, newType.rtype)) {
+ return false;
+ }
+ Class<?>[] srcTypes = newType.ptypes;
+ Class<?>[] dstTypes = ptypes;
+ if (dstTypes == srcTypes) {
+ return true;
+ }
+ assert(dstTypes.length == srcTypes.length);
+ for (int i = 0; i < dstTypes.length; i++) {
+ if (!explicitCastEquivalentToAsType(srcTypes[i], dstTypes[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Reports true if the src can be converted to the dst, by both asType and MHs.eCE,
+ * and with the same effect.
+ * MHs.eCA has the following "upgrades" to MH.asType:
+ * 1. interfaces are unchecked (that is, treated as if aliased to Object)
+ * Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics
+ * 2a. the full matrix of primitive-to-primitive conversions is supported
+ * Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
+ * are not supported by asType, but anything supported by asType is equivalent
+ * with MHs.eCE.
+ * 2b. conversion of void->primitive means explicit cast has to insert zero/false/null.
+ * 3a. unboxing conversions can be followed by the full matrix of primitive conversions
+ * 3b. unboxing of null is permitted (creates a zero primitive value)
+ * Other than interfaces, reference-to-reference conversions are the same.
+ * Boxing primitives to references is the same for both operators.
+ */
+ private static boolean explicitCastEquivalentToAsType(Class<?> src, Class<?> dst) {
+ if (src == dst || dst == Object.class || dst == void.class) {
+ return true;
+ } else if (src.isPrimitive() && src != void.class) {
+ // Could be a prim/prim conversion, where casting is a strict superset.
+ // Or a boxing conversion, which is always to an exact wrapper class.
+ return canConvert(src, dst);
+ } else if (dst.isPrimitive()) {
+ // Unboxing behavior is different between MHs.eCA & MH.asType (see 3b).
+ return false;
+ } else {
+ // R->R always works, but we have to avoid a check-cast to an interface.
+ return !dst.isInterface() || dst.isAssignableFrom(src);
+ }
+ }
- public Class<?> returnType() { return null; }
+ private boolean canConvertParameters(Class<?>[] srcTypes, Class<?>[] dstTypes) {
+ for (int i = 0; i < srcTypes.length; i++) {
+ if (!canConvert(srcTypes[i], dstTypes[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
- public List<Class<?>> parameterList() { return null; }
+ /*non-public*/
+ static boolean canConvert(Class<?> src, Class<?> dst) {
+ // short-circuit a few cases:
+ if (src == dst || src == Object.class || dst == Object.class) return true;
+ // the remainder of this logic is documented in MethodHandle.asType
+ if (src.isPrimitive()) {
+ // can force void to an explicit null, a la reflect.Method.invoke
+ // can also force void to a primitive zero, by analogy
+ if (src == void.class) return true; //or !dst.isPrimitive()?
+ Wrapper sw = Wrapper.forPrimitiveType(src);
+ if (dst.isPrimitive()) {
+ // P->P must widen
+ return Wrapper.forPrimitiveType(dst).isConvertibleFrom(sw);
+ } else {
+ // P->R must box and widen
+ return dst.isAssignableFrom(sw.wrapperType());
+ }
+ } else if (dst.isPrimitive()) {
+ // any value can be dropped
+ if (dst == void.class) return true;
+ Wrapper dw = Wrapper.forPrimitiveType(dst);
+ // R->P must be able to unbox (from a dynamically chosen type) and widen
+ // For example:
+ // Byte/Number/Comparable/Object -> dw:Byte -> byte.
+ // Character/Comparable/Object -> dw:Character -> char
+ // Boolean/Comparable/Object -> dw:Boolean -> boolean
+ // This means that dw must be cast-compatible with src.
+ if (src.isAssignableFrom(dw.wrapperType())) {
+ return true;
+ }
+ // The above does not work if the source reference is strongly typed
+ // to a wrapper whose primitive must be widened. For example:
+ // Byte -> unbox:byte -> short/int/long/float/double
+ // Character -> unbox:char -> int/long/float/double
+ if (Wrapper.isWrapperType(src) &&
+ dw.isConvertibleFrom(Wrapper.forWrapperType(src))) {
+ // can unbox from src and then widen to dst
+ return true;
+ }
+ // We have already covered cases which arise due to runtime unboxing
+ // of a reference type which covers several wrapper types:
+ // Object -> cast:Integer -> unbox:int -> long/float/double
+ // Serializable -> cast:Byte -> unbox:byte -> byte/short/int/long/float/double
+ // An marginal case is Number -> dw:Character -> char, which would be OK if there were a
+ // subclass of Number which wraps a value that can convert to char.
+ // Since there is none, we don't need an extra check here to cover char or boolean.
+ return false;
+ } else {
+ // R->R always works, since null is always valid dynamically
+ return true;
+ }
+ }
- public Class<?>[] parameterArray() { return null; }
+ /** Reports the number of JVM stack slots required to invoke a method
+ * of this type. Note that (for historical reasons) the JVM requires
+ * a second stack slot to pass long and double arguments.
+ * So this method returns {@link #parameterCount() parameterCount} plus the
+ * number of long and double parameters (if any).
+ * <p>
+ * This method is included for the benefit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @return the number of JVM stack slots for this type's parameters
+ */
+ /*non-public*/ int parameterSlotCount() {
+ return form.parameterSlotCount();
+ }
+ /// Queries which have to do with the bytecode architecture
+
+ // Android-changed: These methods aren't needed on Android and are unused within the JDK.
+ //
+ // int parameterSlotDepth(int num);
+ // int returnSlotCount();
+ //
+ // Android-changed: Removed cache of higher order adapters.
+ //
+ // Invokers invokers();
+
+ /**
+ * Finds or creates an instance of a method type, given the spelling of its bytecode descriptor.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * Any class or interface name embedded in the descriptor string
+ * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
+ * on the given loader (or if it is null, on the system class loader).
+ * <p>
+ * Note that it is possible to encounter method types which cannot be
+ * constructed by this method, because their component types are
+ * not all reachable from a common class loader.
+ * <p>
+ * This method is included for the benefit of applications that must
+ * generate bytecodes that process method handles and {@code invokedynamic}.
+ * @param descriptor a bytecode-level type descriptor string "(T...)T"
+ * @param loader the class loader in which to look up the types
+ * @return a method type matching the bytecode-level type descriptor
+ * @throws NullPointerException if the string is null
+ * @throws IllegalArgumentException if the string is not well-formed
+ * @throws TypeNotPresentException if a named type cannot be found
+ */
public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader)
- throws IllegalArgumentException, TypeNotPresentException { return null; }
+ throws IllegalArgumentException, TypeNotPresentException
+ {
+ if (!descriptor.startsWith("(") || // also generates NPE if needed
+ descriptor.indexOf(')') < 0 ||
+ descriptor.indexOf('.') >= 0)
+ throw newIllegalArgumentException("not a method descriptor: "+descriptor);
+ List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
+ Class<?> rtype = types.remove(types.size() - 1);
+ checkSlotCount(types.size());
+ Class<?>[] ptypes = listToArray(types);
+ return makeImpl(rtype, ptypes, true);
+ }
+
+ /**
+ * Produces a bytecode descriptor representation of the method type.
+ * <p>
+ * Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
+ * Two distinct classes which share a common name but have different class loaders
+ * will appear identical when viewed within descriptor strings.
+ * <p>
+ * This method is included for the benefit of applications that must
+ * generate bytecodes that process method handles and {@code invokedynamic}.
+ * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
+ * because the latter requires a suitable class loader argument.
+ * @return the bytecode type descriptor representation
+ */
+ public String toMethodDescriptorString() {
+ String desc = methodDescriptor;
+ if (desc == null) {
+ desc = BytecodeDescriptor.unparse(this);
+ methodDescriptor = desc;
+ }
+ return desc;
+ }
+
+ /*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
+ return BytecodeDescriptor.unparse(cls);
+ }
- public String toMethodDescriptorString() { return null; }
+ /// Serialization.
+
+ /**
+ * There are no serializable fields for {@code MethodType}.
+ */
+ private static final java.io.ObjectStreamField[] serialPersistentFields = { };
+
+ /**
+ * Save the {@code MethodType} instance to a stream.
+ *
+ * @serialData
+ * For portability, the serialized format does not refer to named fields.
+ * Instead, the return type and parameter type arrays are written directly
+ * from the {@code writeObject} method, using two calls to {@code s.writeObject}
+ * as follows:
+ * <blockquote><pre>{@code
+s.writeObject(this.returnType());
+s.writeObject(this.parameterArray());
+ * }</pre></blockquote>
+ * <p>
+ * The deserialized field values are checked as if they were
+ * provided to the factory method {@link #methodType(Class,Class[]) methodType}.
+ * For example, null values, or {@code void} parameter types,
+ * will lead to exceptions during deserialization.
+ * @param s the stream to write the object to
+ * @throws java.io.IOException if there is a problem writing the object
+ */
+ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
+ s.defaultWriteObject(); // requires serialPersistentFields to be an empty array
+ s.writeObject(returnType());
+ s.writeObject(parameterArray());
+ }
+
+ /**
+ * Reconstitute the {@code MethodType} instance from a stream (that is,
+ * deserialize it).
+ * This instance is a scratch object with bogus final fields.
+ * It provides the parameters to the factory method called by
+ * {@link #readResolve readResolve}.
+ * After that call it is discarded.
+ * @param s the stream to read the object from
+ * @throws java.io.IOException if there is a problem reading the object
+ * @throws ClassNotFoundException if one of the component classes cannot be resolved
+ * @see #MethodType()
+ * @see #readResolve
+ * @see #writeObject
+ */
+ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject(); // requires serialPersistentFields to be an empty array
+
+ Class<?> returnType = (Class<?>) s.readObject();
+ Class<?>[] parameterArray = (Class<?>[]) s.readObject();
+
+ // Probably this object will never escape, but let's check
+ // the field values now, just to be sure.
+ checkRtype(returnType);
+ checkPtypes(parameterArray);
+
+ parameterArray = parameterArray.clone(); // make sure it is unshared
+ MethodType_init(returnType, parameterArray);
+ }
+
+ /**
+ * For serialization only.
+ * Sets the final fields to null, pending {@code Unsafe.putObject}.
+ */
+ private MethodType() {
+ this.rtype = null;
+ this.ptypes = null;
+ }
+ private void MethodType_init(Class<?> rtype, Class<?>[] ptypes) {
+ // In order to communicate these values to readResolve, we must
+ // store them into the implementation-specific final fields.
+ checkRtype(rtype);
+ checkPtypes(ptypes);
+ UNSAFE.putObject(this, rtypeOffset, rtype);
+ UNSAFE.putObject(this, ptypesOffset, ptypes);
+ }
+
+ // Support for resetting final fields while deserializing
+ private static final long rtypeOffset, ptypesOffset;
+ static {
+ try {
+ rtypeOffset = UNSAFE.objectFieldOffset
+ (MethodType.class.getDeclaredField("rtype"));
+ ptypesOffset = UNSAFE.objectFieldOffset
+ (MethodType.class.getDeclaredField("ptypes"));
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ /**
+ * Resolves and initializes a {@code MethodType} object
+ * after serialization.
+ * @return the fully initialized {@code MethodType} object
+ */
+ private Object readResolve() {
+ // Do not use a trusted path for deserialization:
+ //return makeImpl(rtype, ptypes, true);
+ // Verify all operands, and make sure ptypes is unshared:
+ return methodType(rtype, ptypes);
+ }
+
+ /**
+ * Simple implementation of weak concurrent intern set.
+ *
+ * @param <T> interned type
+ */
+ private static class ConcurrentWeakInternSet<T> {
+
+ private final ConcurrentMap<WeakEntry<T>, WeakEntry<T>> map;
+ private final ReferenceQueue<T> stale;
+
+ public ConcurrentWeakInternSet() {
+ this.map = new ConcurrentHashMap<>();
+ this.stale = new ReferenceQueue<>();
+ }
+
+ /**
+ * Get the existing interned element.
+ * This method returns null if no element is interned.
+ *
+ * @param elem element to look up
+ * @return the interned element
+ */
+ public T get(T elem) {
+ if (elem == null) throw new NullPointerException();
+ expungeStaleElements();
+
+ WeakEntry<T> value = map.get(new WeakEntry<>(elem));
+ if (value != null) {
+ T res = value.get();
+ if (res != null) {
+ return res;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Interns the element.
+ * Always returns non-null element, matching the one in the intern set.
+ * Under the race against another add(), it can return <i>different</i>
+ * element, if another thread beats us to interning it.
+ *
+ * @param elem element to add
+ * @return element that was actually added
+ */
+ public T add(T elem) {
+ if (elem == null) throw new NullPointerException();
+
+ // Playing double race here, and so spinloop is required.
+ // First race is with two concurrent updaters.
+ // Second race is with GC purging weak ref under our feet.
+ // Hopefully, we almost always end up with a single pass.
+ T interned;
+ WeakEntry<T> e = new WeakEntry<>(elem, stale);
+ do {
+ expungeStaleElements();
+ WeakEntry<T> exist = map.putIfAbsent(e, e);
+ interned = (exist == null) ? elem : exist.get();
+ } while (interned == null);
+ return interned;
+ }
+
+ private void expungeStaleElements() {
+ Reference<? extends T> reference;
+ while ((reference = stale.poll()) != null) {
+ map.remove(reference);
+ }
+ }
+
+ private static class WeakEntry<T> extends WeakReference<T> {
+
+ public final int hashcode;
+
+ public WeakEntry(T key, ReferenceQueue<T> queue) {
+ super(key, queue);
+ hashcode = key.hashCode();
+ }
+
+ public WeakEntry(T key) {
+ super(key);
+ hashcode = key.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof WeakEntry) {
+ Object that = ((WeakEntry) obj).get();
+ Object mine = get();
+ return (that == null || mine == null) ? (this == obj) : mine.equals(that);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashcode;
+ }
+
+ }
+ }
}
diff --git a/java/lang/invoke/VarHandle.java b/java/lang/invoke/VarHandle.java
index 562efb6e..bb93fcf5 100644
--- a/java/lang/invoke/VarHandle.java
+++ b/java/lang/invoke/VarHandle.java
@@ -25,10 +25,6 @@
package java.lang.invoke;
-import dalvik.system.VMRuntime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -446,16 +442,9 @@ public abstract class VarHandle {
*/
// END Android-removed: No VarForm in Android implementation.
- // BEGIN Android-added: fields for common metadata.
- /** The target type for accesses. */
- private final Class<?> varType;
-
- /** The coordinate types of a VarHandle instance. */
- private final List<Class<?>> coordinateTypes;
-
- /** BitMask of supported access mode indexed by AccessMode.ordinal(). */
- private final int accessModesBitMask;
- // END Android-added: fields for common metadata.
+ RuntimeException unsupported() {
+ return new UnsupportedOperationException();
+ }
// Plain accessors
@@ -485,8 +474,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object get(Object... args);
@@ -512,8 +501,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
void set(Object... args);
@@ -545,8 +534,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getVolatile(Object... args);
@@ -576,8 +565,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
void setVolatile(Object... args);
@@ -607,8 +596,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getOpaque(Object... args);
@@ -635,8 +624,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
void setOpaque(Object... args);
@@ -673,8 +662,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAcquire(Object... args);
@@ -705,8 +694,8 @@ public abstract class VarHandle {
* symbolic type descriptor, but a reference cast fails.
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
void setRelease(Object... args);
@@ -742,8 +731,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);
@@ -778,8 +767,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object compareAndExchange(Object... args);
@@ -814,8 +803,8 @@ public abstract class VarHandle {
* @see #getAcquire(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object compareAndExchangeAcquire(Object... args);
@@ -850,8 +839,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object compareAndExchangeRelease(Object... args);
@@ -890,8 +879,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
boolean weakCompareAndSetPlain(Object... args);
@@ -928,8 +917,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
boolean weakCompareAndSet(Object... args);
@@ -967,8 +956,8 @@ public abstract class VarHandle {
* @see #getAcquire(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
boolean weakCompareAndSetAcquire(Object... args);
@@ -1006,8 +995,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
boolean weakCompareAndSetRelease(Object... args);
@@ -1040,8 +1029,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndSet(Object... args);
@@ -1074,8 +1063,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndSetAcquire(Object... args);
@@ -1108,8 +1097,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndSetRelease(Object... args);
@@ -1145,8 +1134,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndAdd(Object... args);
@@ -1179,8 +1168,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndAddAcquire(Object... args);
@@ -1213,8 +1202,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndAddRelease(Object... args);
@@ -1255,8 +1244,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseOr(Object... args);
@@ -1293,8 +1282,8 @@ public abstract class VarHandle {
* @see #getAcquire(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseOrAcquire(Object... args);
@@ -1331,8 +1320,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseOrRelease(Object... args);
@@ -1369,8 +1358,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseAnd(Object... args);
@@ -1407,8 +1396,8 @@ public abstract class VarHandle {
* @see #getAcquire(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseAndAcquire(Object... args);
@@ -1445,8 +1434,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseAndRelease(Object... args);
@@ -1483,8 +1472,8 @@ public abstract class VarHandle {
* @see #getVolatile(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseXor(Object... args);
@@ -1521,8 +1510,8 @@ public abstract class VarHandle {
* @see #getAcquire(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseXorAcquire(Object... args);
@@ -1559,8 +1548,8 @@ public abstract class VarHandle {
* @see #get(Object...)
*/
public final native
- @MethodHandle.PolymorphicSignature
- // Android-removed: unsupported annotation.
+ // Android-removed: unsupported annotations.
+ // @MethodHandle.PolymorphicSignature
// @HotSpotIntrinsicCandidate
Object getAndBitwiseXorRelease(Object... args);
@@ -1570,11 +1559,7 @@ public abstract class VarHandle {
SET(void.class),
COMPARE_AND_SWAP(boolean.class),
COMPARE_AND_EXCHANGE(Object.class),
- GET_AND_UPDATE(Object.class),
- // Android-added: Finer grained access types.
- // These are used to help categorize the access modes that a VarHandle supports.
- GET_AND_UPDATE_BITWISE(Object.class),
- GET_AND_UPDATE_NUMERIC(Object.class);
+ GET_AND_UPDATE(Object.class);
final Class<?> returnType;
final boolean isMonomorphicInReturnType;
@@ -1611,8 +1596,6 @@ public abstract class VarHandle {
ps[i] = value;
return MethodType.methodType(value, ps);
case GET_AND_UPDATE:
- case GET_AND_UPDATE_BITWISE:
- case GET_AND_UPDATE_NUMERIC:
ps = allocateParameters(1, receiver, intermediate);
i = fillParameters(ps, receiver, intermediate);
ps[i] = value;
@@ -1763,73 +1746,73 @@ public abstract class VarHandle {
* method
* {@link VarHandle#getAndAdd VarHandle.getAndAdd}
*/
- GET_AND_ADD("getAndAdd", AccessType.GET_AND_UPDATE_NUMERIC),
+ GET_AND_ADD("getAndAdd", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndAddAcquire VarHandle.getAndAddAcquire}
*/
- GET_AND_ADD_ACQUIRE("getAndAddAcquire", AccessType.GET_AND_UPDATE_NUMERIC),
+ GET_AND_ADD_ACQUIRE("getAndAddAcquire", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndAddRelease VarHandle.getAndAddRelease}
*/
- GET_AND_ADD_RELEASE("getAndAddRelease", AccessType.GET_AND_UPDATE_NUMERIC),
+ GET_AND_ADD_RELEASE("getAndAddRelease", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseOr VarHandle.getAndBitwiseOr}
*/
- GET_AND_BITWISE_OR("getAndBitwiseOr", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_OR("getAndBitwiseOr", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseOrRelease VarHandle.getAndBitwiseOrRelease}
*/
- GET_AND_BITWISE_OR_RELEASE("getAndBitwiseOrRelease", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_OR_RELEASE("getAndBitwiseOrRelease", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseOrAcquire VarHandle.getAndBitwiseOrAcquire}
*/
- GET_AND_BITWISE_OR_ACQUIRE("getAndBitwiseOrAcquire", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_OR_ACQUIRE("getAndBitwiseOrAcquire", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseAnd VarHandle.getAndBitwiseAnd}
*/
- GET_AND_BITWISE_AND("getAndBitwiseAnd", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_AND("getAndBitwiseAnd", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseAndRelease VarHandle.getAndBitwiseAndRelease}
*/
- GET_AND_BITWISE_AND_RELEASE("getAndBitwiseAndRelease", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_AND_RELEASE("getAndBitwiseAndRelease", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseAndAcquire VarHandle.getAndBitwiseAndAcquire}
*/
- GET_AND_BITWISE_AND_ACQUIRE("getAndBitwiseAndAcquire", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_AND_ACQUIRE("getAndBitwiseAndAcquire", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseXor VarHandle.getAndBitwiseXor}
*/
- GET_AND_BITWISE_XOR("getAndBitwiseXor", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_XOR("getAndBitwiseXor", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseXorRelease VarHandle.getAndBitwiseXorRelease}
*/
- GET_AND_BITWISE_XOR_RELEASE("getAndBitwiseXorRelease", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_XOR_RELEASE("getAndBitwiseXorRelease", AccessType.GET_AND_UPDATE),
/**
* The access mode whose access is specified by the corresponding
* method
* {@link VarHandle#getAndBitwiseXorAcquire VarHandle.getAndBitwiseXorAcquire}
*/
- GET_AND_BITWISE_XOR_ACQUIRE("getAndBitwiseXorAcquire", AccessType.GET_AND_UPDATE_BITWISE),
+ GET_AND_BITWISE_XOR_ACQUIRE("getAndBitwiseXorAcquire", AccessType.GET_AND_UPDATE),
;
static final Map<String, AccessMode> methodNameToAccessMode;
@@ -1915,11 +1898,8 @@ public abstract class VarHandle {
* @return the variable type of variables referenced by this VarHandle
*/
public final Class<?> varType() {
- // Android-removed: existing implementation.
- // MethodType typeSet = accessModeType(AccessMode.SET);
- // return typeSet.parameterType(typeSet.parameterCount() - 1)
- // Android-added: return instance field.
- return varType;
+ MethodType typeSet = accessModeType(AccessMode.SET);
+ return typeSet.parameterType(typeSet.parameterCount() - 1);
}
/**
@@ -1929,11 +1909,8 @@ public abstract class VarHandle {
* list is unmodifiable
*/
public final List<Class<?>> coordinateTypes() {
- // Android-removed: existing implementation.
- // MethodType typeGet = accessModeType(AccessMode.GET);
- // return typeGet.parameterList();
- // Android-added: return instance field.
- return coordinateTypes;
+ MethodType typeGet = accessModeType(AccessMode.GET);
+ return typeGet.parameterList();
}
/**
@@ -1963,18 +1940,9 @@ public abstract class VarHandle {
*/
// END Android-removed: Relies on internal class that is not part of the
// Android implementation.
- // Android-added: alternative implementation.
- switch (coordinateTypes.size()) {
- case 0:
- return accessMode.at.accessModeType(null, varType);
- case 1:
- return accessMode.at.accessModeType(coordinateTypes.get(0), varType);
- case 2:
- return accessMode.at.accessModeType(coordinateTypes.get(0), varType,
- coordinateTypes.get(1));
- default:
- throw new InternalError("bad coordinateTypes: " + coordinateTypes);
- }
+ // Android-added: Throw an exception until implemented.
+ unsupported(); // TODO(b/65872996)
+ return null;
}
// Android-removed: Not part of the Android implementation.
@@ -1996,9 +1964,9 @@ public abstract class VarHandle {
public final boolean isAccessModeSupported(AccessMode accessMode) {
// Android-removed: Refers to unused field vform.
// return AccessMode.getMemberName(accessMode.ordinal(), vform) != null;
- // Android-added: use accessModesBitsMask field.
- final int testBit = 1 << accessMode.ordinal();
- return (accessModesBitMask & testBit) == testBit;
+ // Android-added: Throw an exception until implemented.
+ unsupported(); // TODO(b/65872996)
+ return false;
}
/**
@@ -2034,10 +2002,9 @@ public abstract class VarHandle {
bindTo(this);
}
*/
- // END Android-removed: no vform field in Android implementation.
-
- // Android-added: basic implementation following description in javadoc for this method.
- return MethodHandles.varHandleInvoker(accessMode, accessModeType(accessMode)).bindTo(this);
+ // Android-added: Throw an exception until implemented.
+ unsupported(); // TODO(b/65872996)
+ return null;
}
// BEGIN Android-removed: Not used in Android implementation.
@@ -2088,8 +2055,8 @@ public abstract class VarHandle {
*/
// END Android-removed: Not used in Android implementation.
- // BEGIN Android-removed: No VarForm in Android implementation.
/*non-public*/
+ // BEGIN Android-removed: No VarForm in Android implementation.
/*
final void updateVarForm(VarForm newVForm) {
if (vform == newVForm) return;
@@ -2191,197 +2158,4 @@ public abstract class VarHandle {
// NB The compiler recognizes all the fences here as intrinsics.
UNSAFE.storeFence();
}
-
- // BEGIN Android-added: package private constructors.
- /**
- * Constructor for VarHandle with no coordinates.
- *
- * @param varType the variable type of variables to be referenced
- * @param isFinal whether the target variables are final (non-modifiable)
- * @hide
- */
- VarHandle(Class<?> varType, boolean isFinal) {
- this.varType = varType;
- this.coordinateTypes = Collections.EMPTY_LIST;
- this.accessModesBitMask = alignedAccessModesBitMask(varType, isFinal);
- }
-
- /**
- * Constructor for VarHandle with one coordinate.
- *
- * @param varType the variable type of variables to be referenced
- * @param isFinal whether the target variables are final (non-modifiable)
- * @param coordinate the coordinate
- * @hide
- */
- VarHandle(Class<?> varType, boolean isFinal, Class<?> coordinate) {
- this.varType = varType;
- this.coordinateTypes = Collections.singletonList(coordinate);
- this.accessModesBitMask = alignedAccessModesBitMask(varType, isFinal);
- }
-
- /**
- * Constructor for VarHandle with two coordinates.
- *
- * @param varType the variable type of variables to be referenced
- * @param backingArrayType the type of the array accesses will be performed on
- * @param isFinal whether the target variables are final (non-modifiable)
- * @param coordinate0 the first coordinate
- * @param coordinate1 the second coordinate
- * @hide
- */
- VarHandle(Class<?> varType, Class<?> backingArrayType, boolean isFinal,
- Class<?> coordinate0, Class<?> coordinate1) {
- this.varType = varType;
- this.coordinateTypes = Collections.unmodifiableList(
- Arrays.asList(coordinate0, coordinate1));
- Class<?> backingArrayComponentType = backingArrayType.getComponentType();
- if (backingArrayComponentType != varType && backingArrayComponentType != byte.class) {
- throw new InternalError("Unsupported backingArrayType: " + backingArrayType);
- }
-
- if (backingArrayType.getComponentType() == varType) {
- this.accessModesBitMask = alignedAccessModesBitMask(varType, isFinal);
- } else {
- this.accessModesBitMask = unalignedAccessModesBitMask(varType);
- }
- }
- // END Android-changed: package private constructors.
-
- // BEGIN Android-added: helper state for VarHandle properties.
-
- /** BitMask of access modes that do not change the memory referenced by a VarHandle.
- * An example being a read of a variable with volatile ordering effects. */
- private final static int READ_ACCESS_MODES_BIT_MASK;
-
- /** BitMask of access modes that write to the memory referenced by
- * a VarHandle. This does not include any compare and update
- * access modes, nor any bitwise or numeric access modes. An
- * example being a write to variable with release ordering
- * effects.
- */
- private final static int WRITE_ACCESS_MODES_BIT_MASK;
-
- /** BitMask of access modes that are applicable to types
- * supporting for atomic updates. This includes access modes that
- * both read and write a variable such as compare-and-set.
- */
- private final static int ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
-
- /** BitMask of access modes that are applicable to types
- * supporting numeric atomic update operations. */
- private final static int NUMERIC_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
-
- /** BitMask of access modes that are applicable to types
- * supporting bitwise atomic update operations. */
- private final static int BITWISE_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
-
- /** BitMask of all access modes. */
- private final static int ALL_MODES_BIT_MASK;
-
- /** Indicator of machine word size. */
- private final static boolean RUNNING_ON_64BIT = VMRuntime.getRuntime().is64Bit();
-
- static {
- // Check we're not about to overflow the storage of the
- // bitmasks here and in the accessModesBitMask field.
- if (AccessMode.values().length > Integer.SIZE) {
- throw new InternalError("accessModes overflow");
- }
-
- // Access modes bit mask declarations and initialization order
- // follows the presentation order in JEP193.
- READ_ACCESS_MODES_BIT_MASK = accessTypesToBitMask(EnumSet.of(AccessType.GET));
-
- WRITE_ACCESS_MODES_BIT_MASK = accessTypesToBitMask(EnumSet.of(AccessType.SET));
-
- ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK =
- accessTypesToBitMask(EnumSet.of(AccessType.COMPARE_AND_EXCHANGE,
- AccessType.COMPARE_AND_SWAP,
- AccessType.GET_AND_UPDATE));
-
- NUMERIC_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK =
- accessTypesToBitMask(EnumSet.of(AccessType.GET_AND_UPDATE_NUMERIC));
-
- BITWISE_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK =
- accessTypesToBitMask(EnumSet.of(AccessType.GET_AND_UPDATE_BITWISE));
-
- ALL_MODES_BIT_MASK = (READ_ACCESS_MODES_BIT_MASK |
- WRITE_ACCESS_MODES_BIT_MASK |
- ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK |
- NUMERIC_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK |
- BITWISE_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK);
- }
-
- static int accessTypesToBitMask(final EnumSet<AccessType> accessTypes) {
- int m = 0;
- for (AccessMode accessMode : AccessMode.values()) {
- if (accessTypes.contains(accessMode.at)) {
- m |= 1 << accessMode.ordinal();
- }
- }
- return m;
- }
-
- static int alignedAccessModesBitMask(Class<?> varType, boolean isFinal) {
- // For aligned accesses, the supported access modes are described in:
- // @see java.lang.invoke.MethodHandles.Lookup#findVarHandle
- int bitMask = ALL_MODES_BIT_MASK;
-
- // If the field is declared final, keep only the read access modes.
- if (isFinal) {
- bitMask &= READ_ACCESS_MODES_BIT_MASK;
- }
-
- // If the field is anything other than byte, short, char, int,
- // long, float, double then remove the numeric atomic update
- // access modes.
- if (varType != byte.class && varType != short.class && varType != char.class &&
- varType != int.class && varType != long.class
- && varType != float.class && varType != double.class) {
- bitMask &= ~NUMERIC_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
- }
-
- // If the field is not integral, remove the bitwise atomic update access modes.
- if (varType != boolean.class && varType != byte.class && varType != short.class &&
- varType != char.class && varType != int.class && varType != long.class) {
- bitMask &= ~BITWISE_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
- }
- return bitMask;
- }
-
- static int unalignedAccessModesBitMask(Class<?> varType) {
- // The VarHandle refers to a view of byte array or a
- // view of a byte buffer. The corresponding accesses
- // maybe unaligned so the access modes are more
- // restrictive than field or array element accesses.
- //
- // The supported access modes are described in:
- // @see java.lang.invoke.MethodHandles#byteArrayViewVarHandle
- int bitMask = 0;
-
- // Read/write access modes supported for all types except for
- // long and double on 32-bit platforms.
- if (RUNNING_ON_64BIT || (varType != long.class && varType != double.class)) {
- bitMask |= READ_ACCESS_MODES_BIT_MASK | WRITE_ACCESS_MODES_BIT_MASK;
- }
-
- // int, long, float, double support atomic update modes per documentation.
- if (varType == int.class || varType == long.class ||
- varType == float.class || varType == double.class) {
- bitMask |= ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
- }
-
- // int and long support numeric updates per documentation.
- if (varType == int.class || varType == long.class) {
- bitMask |= NUMERIC_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
- }
-
- // int and long support bitwise updates per documentation.
- if (varType == int.class || varType == long.class) {
- bitMask |= BITWISE_ATOMIC_UPDATE_ACCESS_MODES_BIT_MASK;
- }
- return bitMask;
- }
- // END Android-added: helper class for VarHandle properties.
}
diff --git a/java/net/Socket.java b/java/net/Socket.java
index e36d15b2..03e2b717 100644
--- a/java/net/Socket.java
+++ b/java/net/Socket.java
@@ -122,7 +122,7 @@ class Socket implements java.io.Closeable {
Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY
: sun.net.ApplicationProxy.create(proxy);
Proxy.Type type = p.type();
- // Android-changed: Removed HTTP proxy support.
+ // Android-changed: Removed HTTP proxy suppport.
// if (type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) {
if (type == Proxy.Type.SOCKS) {
SecurityManager security = System.getSecurityManager();
@@ -214,7 +214,6 @@ class Socket implements java.io.Closeable {
public Socket(String host, int port)
throws UnknownHostException, IOException
{
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(InetAddress.getAllByName(host), port, (SocketAddress) null, true);
}
@@ -246,7 +245,6 @@ class Socket implements java.io.Closeable {
* @see SecurityManager#checkConnect
*/
public Socket(InetAddress address, int port) throws IOException {
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(nonNullAddress(address), port, (SocketAddress) null, true);
}
@@ -288,7 +286,6 @@ class Socket implements java.io.Closeable {
*/
public Socket(String host, int port, InetAddress localAddr,
int localPort) throws IOException {
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(InetAddress.getAllByName(host), port,
new InetSocketAddress(localAddr, localPort), true);
}
@@ -330,7 +327,6 @@ class Socket implements java.io.Closeable {
*/
public Socket(InetAddress address, int port, InetAddress localAddr,
int localPort) throws IOException {
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(nonNullAddress(address), port,
new InetSocketAddress(localAddr, localPort), true);
}
@@ -378,7 +374,6 @@ class Socket implements java.io.Closeable {
*/
@Deprecated
public Socket(String host, int port, boolean stream) throws IOException {
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(InetAddress.getAllByName(host), port, (SocketAddress) null, stream);
}
@@ -420,11 +415,9 @@ class Socket implements java.io.Closeable {
*/
@Deprecated
public Socket(InetAddress host, int port, boolean stream) throws IOException {
- // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
this(nonNullAddress(host), port, new InetSocketAddress(0), stream);
}
- // BEGIN Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
private static InetAddress[] nonNullAddress(InetAddress address) {
// backward compatibility
if (address == null)
@@ -433,6 +426,8 @@ class Socket implements java.io.Closeable {
return new InetAddress[] { address };
}
+ // Android-changed: Socket ctor should try all addresses
+ // b/30007735
private Socket(InetAddress[] addresses, int port, SocketAddress localAddr,
boolean stream) throws IOException {
if (addresses == null || addresses.length == 0) {
@@ -451,8 +446,9 @@ class Socket implements java.io.Closeable {
break;
} catch (IOException | IllegalArgumentException | SecurityException e) {
try {
- // Android-changed: Let ctor call impl.close() instead of overridable close().
- // Subclasses may not expect a call to close() coming from this constructor.
+ // Android-changed:
+ // Do not call #close, classes that extend this class may do not expect a call
+ // to #close coming from the superclass constructor.
impl.close();
closed = true;
} catch (IOException ce) {
@@ -472,7 +468,6 @@ class Socket implements java.io.Closeable {
closed = false;
}
}
- // END Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735
/**
* Creates the socket implementation.
@@ -1065,7 +1060,7 @@ class Socket implements java.io.Closeable {
*
* The setting only affects socket close.
*
- * @return the setting for {@link SocketOptions#SO_LINGER SO_LINGER}.
+ * @return the setting for SO_LINGER.
* @exception SocketException if there is an error
* in the underlying protocol, such as a TCP error.
* @since JDK1.1
@@ -1773,7 +1768,7 @@ class Socket implements java.io.Closeable {
/* Not implemented yet */
}
- // Android-added: getFileDescriptor$() method for testing and internal use.
+ // Android-added: for testing and internal use.
/**
* @hide internal use only
*/
diff --git a/java/net/SocketException.java b/java/net/SocketException.java
index 64ae7710..286bc427 100644
--- a/java/net/SocketException.java
+++ b/java/net/SocketException.java
@@ -54,7 +54,6 @@ class SocketException extends IOException {
public SocketException() {
}
- // BEGIN Android-added: SocketException ctor with cause for internal use.
/** @hide */
public SocketException(Throwable cause) {
super(cause);
@@ -64,5 +63,4 @@ class SocketException extends IOException {
public SocketException(String msg, Throwable cause) {
super(msg, cause);
}
- // END Android-added: SocketException ctor with cause for internal use.
}
diff --git a/java/net/SocketImpl.java b/java/net/SocketImpl.java
index ade2630f..c0db070e 100644
--- a/java/net/SocketImpl.java
+++ b/java/net/SocketImpl.java
@@ -227,7 +227,6 @@ public abstract class SocketImpl implements SocketOptions {
return fd;
}
- // Android-added: getFD$() for testing.
/**
* @hide used by java.nio tests
*/
diff --git a/java/net/SocketInputStream.java b/java/net/SocketInputStream.java
index 8d0e0c57..f5c4c8f8 100644
--- a/java/net/SocketInputStream.java
+++ b/java/net/SocketInputStream.java
@@ -44,11 +44,6 @@ import sun.net.ConnectionResetException;
*/
class SocketInputStream extends FileInputStream
{
- // Android-removed: Android doesn't need to call native init.
- // static {
- // init();
- //}
-
private boolean eof;
private AbstractPlainSocketImpl impl = null;
private byte temp[];
@@ -171,7 +166,6 @@ class SocketInputStream extends FileInputStream
// acquire file descriptor and do the read
FileDescriptor fd = impl.acquireFD();
try {
- // Android-added: Check BlockGuard policy in read().
BlockGuard.getThreadPolicy().onNetwork();
n = socketRead(fd, b, off, length, timeout);
if (n > 0) {
@@ -295,11 +289,4 @@ class SocketInputStream extends FileInputStream
* Overrides finalize, the fd is closed by the Socket.
*/
protected void finalize() {}
-
- // Android-removed: Android doesn't need native init.
- /*
- * Perform class load-time initializations.
- *
- private native static void init();
- */
}
diff --git a/java/net/SocketOutputStream.java b/java/net/SocketOutputStream.java
index 9e8e7926..c0173f0d 100644
--- a/java/net/SocketOutputStream.java
+++ b/java/net/SocketOutputStream.java
@@ -43,11 +43,6 @@ import dalvik.system.BlockGuard;
*/
class SocketOutputStream extends FileOutputStream
{
- // Android-removed: Android doesn't need to call native init.
- // static {
- // init();
- //}
-
private AbstractPlainSocketImpl impl = null;
private byte temp[] = new byte[1];
private Socket socket = null;
@@ -100,8 +95,6 @@ class SocketOutputStream extends FileOutputStream
* @exception IOException If an I/O error has occurred.
*/
private void socketWrite(byte b[], int off, int len) throws IOException {
-
-
if (len <= 0 || off < 0 || len > b.length - off) {
if (len == 0) {
return;
@@ -112,7 +105,6 @@ class SocketOutputStream extends FileOutputStream
FileDescriptor fd = impl.acquireFD();
try {
- // Android-added: Check BlockGuard policy in socketWrite.
BlockGuard.getThreadPolicy().onNetwork();
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
@@ -182,11 +174,4 @@ class SocketOutputStream extends FileOutputStream
* Overrides finalize, the fd is closed by the Socket.
*/
protected void finalize() {}
-
- // Android-removed: Android doesn't need native init.
- /*
- * Perform class load-time initializations.
- *
- private native static void init();
- */
}
diff --git a/java/net/SocksSocketImpl.java b/java/net/SocksSocketImpl.java
index 0d9d8f59..a81e219b 100644
--- a/java/net/SocksSocketImpl.java
+++ b/java/net/SocksSocketImpl.java
@@ -347,91 +347,12 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
epoint.getPort());
}
if (server == null) {
- // Android-removed: Logic to establish proxy connection based on default ProxySelector.
- // Removed code that tried to establish proxy connection if ProxySelector#getDefault()
- // is not null. This was never the case in previous Android releases, was causing
- // issues and therefore was removed.
+ // Android-removed: Logic to establish proxy connection based on default ProxySelector
/*
- // This is the general case
- // server is not null only when the socket was created with a
- // specified proxy in which case it does bypass the ProxySelector
- ProxySelector sel = java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<ProxySelector>() {
- public ProxySelector run() {
- return ProxySelector.getDefault();
- }
- });
- if (sel == null) {
- /*
- * No default proxySelector --> direct connection
- *
- super.connect(epoint, remainingMillis(deadlineMillis));
- return;
- }
- URI uri;
- // Use getHostString() to avoid reverse lookups
- String host = epoint.getHostString();
- // IPv6 litteral?
- if (epoint.getAddress() instanceof Inet6Address &&
- (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
- host = "[" + host + "]";
- }
- try {
- uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
- } catch (URISyntaxException e) {
- // This shouldn't happen
- assert false : e;
- uri = null;
- }
- Proxy p = null;
- IOException savedExc = null;
- java.util.Iterator<Proxy> iProxy = null;
- iProxy = sel.select(uri).iterator();
- if (iProxy == null || !(iProxy.hasNext())) {
- super.connect(epoint, remainingMillis(deadlineMillis));
- return;
- }
- while (iProxy.hasNext()) {
- p = iProxy.next();
- if (p == null || p.type() != Proxy.Type.SOCKS) {
- super.connect(epoint, remainingMillis(deadlineMillis));
- return;
- }
-
- if (!(p.address() instanceof InetSocketAddress))
- throw new SocketException("Unknown address type for proxy: " + p);
- // Use getHostString() to avoid reverse lookups
- server = ((InetSocketAddress) p.address()).getHostString();
- serverPort = ((InetSocketAddress) p.address()).getPort();
- if (p instanceof SocksProxy) {
- if (((SocksProxy)p).protocolVersion() == 4) {
- useV4 = true;
- }
- }
-
- // Connects to the SOCKS server
- try {
- privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
- // Worked, let's get outta here
- break;
- } catch (IOException e) {
- // Ooops, let's notify the ProxySelector
- sel.connectFailed(uri,p.address(),e);
- server = null;
- serverPort = -1;
- savedExc = e;
- // Will continue the while loop and try the next proxy
- }
- }
-
- /*
- * If server is still null at this point, none of the proxy
- * worked
- *
- if (server == null) {
- throw new SocketException("Can't connect to SOCKS proxy:"
- + savedExc.getMessage());
- }
+ * Removed code that tried to establish proxy connection if
+ * ProxySelector#getDefault() is not null.
+ * This was never the case in previous android releases, was causing
+ * issues and therefore was removed.
*/
super.connect(epoint, remainingMillis(deadlineMillis));
return;
@@ -587,458 +508,6 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
external_address = epoint;
}
- // Android-removed: Dead code. bindV4, socksBind, acceptFrom methods.
- /*
- private void bindV4(InputStream in, OutputStream out,
- InetAddress baddr,
- int lport) throws IOException {
- if (!(baddr instanceof Inet4Address)) {
- throw new SocketException("SOCKS V4 requires IPv4 only addresses");
- }
- super.bind(baddr, lport);
- byte[] addr1 = baddr.getAddress();
- /* Test for AnyLocal *
- InetAddress naddr = baddr;
- if (naddr.isAnyLocalAddress()) {
- naddr = AccessController.doPrivileged(
- new PrivilegedAction<InetAddress>() {
- public InetAddress run() {
- return cmdsock.getLocalAddress();
-
- }
- });
- addr1 = naddr.getAddress();
- }
- out.write(PROTO_VERS4);
- out.write(BIND);
- out.write((super.getLocalPort() >> 8) & 0xff);
- out.write((super.getLocalPort() >> 0) & 0xff);
- out.write(addr1);
- String userName = getUserName();
- try {
- out.write(userName.getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write(0);
- out.flush();
- byte[] data = new byte[8];
- int n = readSocksReply(in, data);
- if (n != 8)
- throw new SocketException("Reply from SOCKS server has bad length: " + n);
- if (data[0] != 0 && data[0] != 4)
- throw new SocketException("Reply from SOCKS server has bad version");
- SocketException ex = null;
- switch (data[1]) {
- case 90:
- // Success!
- external_address = new InetSocketAddress(baddr, lport);
- break;
- case 91:
- ex = new SocketException("SOCKS request rejected");
- break;
- case 92:
- ex = new SocketException("SOCKS server couldn't reach destination");
- break;
- case 93:
- ex = new SocketException("SOCKS authentication failed");
- break;
- default:
- ex = new SocketException("Reply from SOCKS server contains bad status");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- throw ex;
- }
-
- }
-
- /**
- * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
- * means "accept incoming connection from", so the SocketAddress is the
- * the one of the host we do accept connection from.
- *
- * @param saddr the Socket address of the remote host.
- * @exception IOException if an I/O error occurs when binding this socket.
- *
- protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
- if (socket != null) {
- // this is a client socket, not a server socket, don't
- // call the SOCKS proxy for a bind!
- return;
- }
-
- // Connects to the SOCKS server
-
- if (server == null) {
- // This is the general case
- // server is not null only when the socket was created with a
- // specified proxy in which case it does bypass the ProxySelector
- ProxySelector sel = java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<ProxySelector>() {
- public ProxySelector run() {
- return ProxySelector.getDefault();
- }
- });
- if (sel == null) {
- /*
- * No default proxySelector --> direct connection
- *
- return;
- }
- URI uri;
- // Use getHostString() to avoid reverse lookups
- String host = saddr.getHostString();
- // IPv6 litteral?
- if (saddr.getAddress() instanceof Inet6Address &&
- (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
- host = "[" + host + "]";
- }
- try {
- uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
- } catch (URISyntaxException e) {
- // This shouldn't happen
- assert false : e;
- uri = null;
- }
- Proxy p = null;
- Exception savedExc = null;
- java.util.Iterator<Proxy> iProxy = null;
- iProxy = sel.select(uri).iterator();
- if (iProxy == null || !(iProxy.hasNext())) {
- return;
- }
- while (iProxy.hasNext()) {
- p = iProxy.next();
- if (p == null || p.type() != Proxy.Type.SOCKS) {
- return;
- }
-
- if (!(p.address() instanceof InetSocketAddress))
- throw new SocketException("Unknown address type for proxy: " + p);
- // Use getHostString() to avoid reverse lookups
- server = ((InetSocketAddress) p.address()).getHostString();
- serverPort = ((InetSocketAddress) p.address()).getPort();
- if (p instanceof SocksProxy) {
- if (((SocksProxy)p).protocolVersion() == 4) {
- useV4 = true;
- }
- }
-
- // Connects to the SOCKS server
- try {
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>() {
- public Void run() throws Exception {
- cmdsock = new Socket(new PlainSocketImpl());
- cmdsock.connect(new InetSocketAddress(server, serverPort));
- cmdIn = cmdsock.getInputStream();
- cmdOut = cmdsock.getOutputStream();
- return null;
- }
- });
- } catch (Exception e) {
- // Ooops, let's notify the ProxySelector
- sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
- server = null;
- serverPort = -1;
- cmdsock = null;
- savedExc = e;
- // Will continue the while loop and try the next proxy
- }
- }
-
- /*
- * If server is still null at this point, none of the proxy
- * worked
- *
- if (server == null || cmdsock == null) {
- throw new SocketException("Can't connect to SOCKS proxy:"
- + savedExc.getMessage());
- }
- } else {
- try {
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>() {
- public Void run() throws Exception {
- cmdsock = new Socket(new PlainSocketImpl());
- cmdsock.connect(new InetSocketAddress(server, serverPort));
- cmdIn = cmdsock.getInputStream();
- cmdOut = cmdsock.getOutputStream();
- return null;
- }
- });
- } catch (Exception e) {
- throw new SocketException(e.getMessage());
- }
- }
- BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
- InputStream in = cmdIn;
- if (useV4) {
- bindV4(in, out, saddr.getAddress(), saddr.getPort());
- return;
- }
- out.write(PROTO_VERS);
- out.write(2);
- out.write(NO_AUTH);
- out.write(USER_PASSW);
- out.flush();
- byte[] data = new byte[2];
- int i = readSocksReply(in, data);
- if (i != 2 || ((int)data[0]) != PROTO_VERS) {
- // Maybe it's not a V5 sever after all
- // Let's try V4 before we give up
- bindV4(in, out, saddr.getAddress(), saddr.getPort());
- return;
- }
- if (((int)data[1]) == NO_METHODS)
- throw new SocketException("SOCKS : No acceptable methods");
- if (!authenticate(data[1], in, out)) {
- throw new SocketException("SOCKS : authentication failed");
- }
- // We're OK. Let's issue the BIND command.
- out.write(PROTO_VERS);
- out.write(BIND);
- out.write(0);
- int lport = saddr.getPort();
- if (saddr.isUnresolved()) {
- out.write(DOMAIN_NAME);
- out.write(saddr.getHostName().length());
- try {
- out.write(saddr.getHostName().getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- } else if (saddr.getAddress() instanceof Inet4Address) {
- byte[] addr1 = saddr.getAddress().getAddress();
- out.write(IPV4);
- out.write(addr1);
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- out.flush();
- } else if (saddr.getAddress() instanceof Inet6Address) {
- byte[] addr1 = saddr.getAddress().getAddress();
- out.write(IPV6);
- out.write(addr1);
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- out.flush();
- } else {
- cmdsock.close();
- throw new SocketException("unsupported address type : " + saddr);
- }
- data = new byte[4];
- i = readSocksReply(in, data);
- SocketException ex = null;
- int len, nport;
- byte[] addr;
- switch (data[1]) {
- case REQUEST_OK:
- // success!
- switch(data[3]) {
- case IPV4:
- addr = new byte[4];
- i = readSocksReply(in, addr);
- if (i != 4)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address =
- new InetSocketAddress(new Inet4Address("", addr) , nport);
- break;
- case DOMAIN_NAME:
- len = data[1];
- byte[] host = new byte[len];
- i = readSocksReply(in, host);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address = new InetSocketAddress(new String(host), nport);
- break;
- case IPV6:
- len = data[1];
- addr = new byte[len];
- i = readSocksReply(in, addr);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address =
- new InetSocketAddress(new Inet6Address("", addr), nport);
- break;
- }
- break;
- case GENERAL_FAILURE:
- ex = new SocketException("SOCKS server general failure");
- break;
- case NOT_ALLOWED:
- ex = new SocketException("SOCKS: Bind not allowed by ruleset");
- break;
- case NET_UNREACHABLE:
- ex = new SocketException("SOCKS: Network unreachable");
- break;
- case HOST_UNREACHABLE:
- ex = new SocketException("SOCKS: Host unreachable");
- break;
- case CONN_REFUSED:
- ex = new SocketException("SOCKS: Connection refused");
- break;
- case TTL_EXPIRED:
- ex = new SocketException("SOCKS: TTL expired");
- break;
- case CMD_NOT_SUPPORTED:
- ex = new SocketException("SOCKS: Command not supported");
- break;
- case ADDR_TYPE_NOT_SUP:
- ex = new SocketException("SOCKS: address type not supported");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- cmdsock.close();
- cmdsock = null;
- throw ex;
- }
- cmdIn = in;
- cmdOut = out;
- }
-
- /**
- * Accepts a connection from a specific host.
- *
- * @param s the accepted connection.
- * @param saddr the socket address of the host we do accept
- * connection from
- * @exception IOException if an I/O error occurs when accepting the
- * connection.
- *
- protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
- if (cmdsock == null) {
- // Not a Socks ServerSocket.
- return;
- }
- InputStream in = cmdIn;
- // Sends the "SOCKS BIND" request.
- socksBind(saddr);
- in.read();
- int i = in.read();
- in.read();
- SocketException ex = null;
- int nport;
- byte[] addr;
- InetSocketAddress real_end = null;
- switch (i) {
- case REQUEST_OK:
- // success!
- i = in.read();
- switch(i) {
- case IPV4:
- addr = new byte[4];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end =
- new InetSocketAddress(new Inet4Address("", addr) , nport);
- break;
- case DOMAIN_NAME:
- int len = in.read();
- addr = new byte[len];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end = new InetSocketAddress(new String(addr), nport);
- break;
- case IPV6:
- addr = new byte[16];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end =
- new InetSocketAddress(new Inet6Address("", addr), nport);
- break;
- }
- break;
- case GENERAL_FAILURE:
- ex = new SocketException("SOCKS server general failure");
- break;
- case NOT_ALLOWED:
- ex = new SocketException("SOCKS: Accept not allowed by ruleset");
- break;
- case NET_UNREACHABLE:
- ex = new SocketException("SOCKS: Network unreachable");
- break;
- case HOST_UNREACHABLE:
- ex = new SocketException("SOCKS: Host unreachable");
- break;
- case CONN_REFUSED:
- ex = new SocketException("SOCKS: Connection refused");
- break;
- case TTL_EXPIRED:
- ex = new SocketException("SOCKS: TTL expired");
- break;
- case CMD_NOT_SUPPORTED:
- ex = new SocketException("SOCKS: Command not supported");
- break;
- case ADDR_TYPE_NOT_SUP:
- ex = new SocketException("SOCKS: address type not supported");
- break;
- }
- if (ex != null) {
- cmdIn.close();
- cmdOut.close();
- cmdsock.close();
- cmdsock = null;
- throw ex;
- }
-
- /**
- * This is where we have to do some fancy stuff.
- * The datastream from the socket "accepted" by the proxy will
- * come through the cmdSocket. So we have to swap the socketImpls
- *
- if (s instanceof SocksSocketImpl) {
- ((SocksSocketImpl)s).external_address = real_end;
- }
- if (s instanceof PlainSocketImpl) {
- PlainSocketImpl psi = (PlainSocketImpl) s;
- psi.setInputStream((SocketInputStream) in);
- psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
- psi.setAddress(cmdsock.getImpl().getInetAddress());
- psi.setPort(cmdsock.getImpl().getPort());
- psi.setLocalPort(cmdsock.getImpl().getLocalPort());
- } else {
- s.fd = cmdsock.getImpl().fd;
- s.address = cmdsock.getImpl().address;
- s.port = cmdsock.getImpl().port;
- s.localport = cmdsock.getImpl().localport;
- }
-
- // Need to do that so that the socket won't be closed
- // when the ServerSocket is closed by the user.
- // It kinds of detaches the Socket because it is now
- // used elsewhere.
- cmdsock = null;
- }
- */
-
/**
* Returns the value of this socket's {@code address} field.
*
diff --git a/java/net/URLClassLoader.java b/java/net/URLClassLoader.java
index 6e8acb1c..edb9b882 100644
--- a/java/net/URLClassLoader.java
+++ b/java/net/URLClassLoader.java
@@ -103,8 +103,8 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
if (security != null) {
security.checkCreateClassLoader();
}
+ ucp = new URLClassPath(urls);
this.acc = AccessController.getContext();
- ucp = new URLClassPath(urls, acc);
}
URLClassLoader(URL[] urls, ClassLoader parent,
@@ -115,8 +115,8 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
if (security != null) {
security.checkCreateClassLoader();
}
+ ucp = new URLClassPath(urls);
this.acc = acc;
- ucp = new URLClassPath(urls, acc);
}
/**
@@ -147,8 +147,8 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
if (security != null) {
security.checkCreateClassLoader();
}
+ ucp = new URLClassPath(urls);
this.acc = AccessController.getContext();
- ucp = new URLClassPath(urls, acc);
}
URLClassLoader(URL[] urls, AccessControlContext acc) {
@@ -158,8 +158,8 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
if (security != null) {
security.checkCreateClassLoader();
}
+ ucp = new URLClassPath(urls);
this.acc = acc;
- ucp = new URLClassPath(urls, acc);
}
/**
@@ -180,7 +180,6 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
* @exception SecurityException if a security manager exists and its
* {@code checkCreateClassLoader} method doesn't allow
* creation of a class loader.
- * @exception NullPointerException if {@code urls} is {@code null}.
* @see SecurityManager#checkCreateClassLoader
*/
public URLClassLoader(URL[] urls, ClassLoader parent,
@@ -272,13 +271,13 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
* and errors are not caught. Calling close on an already closed
* loader has no effect.
* <p>
- * @exception IOException if closing any file opened by this class loader
+ * @throws IOException if closing any file opened by this class loader
* resulted in an IOException. Any such exceptions are caught internally.
* If only one is caught, then it is re-thrown. If more than one exception
* is caught, then the second and following exceptions are added
* as suppressed exceptions of the first one caught, which is then re-thrown.
*
- * @exception SecurityException if a security manager is set, and it denies
+ * @throws SecurityException if a security manager is set, and it denies
* {@link RuntimePermission}{@code ("closeClassLoader")}
*
* @since 1.7
@@ -456,16 +455,12 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
// Use (direct) ByteBuffer:
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
- // Android-removed: Android doesn't use sun.misc.PerfCounter.
- // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// must read certificates AFTER reading bytes.
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
- // Android-removed: Android doesn't use sun.misc.PerfCounter.
- // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, b, 0, b.length, cs);
}
}
@@ -771,7 +766,6 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
}
static {
- // Android-removed: SharedSecrets.setJavaNetAccess call. Android doesn't use it.
/*sun.misc.SharedSecrets.setJavaNetAccess (
new sun.misc.JavaNetAccess() {
public URLClassPath getURLClassPath (URLClassLoader u) {
diff --git a/java/security/AlgorithmParameters.java b/java/security/AlgorithmParameters.java
index 864866ef..36bb3ee0 100644
--- a/java/security/AlgorithmParameters.java
+++ b/java/security/AlgorithmParameters.java
@@ -29,8 +29,6 @@ import java.io.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
-import sun.security.jca.Providers;
-
/**
* This class is used as an opaque representation of cryptographic parameters.
*
@@ -287,8 +285,6 @@ public class AlgorithmParameters {
{
if (provider == null || provider.length() == 0)
throw new IllegalArgumentException("missing provider");
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "AlgorithmParameters", algorithm);
Object[] objs = Security.getImpl(algorithm, "AlgorithmParameters",
provider);
return new AlgorithmParameters((AlgorithmParametersSpi)objs[0],
@@ -334,8 +330,6 @@ public class AlgorithmParameters {
{
if (provider == null)
throw new IllegalArgumentException("missing provider");
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "AlgorithmParameters", algorithm);
Object[] objs = Security.getImpl(algorithm, "AlgorithmParameters",
provider);
return new AlgorithmParameters((AlgorithmParametersSpi)objs[0],
diff --git a/java/security/KeyFactory.java b/java/security/KeyFactory.java
index d01ce161..a6b912c5 100644
--- a/java/security/KeyFactory.java
+++ b/java/security/KeyFactory.java
@@ -231,8 +231,6 @@ public class KeyFactory {
*/
public static KeyFactory getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyFactory", algorithm);
Instance instance = GetInstance.getInstance("KeyFactory",
KeyFactorySpi.class, algorithm, provider);
return new KeyFactory((KeyFactorySpi)instance.impl,
@@ -270,8 +268,6 @@ public class KeyFactory {
*/
public static KeyFactory getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyFactory", algorithm);
Instance instance = GetInstance.getInstance("KeyFactory",
KeyFactorySpi.class, algorithm, provider);
return new KeyFactory((KeyFactorySpi)instance.impl,
diff --git a/java/security/KeyPairGenerator.java b/java/security/KeyPairGenerator.java
index 51a0ec93..68ab5e94 100644
--- a/java/security/KeyPairGenerator.java
+++ b/java/security/KeyPairGenerator.java
@@ -299,8 +299,6 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi {
public static KeyPairGenerator getInstance(String algorithm,
String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyPairGenerator", algorithm);
Instance instance = GetInstance.getInstance("KeyPairGenerator",
KeyPairGeneratorSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
@@ -337,8 +335,6 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi {
*/
public static KeyPairGenerator getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyPairGenerator", algorithm);
Instance instance = GetInstance.getInstance("KeyPairGenerator",
KeyPairGeneratorSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
diff --git a/java/security/MessageDigest.java b/java/security/MessageDigest.java
index ab2614a5..8e5dab14 100644
--- a/java/security/MessageDigest.java
+++ b/java/security/MessageDigest.java
@@ -35,8 +35,6 @@ import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
-import sun.security.jca.Providers;
-
/**
* This MessageDigest class provides applications the functionality of a
* message digest algorithm, such as SHA-1 or SHA-256.
@@ -257,8 +255,6 @@ public abstract class MessageDigest extends MessageDigestSpi {
{
if (provider == null || provider.length() == 0)
throw new IllegalArgumentException("missing provider");
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "MessageDigest", algorithm);
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest) {
MessageDigest md = (MessageDigest)objs[0];
@@ -307,8 +303,6 @@ public abstract class MessageDigest extends MessageDigestSpi {
{
if (provider == null)
throw new IllegalArgumentException("missing provider");
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "MessageDigest", algorithm);
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest) {
MessageDigest md = (MessageDigest)objs[0];
diff --git a/java/security/Signature.java b/java/security/Signature.java
index 9deaf564..5a0e6a87 100644
--- a/java/security/Signature.java
+++ b/java/security/Signature.java
@@ -498,8 +498,6 @@ public abstract class Signature extends SignatureSpi {
}
return getInstanceRSA(p);
}
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "Signature", algorithm);
Instance instance = GetInstance.getInstance
("Signature", SignatureSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
@@ -543,8 +541,6 @@ public abstract class Signature extends SignatureSpi {
}
return getInstanceRSA(provider);
}
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "Signature", algorithm);
Instance instance = GetInstance.getInstance
("Signature", SignatureSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
diff --git a/java/security/cert/CertificateFactory.java b/java/security/cert/CertificateFactory.java
index 5ccbb333..e74ff0a2 100644
--- a/java/security/cert/CertificateFactory.java
+++ b/java/security/cert/CertificateFactory.java
@@ -250,8 +250,6 @@ public class CertificateFactory {
String provider) throws CertificateException,
NoSuchProviderException {
try {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "CertificateFactory", type);
Instance instance = GetInstance.getInstance("CertificateFactory",
CertificateFactorySpi.class, type, provider);
return new CertificateFactory((CertificateFactorySpi)instance.impl,
@@ -293,8 +291,6 @@ public class CertificateFactory {
public static final CertificateFactory getInstance(String type,
Provider provider) throws CertificateException {
try {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "CertificateFactory", type);
Instance instance = GetInstance.getInstance("CertificateFactory",
CertificateFactorySpi.class, type, provider);
return new CertificateFactory((CertificateFactorySpi)instance.impl,
diff --git a/javax/crypto/KeyAgreement.java b/javax/crypto/KeyAgreement.java
index ce42de8a..ffa18b1c 100644
--- a/javax/crypto/KeyAgreement.java
+++ b/javax/crypto/KeyAgreement.java
@@ -252,8 +252,6 @@ public class KeyAgreement {
public static final KeyAgreement getInstance(String algorithm,
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyAgreement", algorithm);
Instance instance = JceSecurity.getInstance
("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
return new KeyAgreement((KeyAgreementSpi)instance.impl,
@@ -294,8 +292,6 @@ public class KeyAgreement {
*/
public static final KeyAgreement getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyAgreement", algorithm);
Instance instance = JceSecurity.getInstance
("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
return new KeyAgreement((KeyAgreementSpi)instance.impl,
diff --git a/javax/crypto/KeyGenerator.java b/javax/crypto/KeyGenerator.java
index b0977f0a..5dfde971 100644
--- a/javax/crypto/KeyGenerator.java
+++ b/javax/crypto/KeyGenerator.java
@@ -326,8 +326,6 @@ public class KeyGenerator {
public static final KeyGenerator getInstance(String algorithm,
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyGenerator", algorithm);
Instance instance = JceSecurity.getInstance("KeyGenerator",
KeyGeneratorSpi.class, algorithm, provider);
return new KeyGenerator((KeyGeneratorSpi)instance.impl,
@@ -366,8 +364,6 @@ public class KeyGenerator {
*/
public static final KeyGenerator getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "KeyGenerator", algorithm);
Instance instance = JceSecurity.getInstance("KeyGenerator",
KeyGeneratorSpi.class, algorithm, provider);
return new KeyGenerator((KeyGeneratorSpi)instance.impl,
diff --git a/javax/crypto/Mac.java b/javax/crypto/Mac.java
index dab6971c..c3d99fb3 100644
--- a/javax/crypto/Mac.java
+++ b/javax/crypto/Mac.java
@@ -309,8 +309,6 @@ public class Mac implements Cloneable {
*/
public static final Mac getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "Mac", algorithm);
Instance instance = JceSecurity.getInstance
("Mac", MacSpi.class, algorithm, provider);
return new Mac((MacSpi)instance.impl, instance.provider, algorithm);
@@ -346,8 +344,6 @@ public class Mac implements Cloneable {
*/
public static final Mac getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "Mac", algorithm);
Instance instance = JceSecurity.getInstance
("Mac", MacSpi.class, algorithm, provider);
return new Mac((MacSpi)instance.impl, instance.provider, algorithm);
diff --git a/javax/crypto/SecretKeyFactory.java b/javax/crypto/SecretKeyFactory.java
index be1cef47..c1358c20 100644
--- a/javax/crypto/SecretKeyFactory.java
+++ b/javax/crypto/SecretKeyFactory.java
@@ -385,8 +385,6 @@ public class SecretKeyFactory {
public static final SecretKeyFactory getInstance(String algorithm,
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "SecretKeyFactory", algorithm);
Instance instance = JceSecurity.getInstance("SecretKeyFactory",
SecretKeyFactorySpi.class, algorithm, provider);
return new SecretKeyFactory((SecretKeyFactorySpi)instance.impl,
@@ -427,8 +425,6 @@ public class SecretKeyFactory {
*/
public static final SecretKeyFactory getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
- // Android-added: Check for Bouncy Castle deprecation
- Providers.checkBouncyCastleDeprecation(provider, "SecretKeyFactory", algorithm);
Instance instance = JceSecurity.getInstance("SecretKeyFactory",
SecretKeyFactorySpi.class, algorithm, provider);
return new SecretKeyFactory((SecretKeyFactorySpi)instance.impl,