summaryrefslogtreecommitdiff
path: root/com/android
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-10-10 15:20:13 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-10-10 15:20:13 -0400
commit93b7ee4fce01df52a6607f0b1965cbafdfeaf1a6 (patch)
tree49f76f879a89c256a4f65b674086be50760bdffb /com/android
parentbc81c7ada5aab3806dd0b17498f5c9672c9b33c4 (diff)
downloadandroid-28-93b7ee4fce01df52a6607f0b1965cbafdfeaf1a6.tar.gz
Import Android SDK Platform P [4386628]
/google/data/ro/projects/android/fetch_artifact \ --bid 4386628 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4386628.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: I9b8400ac92116cae4f033d173f7a5682b26ccba9
Diffstat (limited to 'com/android')
-rw-r--r--com/android/commands/pm/Pm.java4
-rw-r--r--com/android/defcontainer/DefaultContainerService.java140
-rw-r--r--com/android/ims/ImsCallProfile.java55
-rw-r--r--com/android/internal/alsa/AlsaDevicesParser.java4
-rw-r--r--com/android/internal/app/ChooserActivity.java19
-rw-r--r--com/android/internal/app/NightDisplayController.java150
-rw-r--r--com/android/internal/app/ShutdownActivity.java7
-rw-r--r--com/android/internal/app/procstats/DumpUtils.java19
-rw-r--r--com/android/internal/app/procstats/ProcessState.java80
-rw-r--r--com/android/internal/app/procstats/ProcessStats.java42
-rw-r--r--com/android/internal/content/PackageHelper.java283
-rw-r--r--com/android/internal/os/BatteryStatsHelper.java3
-rw-r--r--com/android/internal/os/BatteryStatsImpl.java279
-rw-r--r--com/android/internal/os/Zygote.java5
-rw-r--r--com/android/internal/telephony/CallForwardInfo.java12
-rw-r--r--com/android/internal/telephony/CarrierKeyDownloadManager.java164
-rw-r--r--com/android/internal/telephony/CarrierServiceStateTracker.java9
-rw-r--r--com/android/internal/telephony/ClientWakelockTracker.java12
-rw-r--r--com/android/internal/telephony/Connection.java11
-rw-r--r--com/android/internal/telephony/DefaultPhoneNotifier.java3
-rw-r--r--com/android/internal/telephony/GsmCdmaPhone.java13
-rw-r--r--com/android/internal/telephony/InboundSmsHandler.java47
-rw-r--r--com/android/internal/telephony/Phone.java14
-rw-r--r--com/android/internal/telephony/RIL.java2
-rw-r--r--com/android/internal/telephony/ServiceStateTracker.java59
-rw-r--r--com/android/internal/telephony/cat/AppInterface.java1
-rw-r--r--com/android/internal/telephony/cat/CatService.java37
-rw-r--r--com/android/internal/telephony/cat/CommandParams.java9
-rw-r--r--com/android/internal/telephony/cat/CommandParamsFactory.java73
-rw-r--r--com/android/internal/telephony/cdma/SmsMessage.java2
-rw-r--r--com/android/internal/telephony/dataconnection/DcTracker.java25
-rw-r--r--com/android/internal/telephony/gsm/GsmSmsAddress.java8
-rw-r--r--com/android/internal/telephony/gsm/SmsMessage.java4
-rw-r--r--com/android/internal/telephony/imsphone/ImsPhone.java3
-rw-r--r--com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java9
-rw-r--r--com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java71
-rw-r--r--com/android/internal/telephony/uicc/AdnRecord.java15
-rw-r--r--com/android/internal/telephony/uicc/SIMRecords.java3
-rw-r--r--com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java107
-rw-r--r--com/android/internal/util/BitUtils.java4
-rw-r--r--com/android/internal/util/RingBuffer.java67
-rw-r--r--com/android/internal/widget/LinearLayoutManager.java4
-rw-r--r--com/android/internal/widget/LockPatternUtils.java19
-rw-r--r--com/android/internal/widget/Magnifier.java184
-rw-r--r--com/android/keyguard/KeyguardDisplayManager.java21
-rw-r--r--com/android/keyguard/KeyguardSimPinView.java6
-rw-r--r--com/android/keyguard/KeyguardSimPukView.java6
-rw-r--r--com/android/keyguard/KeyguardUpdateMonitor.java6
-rw-r--r--com/android/keyguard/ViewMediatorCallback.java5
-rw-r--r--com/android/layoutlib/bridge/Bridge.java655
-rw-r--r--com/android/layoutlib/bridge/android/BridgeContext.java79
-rw-r--r--com/android/layoutlib/bridge/bars/AppCompatActionBar.java103
-rw-r--r--com/android/layoutlib/bridge/impl/GcSnapshot.java69
-rw-r--r--com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--com/android/providers/settings/SettingsProvider.java20
-rw-r--r--com/android/server/BatteryService.java5
-rw-r--r--com/android/server/ConnectivityService.java60
-rw-r--r--com/android/server/DeviceIdleController.java204
-rw-r--r--com/android/server/DiskStatsService.java16
-rw-r--r--com/android/server/IpSecService.java160
-rw-r--r--com/android/server/LocationManagerService.java2
-rw-r--r--com/android/server/NetworkManagementService.java17
-rw-r--r--com/android/server/StorageManagerService.java1327
-rw-r--r--com/android/server/SystemServer.java87
-rw-r--r--com/android/server/accounts/AccountManagerService.java40
-rw-r--r--com/android/server/am/ActiveServices.java11
-rw-r--r--com/android/server/am/ActivityDisplay.java414
-rw-r--r--com/android/server/am/ActivityManagerService.java804
-rw-r--r--com/android/server/am/ActivityManagerShellCommand.java237
-rw-r--r--com/android/server/am/ActivityMetricsLogger.java6
-rw-r--r--com/android/server/am/ActivityRecord.java72
-rw-r--r--com/android/server/am/ActivityStack.java616
-rw-r--r--com/android/server/am/ActivityStackSupervisor.java1003
-rw-r--r--com/android/server/am/ActivityStarter.java354
-rw-r--r--com/android/server/am/AppErrors.java4
-rw-r--r--com/android/server/am/BatteryStatsService.java79
-rw-r--r--com/android/server/am/KeyguardController.java48
-rw-r--r--com/android/server/am/LockTaskController.java104
-rw-r--r--com/android/server/am/PendingIntentRecord.java2
-rw-r--r--com/android/server/am/PinnedActivityStack.java12
-rw-r--r--com/android/server/am/ProcessStatsService.java51
-rw-r--r--com/android/server/am/ServiceRecord.java5
-rw-r--r--com/android/server/am/TaskChangeNotificationController.java8
-rw-r--r--com/android/server/am/TaskPersister.java6
-rw-r--r--com/android/server/am/TaskRecord.java195
-rw-r--r--com/android/server/am/UserController.java1284
-rw-r--r--com/android/server/am/UserState.java4
-rw-r--r--com/android/server/appwidget/AppWidgetServiceImpl.java16
-rw-r--r--com/android/server/audio/AudioEventLogger.java18
-rw-r--r--com/android/server/audio/AudioService.java58
-rw-r--r--com/android/server/audio/AudioServiceEvents.java21
-rw-r--r--com/android/server/audio/MediaFocusControl.java39
-rw-r--r--com/android/server/audio/PlaybackActivityMonitor.java100
-rw-r--r--com/android/server/autofill/AutofillManagerService.java19
-rw-r--r--com/android/server/autofill/AutofillManagerServiceImpl.java55
-rw-r--r--com/android/server/autofill/Helper.java13
-rw-r--r--com/android/server/autofill/RemoteFillService.java5
-rw-r--r--com/android/server/autofill/Session.java311
-rw-r--r--com/android/server/autofill/ui/AutoFillUI.java66
-rw-r--r--com/android/server/autofill/ui/FillUi.java44
-rw-r--r--com/android/server/autofill/ui/SaveUi.java147
-rw-r--r--com/android/server/backup/BackupManagerConstants.java14
-rw-r--r--com/android/server/backup/BackupManagerService.java89
-rw-r--r--com/android/server/backup/BackupManagerServiceInterface.java2
-rw-r--r--com/android/server/backup/FullBackupJob.java4
-rw-r--r--com/android/server/backup/KeyValueAdbBackupEngine.java12
-rw-r--r--com/android/server/backup/KeyValueAdbRestoreEngine.java4
-rw-r--r--com/android/server/backup/KeyValueBackupJob.java4
-rw-r--r--com/android/server/backup/PackageManagerBackupAgent.java4
-rw-r--r--com/android/server/backup/ProcessedPackagesJournal.java6
-rw-r--r--com/android/server/backup/RefactoredBackupManagerService.java226
-rw-r--r--com/android/server/backup/Trampoline.java9
-rw-r--r--com/android/server/backup/TransportManager.java27
-rw-r--r--com/android/server/backup/fullbackup/PerformAdbBackupTask.java7
-rw-r--r--com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java17
-rw-r--r--com/android/server/backup/internal/PerformBackupTask.java15
-rw-r--r--com/android/server/backup/internal/RunInitializeReceiver.java22
-rw-r--r--com/android/server/backup/restore/PerformAdbRestoreTask.java3
-rw-r--r--com/android/server/backup/restore/PerformUnifiedRestoreTask.java13
-rw-r--r--com/android/server/backup/utils/AppBackupUtils.java29
-rw-r--r--com/android/server/connectivity/IpConnectivityEventBuilder.java2
-rw-r--r--com/android/server/connectivity/IpConnectivityMetrics.java49
-rw-r--r--com/android/server/connectivity/Nat464Xlat.java38
-rw-r--r--com/android/server/connectivity/NetdEventListenerService.java33
-rw-r--r--com/android/server/connectivity/tethering/OffloadController.java60
-rw-r--r--com/android/server/content/SyncManager.java47
-rw-r--r--com/android/server/content/SyncStorageEngine.java6
-rw-r--r--com/android/server/devicepolicy/DevicePolicyManagerService.java38
-rw-r--r--com/android/server/display/DisplayDeviceInfo.java6
-rw-r--r--com/android/server/display/LocalDisplayAdapter.java9
-rw-r--r--com/android/server/display/LogicalDisplay.java3
-rw-r--r--com/android/server/display/NightDisplayService.java101
-rw-r--r--com/android/server/display/VirtualDisplayAdapter.java5
-rw-r--r--com/android/server/fingerprint/FingerprintService.java163
-rw-r--r--com/android/server/fingerprint/InternalEnumerateClient.java9
-rw-r--r--com/android/server/job/JobSchedulerService.java44
-rw-r--r--com/android/server/job/JobSchedulerShellCommand.java44
-rw-r--r--com/android/server/locksettings/LockSettingsService.java18
-rw-r--r--com/android/server/locksettings/LockSettingsStrongAuth.java6
-rw-r--r--com/android/server/locksettings/SyntheticPasswordManager.java14
-rw-r--r--com/android/server/media/MediaRouterService.java10
-rw-r--r--com/android/server/media/MediaSessionRecord.java27
-rw-r--r--com/android/server/media/MediaSessionService.java4
-rw-r--r--com/android/server/net/NetworkPolicyManagerService.java32
-rw-r--r--com/android/server/notification/ConditionProviders.java5
-rw-r--r--com/android/server/notification/ImportanceExtractor.java2
-rw-r--r--com/android/server/notification/ManagedServices.java114
-rw-r--r--com/android/server/notification/NotificationAdjustmentExtractor.java3
-rw-r--r--com/android/server/notification/NotificationChannelExtractor.java2
-rw-r--r--com/android/server/notification/NotificationDelegate.java7
-rw-r--r--com/android/server/notification/NotificationManagerInternal.java2
-rw-r--r--com/android/server/notification/NotificationManagerService.java197
-rw-r--r--com/android/server/notification/NotificationRecord.java51
-rw-r--r--com/android/server/notification/PriorityExtractor.java2
-rw-r--r--com/android/server/notification/RankingConfig.java2
-rw-r--r--com/android/server/notification/RankingHelper.java75
-rw-r--r--com/android/server/notification/ScheduleCalendar.java3
-rw-r--r--com/android/server/notification/ZenModeHelper.java2
-rw-r--r--com/android/server/oemlock/OemLockService.java17
-rw-r--r--com/android/server/pm/BackgroundDexOptService.java6
-rw-r--r--com/android/server/pm/BasePermission.java105
-rw-r--r--com/android/server/pm/DefaultPermissionGrantPolicy.java1244
-rw-r--r--com/android/server/pm/DumpState.java95
-rw-r--r--com/android/server/pm/InstantAppRegistry.java7
-rw-r--r--com/android/server/pm/KeySetManagerService.java2
-rw-r--r--com/android/server/pm/PackageDexOptimizer.java52
-rw-r--r--com/android/server/pm/PackageInstallerService.java54
-rw-r--r--com/android/server/pm/PackageInstallerSession.java90
-rw-r--r--com/android/server/pm/PackageManagerService.java2606
-rw-r--r--com/android/server/pm/PackageManagerServiceCompilerMapping.java14
-rw-r--r--com/android/server/pm/PackageManagerServiceUtils.java77
-rw-r--r--com/android/server/pm/PackageManagerShellCommand.java4
-rw-r--r--com/android/server/pm/PackageSetting.java18
-rw-r--r--com/android/server/pm/PackageSettingBase.java9
-rw-r--r--com/android/server/pm/SettingBase.java2
-rw-r--r--com/android/server/pm/Settings.java238
-rw-r--r--com/android/server/pm/SharedUserSetting.java22
-rw-r--r--com/android/server/pm/ShortcutLauncher.java3
-rw-r--r--com/android/server/pm/ShortcutPackage.java7
-rw-r--r--com/android/server/pm/ShortcutService.java332
-rw-r--r--com/android/server/pm/ShortcutUser.java73
-rw-r--r--com/android/server/pm/UserManagerService.java16
-rw-r--r--com/android/server/pm/permission/BasePermission.java564
-rw-r--r--com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1296
-rw-r--r--com/android/server/pm/permission/PermissionManagerInternal.java140
-rw-r--r--com/android/server/pm/permission/PermissionManagerService.java1081
-rw-r--r--com/android/server/pm/permission/PermissionSettings.java149
-rw-r--r--com/android/server/pm/permission/PermissionsState.java (renamed from com/android/server/pm/PermissionsState.java)22
-rw-r--r--com/android/server/policy/PhoneWindowManager.java339
-rw-r--r--com/android/server/policy/WindowOrientationListener.java8
-rw-r--r--com/android/server/policy/keyguard/KeyguardServiceDelegate.java46
-rw-r--r--com/android/server/power/PowerManagerService.java34
-rw-r--r--com/android/server/power/ShutdownThread.java3
-rw-r--r--com/android/server/stats/StatsCompanionService.java301
-rw-r--r--com/android/server/statusbar/StatusBarManagerService.java139
-rw-r--r--com/android/server/storage/AppCollector.java2
-rw-r--r--com/android/server/storage/DiskStatsFileLogger.java14
-rw-r--r--com/android/server/timezone/IntentHelper.java21
-rw-r--r--com/android/server/timezone/IntentHelperImpl.java45
-rw-r--r--com/android/server/timezone/PackageTracker.java58
-rw-r--r--com/android/server/timezone/PackageTrackerHelperImpl.java8
-rw-r--r--com/android/server/timezone/RulesManagerService.java36
-rw-r--r--com/android/server/timezone/TimeZoneUpdateIdler.java100
-rw-r--r--com/android/server/twilight/TwilightState.java23
-rw-r--r--com/android/server/usage/AppStandbyController.java987
-rw-r--r--com/android/server/usage/UsageStatsService.java907
-rw-r--r--com/android/server/usb/UsbAlsaManager.java6
-rw-r--r--com/android/server/utils/ManagedApplicationService.java343
-rw-r--r--com/android/server/utils/PriorityDump.java14
-rw-r--r--com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java5
-rw-r--r--com/android/server/vr/Vr2dDisplay.java3
-rw-r--r--com/android/server/vr/VrManagerInternal.java7
-rw-r--r--com/android/server/vr/VrManagerService.java270
-rw-r--r--com/android/server/webkit/SystemImpl.java4
-rw-r--r--com/android/server/wifi/NetworkListStoreData.java21
-rw-r--r--com/android/server/wifi/OpenNetworkNotifier.java55
-rw-r--r--com/android/server/wifi/VelocityBasedConnectedScore.java133
-rw-r--r--com/android/server/wifi/WifiBackupRestore.java5
-rw-r--r--com/android/server/wifi/WifiConfigManager.java16
-rw-r--r--com/android/server/wifi/WifiConnectivityManager.java20
-rw-r--r--com/android/server/wifi/WifiCountryCode.java21
-rw-r--r--com/android/server/wifi/WifiInjector.java18
-rw-r--r--com/android/server/wifi/WifiMetrics.java127
-rw-r--r--com/android/server/wifi/WifiNative.java4
-rw-r--r--com/android/server/wifi/WifiScoreReport.java20
-rw-r--r--com/android/server/wifi/WifiServiceImpl.java2
-rw-r--r--com/android/server/wifi/WifiStateMachine.java135
-rw-r--r--com/android/server/wifi/WifiVendorHal.java27
-rw-r--r--com/android/server/wifi/WificondControl.java9
-rw-r--r--com/android/server/wifi/aware/WifiAwareClientState.java42
-rw-r--r--com/android/server/wifi/aware/WifiAwareRttStateManager.java206
-rw-r--r--com/android/server/wifi/aware/WifiAwareServiceImpl.java41
-rw-r--r--com/android/server/wifi/aware/WifiAwareStateManager.java124
-rw-r--r--com/android/server/wifi/rtt/RttNative.java248
-rw-r--r--com/android/server/wifi/rtt/RttService.java67
-rw-r--r--com/android/server/wifi/rtt/RttServiceImpl.java399
-rw-r--r--com/android/server/wifi/scanner/ChannelHelper.java10
-rw-r--r--com/android/server/wifi/scanner/HalWifiScannerImpl.java5
-rw-r--r--com/android/server/wifi/scanner/WifiScannerImpl.java6
-rw-r--r--com/android/server/wifi/scanner/WifiScanningServiceImpl.java12
-rw-r--r--com/android/server/wifi/scanner/WificondScannerImpl.java314
-rw-r--r--com/android/server/wifi/util/KalmanFilter.java62
-rw-r--r--com/android/server/wm/AppWindowAnimator.java40
-rw-r--r--com/android/server/wm/AppWindowToken.java11
-rw-r--r--com/android/server/wm/ConfigurationContainer.java83
-rw-r--r--com/android/server/wm/DisplayContent.java86
-rw-r--r--com/android/server/wm/DockedStackDividerController.java29
-rw-r--r--com/android/server/wm/DragResizeMode.java15
-rw-r--r--com/android/server/wm/PinnedStackController.java49
-rw-r--r--com/android/server/wm/PinnedStackWindowController.java14
-rw-r--r--com/android/server/wm/RootWindowContainer.java24
-rw-r--r--com/android/server/wm/Session.java4
-rw-r--r--com/android/server/wm/StackWindowController.java5
-rw-r--r--com/android/server/wm/Task.java29
-rw-r--r--com/android/server/wm/TaskSnapshotController.java4
-rw-r--r--com/android/server/wm/TaskStack.java40
-rw-r--r--com/android/server/wm/WindowContainer.java21
-rw-r--r--com/android/server/wm/WindowLayersController.java32
-rw-r--r--com/android/server/wm/WindowManagerService.java53
-rw-r--r--com/android/server/wm/WindowState.java35
-rw-r--r--com/android/server/wm/WindowSurfacePlacer.java19
-rw-r--r--com/android/server/wm/WindowToken.java7
-rw-r--r--com/android/settingslib/applications/ApplicationsState.java34
-rw-r--r--com/android/settingslib/applications/StorageStatsSource.java2
-rw-r--r--com/android/settingslib/development/AbstractEnableAdbPreferenceController.java18
-rw-r--r--com/android/settingslib/development/AbstractLogdSizePreferenceController.java10
-rw-r--r--com/android/settingslib/development/AbstractLogpersistPreferenceController.java7
-rw-r--r--com/android/settingslib/development/DeveloperOptionsPreferenceController.java76
-rw-r--r--com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java64
-rw-r--r--com/android/settingslib/drawer/TileUtils.java70
-rw-r--r--com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java5
-rw-r--r--com/android/settingslib/suggestions/SuggestionParser.java2
-rw-r--r--com/android/settingslib/wifi/WifiTracker.java104
-rw-r--r--com/android/settingslib/wifi/WifiTrackerFactory.java11
-rw-r--r--com/android/settingslib/wrapper/PackageManagerWrapper.java59
-rw-r--r--com/android/setupwizardlib/test/util/DrawingTestActivity.java9
-rw-r--r--com/android/setupwizardlib/util/LinkAccessibilityHelper.java394
-rw-r--r--com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java (renamed from com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java)95
-rw-r--r--com/android/setupwizardlib/util/PartnerTest.java28
-rw-r--r--com/android/setupwizardlib/view/IllustrationVideoViewTest.java2
-rw-r--r--com/android/setupwizardlib/view/NavigationBarButton.java127
-rw-r--r--com/android/setupwizardlib/view/RichTextView.java49
-rw-r--r--com/android/systemui/BatteryMeterView.java6
-rw-r--r--com/android/systemui/assist/AssistManager.java6
-rw-r--r--com/android/systemui/doze/AlwaysOnDisplayPolicy.java100
-rw-r--r--com/android/systemui/doze/DozePauser.java7
-rw-r--r--com/android/systemui/doze/DozeScreenBrightness.java12
-rw-r--r--com/android/systemui/globalactions/GlobalActionsDialog.java24
-rw-r--r--com/android/systemui/keyguard/KeyguardViewMediator.java65
-rw-r--r--com/android/systemui/media/NotificationPlayer.java74
-rw-r--r--com/android/systemui/pip/phone/PipManager.java27
-rw-r--r--com/android/systemui/pip/phone/PipMediaController.java2
-rw-r--r--com/android/systemui/pip/phone/PipMenuActivityController.java11
-rw-r--r--com/android/systemui/pip/phone/PipMotionHelper.java22
-rw-r--r--com/android/systemui/pip/phone/PipNotificationController.java107
-rw-r--r--com/android/systemui/pip/phone/PipUtils.java17
-rw-r--r--com/android/systemui/pip/tv/PipManager.java17
-rw-r--r--com/android/systemui/qs/AlphaControlledSignalTileView.java86
-rw-r--r--com/android/systemui/qs/SignalTileView.java6
-rw-r--r--com/android/systemui/qs/SlashDrawable.java6
-rw-r--r--com/android/systemui/qs/tileimpl/QSIconViewImpl.java12
-rw-r--r--com/android/systemui/qs/tileimpl/SlashImageView.java18
-rw-r--r--com/android/systemui/qs/tiles/BluetoothTile.java25
-rw-r--r--com/android/systemui/qs/tiles/CellularTile.java2
-rw-r--r--com/android/systemui/qs/tiles/WifiTile.java4
-rw-r--r--com/android/systemui/recents/Recents.java10
-rw-r--r--com/android/systemui/recents/RecentsActivity.java3
-rw-r--r--com/android/systemui/recents/RecentsImpl.java25
-rw-r--r--com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java14
-rw-r--r--com/android/systemui/recents/events/activity/LaunchTaskEvent.java17
-rw-r--r--com/android/systemui/recents/misc/SystemServicesProxy.java145
-rw-r--r--com/android/systemui/recents/model/HighResThumbnailLoader.java2
-rw-r--r--com/android/systemui/recents/model/RecentsTaskLoadPlan.java7
-rw-r--r--com/android/systemui/recents/model/Task.java38
-rw-r--r--com/android/systemui/recents/model/TaskKeyCache.java2
-rw-r--r--com/android/systemui/recents/model/TaskStack.java22
-rw-r--r--com/android/systemui/recents/views/RecentsTransitionHelper.java54
-rw-r--r--com/android/systemui/recents/views/RecentsView.java61
-rw-r--r--com/android/systemui/recents/views/RecentsViewTouchHandler.java47
-rw-r--r--com/android/systemui/recents/views/TaskStackView.java39
-rw-r--r--com/android/systemui/recents/views/TaskView.java5
-rw-r--r--com/android/systemui/recents/views/TaskViewHeader.java19
-rw-r--r--com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java82
-rw-r--r--com/android/systemui/stackdivider/DividerView.java41
-rw-r--r--com/android/systemui/stackdivider/WindowManagerProxy.java30
-rw-r--r--com/android/systemui/statusbar/ExpandableNotificationRow.java19
-rw-r--r--com/android/systemui/statusbar/ExpandableView.java14
-rw-r--r--com/android/systemui/statusbar/KeyboardShortcuts.java35
-rw-r--r--com/android/systemui/statusbar/NotificationData.java38
-rw-r--r--com/android/systemui/statusbar/NotificationSnooze.java118
-rw-r--r--com/android/systemui/statusbar/SignalClusterView.java33
-rw-r--r--com/android/systemui/statusbar/StatusBarIconView.java33
-rw-r--r--com/android/systemui/statusbar/car/CarNavigationBarController.java29
-rw-r--r--com/android/systemui/statusbar/car/CarStatusBar.java9
-rw-r--r--com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java17
-rw-r--r--com/android/systemui/statusbar/notification/NotificationViewWrapper.java10
-rw-r--r--com/android/systemui/statusbar/phone/BarTransitions.java2
-rw-r--r--com/android/systemui/statusbar/phone/DozeScrimController.java2
-rw-r--r--com/android/systemui/statusbar/phone/LightBarController.java8
-rw-r--r--com/android/systemui/statusbar/phone/NavigationBarFragment.java2
-rw-r--r--com/android/systemui/statusbar/phone/NotificationIconAreaController.java26
-rw-r--r--com/android/systemui/statusbar/phone/NotificationPanelView.java5
-rw-r--r--com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java33
-rw-r--r--com/android/systemui/statusbar/phone/StatusBar.java1177
-rw-r--r--com/android/systemui/statusbar/phone/StatusBarIconController.java16
-rw-r--r--com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java11
-rw-r--r--com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java1
-rw-r--r--com/android/systemui/statusbar/policy/AccessPointControllerImpl.java15
-rw-r--r--com/android/systemui/statusbar/policy/CallbackHandler.java6
-rw-r--r--com/android/systemui/statusbar/policy/NetworkController.java2
-rw-r--r--com/android/systemui/statusbar/policy/NetworkControllerImpl.java41
-rw-r--r--com/android/systemui/util/Utils.java53
-rw-r--r--com/android/systemui/volume/ZenModePanel.java7
-rw-r--r--com/android/uiautomator/testrunner/UiAutomatorTestCase.java106
-rw-r--r--com/android/webview/nullwebview/NullWebViewFactoryProvider.java85
355 files changed, 18176 insertions, 14024 deletions
diff --git a/com/android/commands/pm/Pm.java b/com/android/commands/pm/Pm.java
index ad989dee..c5c38f53 100644
--- a/com/android/commands/pm/Pm.java
+++ b/com/android/commands/pm/Pm.java
@@ -416,7 +416,7 @@ public final class Pm {
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
null, null);
params.sessionParams.setSize(
- PackageHelper.calculateInstalledSize(pkgLite, false,
+ PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
System.err.println("Error: Failed to parse APK file: " + e);
@@ -636,7 +636,7 @@ public final class Pm {
out = session.openWrite(splitName, 0, sizeBytes);
int total = 0;
- byte[] buffer = new byte[65536];
+ byte[] buffer = new byte[1024 * 1024];
int c;
while ((c = in.read(buffer)) != -1) {
total += c;
diff --git a/com/android/defcontainer/DefaultContainerService.java b/com/android/defcontainer/DefaultContainerService.java
index 3800e6f7..4a771ebd 100644
--- a/com/android/defcontainer/DefaultContainerService.java
+++ b/com/android/defcontainer/DefaultContainerService.java
@@ -16,8 +16,6 @@
package com.android.defcontainer;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
@@ -31,21 +29,15 @@ import android.content.pm.PackageParser.PackageParserException;
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.os.Binder;
-import android.os.Environment;
import android.os.Environment.UserEnvironment;
-import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructStatVfs;
import android.util.Slog;
import com.android.internal.app.IMediaContainerService;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
@@ -72,51 +64,6 @@ public class DefaultContainerService extends IntentService {
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
- * Creates a new container and copies package there.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- * @param containerId the id of the secure container that should be used
- * for creating a secure container into which the resource
- * will be copied.
- * @param key Refers to key used for encrypting the secure container
- * @return Returns the new cache path where the resource has been copied
- * into
- */
- @Override
- public String copyPackageToContainer(String packagePath, String containerId, String key,
- boolean isExternal, boolean isForwardLocked, String abiOverride) {
- if (packagePath == null || containerId == null) {
- return null;
- }
-
- if (isExternal) {
- // Make sure the sdcard is mounted.
- String status = Environment.getExternalStorageState();
- if (!status.equals(Environment.MEDIA_MOUNTED)) {
- Slog.w(TAG, "Make sure sdcard is mounted.");
- return null;
- }
- }
-
- PackageLite pkg = null;
- NativeLibraryHelper.Handle handle = null;
- try {
- final File packageFile = new File(packagePath);
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- handle = NativeLibraryHelper.Handle.create(pkg);
- return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal,
- isForwardLocked, abiOverride);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to copy package at " + packagePath, e);
- return null;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
- /**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
@@ -153,7 +100,6 @@ public class DefaultContainerService extends IntentService {
public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
String abiOverride) {
final Context context = DefaultContainerService.this;
- final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
@@ -167,7 +113,7 @@ public class DefaultContainerService extends IntentService {
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
+ sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
@@ -230,13 +176,13 @@ public class DefaultContainerService extends IntentService {
* containing one or more APKs.
*/
@Override
- public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
- String abiOverride) throws RemoteException {
+ public long calculateInstalledSize(String packagePath, String abiOverride)
+ throws RemoteException {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
+ return PackageHelper.calculateInstalledSize(pkg, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
@@ -292,60 +238,6 @@ public class DefaultContainerService extends IntentService {
return mBinder;
}
- private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
- String newCid, String key, boolean isExternal, boolean isForwardLocked,
- String abiOverride) throws IOException {
-
- // Calculate container size, rounding up to nearest MB and adding an
- // extra MB for filesystem overhead
- final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle,
- isForwardLocked, abiOverride);
-
- // Create new container
- final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key,
- Process.myUid(), isExternal);
- if (newMountPath == null) {
- throw new IOException("Failed to create container " + newCid);
- }
- final File targetDir = new File(newMountPath);
-
- try {
- // Copy all APKs
- copyFile(pkg.baseCodePath, targetDir, "base.apk", isForwardLocked);
- if (!ArrayUtils.isEmpty(pkg.splitNames)) {
- for (int i = 0; i < pkg.splitNames.length; i++) {
- copyFile(pkg.splitCodePaths[i], targetDir,
- "split_" + pkg.splitNames[i] + ".apk", isForwardLocked);
- }
- }
-
- // Extract native code
- final File libraryRoot = new File(targetDir, LIB_DIR_NAME);
- final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- abiOverride);
- if (res != PackageManager.INSTALL_SUCCEEDED) {
- throw new IOException("Failed to extract native code, res=" + res);
- }
-
- if (!PackageHelper.finalizeSdDir(newCid)) {
- throw new IOException("Failed to finalize " + newCid);
- }
-
- if (PackageHelper.isContainerMounted(newCid)) {
- PackageHelper.unMountSdDir(newCid);
- }
-
- } catch (ErrnoException e) {
- PackageHelper.destroySdDir(newCid);
- throw e.rethrowAsIOException();
- } catch (IOException e) {
- PackageHelper.destroySdDir(newCid);
- throw e;
- }
-
- return newMountPath;
- }
-
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
copyFile(pkg.baseCodePath, target, "base.apk");
@@ -373,28 +265,4 @@ public class DefaultContainerService extends IntentService {
IoUtils.closeQuietly(in);
}
}
-
- private void copyFile(String sourcePath, File targetDir, String targetName,
- boolean isForwardLocked) throws IOException, ErrnoException {
- final File sourceFile = new File(sourcePath);
- final File targetFile = new File(targetDir, targetName);
-
- Slog.d(TAG, "Copying " + sourceFile + " to " + targetFile);
- if (!FileUtils.copyFile(sourceFile, targetFile)) {
- throw new IOException("Failed to copy " + sourceFile + " to " + targetFile);
- }
-
- if (isForwardLocked) {
- final String publicTargetName = PackageHelper.replaceEnd(targetName,
- ".apk", ".zip");
- final File publicTargetFile = new File(targetDir, publicTargetName);
-
- PackageHelper.extractPublicFiles(sourceFile, publicTargetFile);
-
- Os.chmod(targetFile.getAbsolutePath(), 0640);
- Os.chmod(publicTargetFile.getAbsolutePath(), 0644);
- } else {
- Os.chmod(targetFile.getAbsolutePath(), 0644);
- }
- }
}
diff --git a/com/android/ims/ImsCallProfile.java b/com/android/ims/ImsCallProfile.java
index 36abfc9d..489c208a 100644
--- a/com/android/ims/ImsCallProfile.java
+++ b/com/android/ims/ImsCallProfile.java
@@ -19,7 +19,9 @@ package com.android.ims;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.telecom.VideoProfile;
+import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
@@ -216,6 +218,29 @@ public class ImsCallProfile implements Parcelable {
public int mServiceType;
public int mCallType;
public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+
+ /**
+ * Extras associated with this {@link ImsCallProfile}.
+ * <p>
+ * Valid data types include:
+ * <ul>
+ * <li>{@link Integer} (and int)</li>
+ * <li>{@link Long} (and long)</li>
+ * <li>{@link Double} (and double)</li>
+ * <li>{@link String}</li>
+ * <li>{@code int[]}</li>
+ * <li>{@code long[]}</li>
+ * <li>{@code double[]}</li>
+ * <li>{@code String[]}</li>
+ * <li>{@link PersistableBundle}</li>
+ * <li>{@link Boolean} (and boolean)</li>
+ * <li>{@code boolean[]}</li>
+ * <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
+ * </ul>
+ * <p>
+ * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
+ * a {@link android.os.Binder}.
+ */
public Bundle mCallExtras;
public ImsStreamMediaProfile mMediaProfile;
@@ -315,16 +340,17 @@ public class ImsCallProfile implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ Bundle filteredExtras = maybeCleanseExtras(mCallExtras);
out.writeInt(mServiceType);
out.writeInt(mCallType);
- out.writeParcelable(mCallExtras, 0);
+ out.writeBundle(filteredExtras);
out.writeParcelable(mMediaProfile, 0);
}
private void readFromParcel(Parcel in) {
mServiceType = in.readInt();
mCallType = in.readInt();
- mCallExtras = in.readParcelable(null);
+ mCallExtras = in.readBundle();
mMediaProfile = in.readParcelable(null);
}
@@ -465,6 +491,31 @@ public class ImsCallProfile implements Parcelable {
}
/**
+ * Cleanses a {@link Bundle} to ensure that it contains only data of type:
+ * 1. Primitive data types (e.g. int, bool, and other values determined by
+ * {@link android.os.PersistableBundle#isValidType(Object)}).
+ * 2. Other Bundles.
+ * 3. {@link Parcelable} objects in the {@code android.*} namespace.
+ * @param extras the source {@link Bundle}
+ * @return where all elements are valid types the source {@link Bundle} is returned unmodified,
+ * otherwise a copy of the {@link Bundle} with the invalid elements is returned.
+ */
+ private Bundle maybeCleanseExtras(Bundle extras) {
+ if (extras == null) {
+ return null;
+ }
+
+ int startSize = extras.size();
+ Bundle filtered = extras.filterValues();
+ int endSize = filtered.size();
+ if (startSize != endSize) {
+ Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were "
+ + "removed - only primitive types and system parcelables are permitted.");
+ }
+ return filtered;
+ }
+
+ /**
* Determines if a video state is set in a video state bit-mask.
*
* @param videoState The video state bit mask.
diff --git a/com/android/internal/alsa/AlsaDevicesParser.java b/com/android/internal/alsa/AlsaDevicesParser.java
index 7cdd8970..6e3d5966 100644
--- a/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/com/android/internal/alsa/AlsaDevicesParser.java
@@ -258,7 +258,7 @@ public class AlsaDevicesParser {
return line.charAt(kIndex_CardDeviceField) == '[';
}
- public void scan() {
+ public boolean scan() {
mDeviceRecords.clear();
File devicesFile = new File(kDevicesFilePath);
@@ -274,11 +274,13 @@ public class AlsaDevicesParser {
}
}
reader.close();
+ return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
+ return false;
}
//
diff --git a/com/android/internal/app/ChooserActivity.java b/com/android/internal/app/ChooserActivity.java
index 2cab009f..6e0ba341 100644
--- a/com/android/internal/app/ChooserActivity.java
+++ b/com/android/internal/app/ChooserActivity.java
@@ -100,7 +100,7 @@ public class ChooserActivity extends ResolverActivity {
private static final boolean DEBUG = false;
private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
- private static final int WATCHDOG_TIMEOUT_MILLIS = 5000;
+ private static final int WATCHDOG_TIMEOUT_MILLIS = 2000;
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
@@ -1450,11 +1450,16 @@ public class ChooserActivity extends ResolverActivity {
getFirstRowPosition(rowPosition + 1));
int serviceSpacing = holder.row.getContext().getResources()
.getDimensionPixelSize(R.dimen.chooser_service_spacing);
- int top = rowPosition == 0 ? serviceSpacing : 0;
- if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
- setVertPadding(holder, top, serviceSpacing);
+ if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ // if the row is the only row for target service
+ setVertPadding(holder, 0, 0);
} else {
- setVertPadding(holder, top, 0);
+ int top = rowPosition == 0 ? serviceSpacing : 0;
+ if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ setVertPadding(holder, top, serviceSpacing);
+ } else {
+ setVertPadding(holder, top, 0);
+ }
}
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
@@ -1580,8 +1585,8 @@ public class ChooserActivity extends ResolverActivity {
} catch (RemoteException e) {
Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
mChooserActivity.unbindService(this);
- destroy();
mChooserActivity.mServiceConnections.remove(this);
+ destroy();
}
}
}
@@ -1597,7 +1602,6 @@ public class ChooserActivity extends ResolverActivity {
}
mChooserActivity.unbindService(this);
- destroy();
mChooserActivity.mServiceConnections.remove(this);
if (mChooserActivity.mServiceConnections.isEmpty()) {
mChooserActivity.mChooserHandler.removeMessages(
@@ -1605,6 +1609,7 @@ public class ChooserActivity extends ResolverActivity {
mChooserActivity.sendVoiceChoicesIfNeeded();
}
mConnectedComponent = null;
+ destroy();
}
}
diff --git a/com/android/internal/app/NightDisplayController.java b/com/android/internal/app/NightDisplayController.java
index 860c5c4c..7a1383c7 100644
--- a/com/android/internal/app/NightDisplayController.java
+++ b/com/android/internal/app/NightDisplayController.java
@@ -32,8 +32,12 @@ import com.android.internal.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Calendar;
-import java.util.Locale;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
/**
* Controller for managing Night display settings.
@@ -116,8 +120,9 @@ public final class NightDisplayController {
*/
public boolean setActivated(boolean activated) {
if (isActivated() != activated) {
- Secure.putLongForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
+ Secure.putStringForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().toString(),
mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
@@ -128,17 +133,22 @@ public final class NightDisplayController {
* Returns the time when Night display's activation state last changed, or {@code null} if it
* has never been changed.
*/
- public Calendar getLastActivatedTime() {
+ public LocalDateTime getLastActivatedTime() {
final ContentResolver cr = mContext.getContentResolver();
- final long lastActivatedTimeMillis = Secure.getLongForUser(
- cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId);
- if (lastActivatedTimeMillis < 0) {
- return null;
+ final String lastActivatedTime = Secure.getStringForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
+ if (lastActivatedTime != null) {
+ try {
+ return LocalDateTime.parse(lastActivatedTime);
+ } catch (DateTimeParseException ignored) {}
+ // Uses the old epoch time.
+ try {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+ ZoneId.systemDefault());
+ } catch (DateTimeException|NumberFormatException ignored) {}
}
-
- final Calendar lastActivatedTime = Calendar.getInstance();
- lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
- return lastActivatedTime;
+ return null;
}
/**
@@ -183,8 +193,10 @@ public final class NightDisplayController {
}
if (getAutoMode() != autoMode) {
- Secure.putLongForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId);
+ Secure.putStringForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ null,
+ mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
@@ -206,7 +218,7 @@ public final class NightDisplayController {
R.integer.config_defaultNightDisplayCustomStartTime);
}
- return LocalTime.valueOf(startTimeValue);
+ return LocalTime.ofSecondOfDay(startTimeValue / 1000);
}
/**
@@ -221,7 +233,7 @@ public final class NightDisplayController {
throw new IllegalArgumentException("startTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -240,7 +252,7 @@ public final class NightDisplayController {
R.integer.config_defaultNightDisplayCustomEndTime);
}
- return LocalTime.valueOf(endTimeValue);
+ return LocalTime.ofSecondOfDay(endTimeValue / 1000);
}
/**
@@ -255,7 +267,7 @@ public final class NightDisplayController {
throw new IllegalArgumentException("endTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -379,106 +391,6 @@ public final class NightDisplayController {
}
/**
- * A time without a time-zone or date.
- */
- public static class LocalTime {
-
- /**
- * The hour of the day from 0 - 23.
- */
- public final int hourOfDay;
- /**
- * The minute within the hour from 0 - 59.
- */
- public final int minute;
-
- public LocalTime(int hourOfDay, int minute) {
- if (hourOfDay < 0 || hourOfDay > 23) {
- throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
- } else if (minute < 0 || minute > 59) {
- throw new IllegalArgumentException("Invalid minute: " + minute);
- }
-
- this.hourOfDay = hourOfDay;
- this.minute = minute;
- }
-
- /**
- * Returns the first date time corresponding to this local time that occurs before the
- * provided date time.
- *
- * @param time the date time to compare against
- * @return the prior date time corresponding to this local time
- */
- public Calendar getDateTimeBefore(Calendar time) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, time.get(Calendar.YEAR));
- c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
- c.set(Calendar.HOUR_OF_DAY, hourOfDay);
- c.set(Calendar.MINUTE, minute);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
-
- // Check if the local time has past, if so return the same time tomorrow.
- if (c.after(time)) {
- c.add(Calendar.DATE, -1);
- }
-
- return c;
- }
-
- /**
- * Returns the first date time corresponding to this local time that occurs after the
- * provided date time.
- *
- * @param time the date time to compare against
- * @return the next date time corresponding to this local time
- */
- public Calendar getDateTimeAfter(Calendar time) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, time.get(Calendar.YEAR));
- c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
- c.set(Calendar.HOUR_OF_DAY, hourOfDay);
- c.set(Calendar.MINUTE, minute);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
-
- // Check if the local time has past, if so return the same time tomorrow.
- if (c.before(time)) {
- c.add(Calendar.DATE, 1);
- }
-
- return c;
- }
-
- /**
- * Returns a local time corresponding the given number of milliseconds from midnight.
- *
- * @param millis the number of milliseconds from midnight
- * @return the corresponding local time
- */
- private static LocalTime valueOf(int millis) {
- final int hourOfDay = (millis / 3600000) % 24;
- final int minutes = (millis / 60000) % 60;
- return new LocalTime(hourOfDay, minutes);
- }
-
- /**
- * Returns the local time represented as milliseconds from midnight.
- */
- private int toMillis() {
- return hourOfDay * 3600000 + minute * 60000;
- }
-
- @Override
- public String toString() {
- return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
- }
- }
-
- /**
* Callback invoked whenever the Night display settings are changed.
*/
public interface Callback {
diff --git a/com/android/internal/app/ShutdownActivity.java b/com/android/internal/app/ShutdownActivity.java
index 745d28f3..f81e8383 100644
--- a/com/android/internal/app/ShutdownActivity.java
+++ b/com/android/internal/app/ShutdownActivity.java
@@ -41,6 +41,9 @@ public class ShutdownActivity extends Activity {
mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false);
+ final String reason = mUserRequested
+ ? PowerManager.SHUTDOWN_USER_REQUESTED
+ : intent.getStringExtra(Intent.EXTRA_REASON);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
Thread thr = new Thread("ShutdownActivity") {
@@ -52,9 +55,7 @@ public class ShutdownActivity extends Activity {
if (mReboot) {
pm.reboot(mConfirm, null, false);
} else {
- pm.shutdown(mConfirm,
- mUserRequested ? PowerManager.SHUTDOWN_USER_REQUESTED : null,
- false);
+ pm.shutdown(mConfirm, reason, false);
}
} catch (RemoteException e) {
}
diff --git a/com/android/internal/app/procstats/DumpUtils.java b/com/android/internal/app/procstats/DumpUtils.java
index ebedc89c..0bc8c483 100644
--- a/com/android/internal/app/procstats/DumpUtils.java
+++ b/com/android/internal/app/procstats/DumpUtils.java
@@ -29,6 +29,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import static com.android.internal.app.procstats.ProcessStats.*;
@@ -66,6 +67,8 @@ public final class DumpUtils {
"cch-activity", "cch-aclient", "cch-empty"
};
+ // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto
+ // Update states must sync enum definition as well, the ordering must not be changed.
static final String[] ADJ_SCREEN_TAGS = new String[] {
"0", "1"
};
@@ -177,6 +180,13 @@ public final class DumpUtils {
printArrayEntry(pw, STATE_TAGS, state, 1);
}
+ public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId,
+ long stateId, int state) {
+ state = printProto(proto, screenId, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD * STATE_COUNT);
+ state = printProto(proto, memId, ADJ_MEM_TAGS, state, STATE_COUNT);
+ printProto(proto, stateId, STATE_TAGS, state, 1);
+ }
+
public static void printAdjTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
@@ -352,6 +362,15 @@ public final class DumpUtils {
return value - index*mod;
}
+ public static int printProto(ProtoOutputStream proto, long fieldId, String[] array, int value, int mod) {
+ int index = value/mod;
+ if (index >= 0 && index < array.length) {
+ // Valid state enum number starts at 1, 0 stands for unknown.
+ proto.write(fieldId, index + 1);
+ } // else enum default is always zero in proto3
+ return value - index*mod;
+ }
+
public static String collapseString(String pkgName, String itemName) {
if (itemName.startsWith(pkgName)) {
final int ITEMLEN = itemName.length();
diff --git a/com/android/internal/app/procstats/ProcessState.java b/com/android/internal/app/procstats/ProcessState.java
index e0a40536..7519fce4 100644
--- a/com/android/internal/app/procstats/ProcessState.java
+++ b/com/android/internal/app/procstats/ProcessState.java
@@ -21,6 +21,8 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.pm.PackageProto;
+import android.service.procstats.ProcessStatsProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -29,6 +31,8 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.app.procstats.ProcessStats.PackageState;
@@ -69,6 +73,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
public final class ProcessState {
@@ -1157,6 +1164,7 @@ public final class ProcessState {
}
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
@@ -1167,4 +1175,76 @@ public final class ProcessState {
sb.append("}");
return sb.toString();
}
+
+ public void toProto(ProtoOutputStream proto, String procName, int uid, long now) {
+ proto.write(ProcessStatsProto.PROCESS, procName);
+ proto.write(ProcessStatsProto.UID, uid);
+ if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) {
+ final long killToken = proto.start(ProcessStatsProto.KILL);
+ proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu);
+ proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS,
+ mMinCachedKillPss, mAvgCachedKillPss, mMaxCachedKillPss);
+ proto.end(killToken);
+ }
+
+ // Group proc stats by type (screen state + mem state + process state)
+ Map<Integer, Long> durationByState = new HashMap<>();
+ boolean didCurState = false;
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ long time = mDurations.getValue(key);
+ if (mCurState == type) {
+ didCurState = true;
+ time += now - mStartTime;
+ }
+ durationByState.put(type, time);
+ }
+ if (!didCurState && mCurState != STATE_NOTHING) {
+ durationByState.put(mCurState, now - mStartTime);
+ }
+
+ for (int i=0; i<mPssTable.getKeyCount(); i++) {
+ final int key = mPssTable.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ if (!durationByState.containsKey(type)) {
+ // state without duration should not have stats!
+ continue;
+ }
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ type);
+
+ long duration = durationByState.get(type);
+ durationByState.remove(type); // remove the key since it is already being dumped.
+ proto.write(ProcessStatsProto.State.DURATION_MS, duration);
+
+ proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
+ mPssTable.getValue(key, PSS_MINIMUM),
+ mPssTable.getValue(key, PSS_AVERAGE),
+ mPssTable.getValue(key, PSS_MAXIMUM));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
+ mPssTable.getValue(key, PSS_USS_MINIMUM),
+ mPssTable.getValue(key, PSS_USS_AVERAGE),
+ mPssTable.getValue(key, PSS_USS_MAXIMUM));
+
+ proto.end(stateToken);
+ }
+
+ for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) {
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ entry.getKey());
+ proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue());
+ proto.end(stateToken);
+ }
+ }
}
diff --git a/com/android/internal/app/procstats/ProcessStats.java b/com/android/internal/app/procstats/ProcessStats.java
index 35b53c22..14f5e5b5 100644
--- a/com/android/internal/app/procstats/ProcessStats.java
+++ b/com/android/internal/app/procstats/ProcessStats.java
@@ -22,6 +22,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.procstats.ProcessStatsSectionProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -30,6 +31,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.DurationsTable;
@@ -1706,6 +1708,46 @@ public final class ProcessStats implements Parcelable {
}
}
+ public void toProto(ProtoOutputStream proto, long now) {
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+
+ proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
+ proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
+ mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
+ proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
+ proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
+ proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
+ boolean partial = true;
+ if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
+ partial = false;
+ }
+ if ((mFlags&FLAG_SYSPROPS) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
+ partial = false;
+ }
+ if ((mFlags&FLAG_COMPLETE) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
+ partial = false;
+ }
+ if (partial) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ final long processStateToken = proto.start(ProcessStatsSectionProto.PROCESS_STATS);
+ procState.toProto(proto, procName, uid, now);
+ proto.end(processStateToken);
+ }
+ }
+ }
final public static class ProcessStateHolder {
public final int appVersion;
diff --git a/com/android/internal/content/PackageHelper.java b/com/android/internal/content/PackageHelper.java
index e923223d..59a7995a 100644
--- a/com/android/internal/content/PackageHelper.java
+++ b/com/android/internal/content/PackageHelper.java
@@ -16,7 +16,6 @@
package com.android.internal.content;
-import static android.net.TrafficStats.MB_IN_BYTES;
import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
import android.content.Context;
@@ -27,13 +26,11 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
@@ -45,15 +42,9 @@ import com.android.internal.annotations.VisibleForTesting;
import libcore.io.IoUtils;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
/**
* Constants used internally between the PackageManager
@@ -72,7 +63,6 @@ public class PackageHelper {
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
- private static final boolean localLOGV = false;
private static final String TAG = "PackageHelper";
// App installation location settings values
public static final int APP_INSTALL_AUTO = 0;
@@ -91,259 +81,6 @@ public class PackageHelper {
}
}
- public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid,
- boolean isExternal) {
- // Round up to nearest MB, plus another MB for filesystem overhead
- final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
- try {
- IStorageManager storageManager = getStorageManager();
-
- if (localLOGV)
- Log.i(TAG, "Size of container " + sizeMb + " MB");
-
- int rc = storageManager.createSecureContainer(cid, sizeMb, "ext4", sdEncKey, uid,
- isExternal);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to create secure container " + cid);
- return null;
- }
- String cachePath = storageManager.getSecureContainerPath(cid);
- if (localLOGV) Log.i(TAG, "Created secure container " + cid +
- " at " + cachePath);
- return cachePath;
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return null;
- }
-
- public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) {
- // Round up to nearest MB, plus another MB for filesystem overhead
- final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
- try {
- IStorageManager storageManager = getStorageManager();
- int rc = storageManager.resizeSecureContainer(cid, sizeMb, sdEncKey);
- if (rc == StorageResultCode.OperationSucceeded) {
- return true;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- Log.e(TAG, "Failed to create secure container " + cid);
- return false;
- }
-
- public static String mountSdDir(String cid, String key, int ownerUid) {
- return mountSdDir(cid, key, ownerUid, true);
- }
-
- public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) {
- try {
- int rc = getStorageManager().mountSecureContainer(cid, key, ownerUid, readOnly);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
- return null;
- }
- return getStorageManager().getSecureContainerPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return null;
- }
-
- public static boolean unMountSdDir(String cid) {
- try {
- int rc = getStorageManager().unmountSecureContainer(cid, true);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return false;
- }
-
- public static boolean renameSdDir(String oldId, String newId) {
- try {
- int rc = getStorageManager().renameSecureContainer(oldId, newId);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to rename " + oldId + " to " +
- newId + "with rc " + rc);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.i(TAG, "Failed ot rename " + oldId + " to " + newId +
- " with exception : " + e);
- }
- return false;
- }
-
- public static String getSdDir(String cid) {
- try {
- return getStorageManager().getSecureContainerPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get container path for " + cid +
- " with exception " + e);
- }
- return null;
- }
-
- public static String getSdFilesystem(String cid) {
- try {
- return getStorageManager().getSecureContainerFilesystemPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get container path for " + cid +
- " with exception " + e);
- }
- return null;
- }
-
- public static boolean finalizeSdDir(String cid) {
- try {
- int rc = getStorageManager().finalizeSecureContainer(cid);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to finalize container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to finalize container " + cid +
- " with exception " + e);
- }
- return false;
- }
-
- public static boolean destroySdDir(String cid) {
- try {
- if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid);
- int rc = getStorageManager().destroySecureContainer(cid, true);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to destroy container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy container " + cid +
- " with exception " + e);
- }
- return false;
- }
-
- public static String[] getSecureContainerList() {
- try {
- return getStorageManager().getSecureContainerList();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get secure container list with exception" +
- e);
- }
- return null;
- }
-
- public static boolean isContainerMounted(String cid) {
- try {
- return getStorageManager().isSecureContainerMounted(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to find out if container " + cid + " mounted");
- }
- return false;
- }
-
- /**
- * Extract public files for the single given APK.
- */
- public static long extractPublicFiles(File apkFile, File publicZipFile)
- throws IOException {
- final FileOutputStream fstr;
- final ZipOutputStream publicZipOutStream;
-
- if (publicZipFile == null) {
- fstr = null;
- publicZipOutStream = null;
- } else {
- fstr = new FileOutputStream(publicZipFile);
- publicZipOutStream = new ZipOutputStream(fstr);
- Log.d(TAG, "Extracting " + apkFile + " to " + publicZipFile);
- }
-
- long size = 0L;
-
- try {
- final ZipFile privateZip = new ZipFile(apkFile.getAbsolutePath());
- try {
- // Copy manifest, resources.arsc and res directory to public zip
- for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) {
- final String zipEntryName = zipEntry.getName();
- if ("AndroidManifest.xml".equals(zipEntryName)
- || "resources.arsc".equals(zipEntryName)
- || zipEntryName.startsWith("res/")) {
- size += zipEntry.getSize();
- if (publicZipFile != null) {
- copyZipEntry(zipEntry, privateZip, publicZipOutStream);
- }
- }
- }
- } finally {
- try { privateZip.close(); } catch (IOException e) {}
- }
-
- if (publicZipFile != null) {
- publicZipOutStream.finish();
- publicZipOutStream.flush();
- FileUtils.sync(fstr);
- publicZipOutStream.close();
- FileUtils.setPermissions(publicZipFile.getAbsolutePath(), FileUtils.S_IRUSR
- | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1);
- }
- } finally {
- IoUtils.closeQuietly(publicZipOutStream);
- }
-
- return size;
- }
-
- private static void copyZipEntry(ZipEntry zipEntry, ZipFile inZipFile,
- ZipOutputStream outZipStream) throws IOException {
- byte[] buffer = new byte[4096];
- int num;
-
- ZipEntry newEntry;
- if (zipEntry.getMethod() == ZipEntry.STORED) {
- // Preserve the STORED method of the input entry.
- newEntry = new ZipEntry(zipEntry);
- } else {
- // Create a new entry so that the compressed len is recomputed.
- newEntry = new ZipEntry(zipEntry.getName());
- }
- outZipStream.putNextEntry(newEntry);
-
- final InputStream data = inZipFile.getInputStream(zipEntry);
- try {
- while ((num = data.read(buffer)) > 0) {
- outZipStream.write(buffer, 0, num);
- }
- outZipStream.flush();
- } finally {
- IoUtils.closeQuietly(data);
- }
- }
-
- public static boolean fixSdPermissions(String cid, int gid, String filename) {
- try {
- int rc = getStorageManager().fixPermissionsSecureContainer(cid, gid, filename);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to fixperms container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to fixperms container " + cid + " with exception " + e);
- }
- return false;
- }
-
/**
* A group of external dependencies used in
* {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
@@ -638,29 +375,37 @@ public class PackageHelper {
return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
+ @Deprecated
public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
String abiOverride) throws IOException {
+ return calculateInstalledSize(pkg, abiOverride);
+ }
+
+ public static long calculateInstalledSize(PackageLite pkg, String abiOverride)
+ throws IOException {
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(pkg);
- return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride);
+ return calculateInstalledSize(pkg, handle, abiOverride);
} finally {
IoUtils.closeQuietly(handle);
}
}
+ @Deprecated
+ public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
+ NativeLibraryHelper.Handle handle, String abiOverride) throws IOException {
+ return calculateInstalledSize(pkg, handle, abiOverride);
+ }
+
public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
- boolean isForwardLocked, String abiOverride) throws IOException {
+ String abiOverride) throws IOException {
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
-
- if (isForwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
- }
}
// Include all relevant native code
diff --git a/com/android/internal/os/BatteryStatsHelper.java b/com/android/internal/os/BatteryStatsHelper.java
index f085e290..15dc6f50 100644
--- a/com/android/internal/os/BatteryStatsHelper.java
+++ b/com/android/internal/os/BatteryStatsHelper.java
@@ -143,6 +143,9 @@ public class BatteryStatsHelper {
public static boolean checkWifiOnly(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
+ if (cm == null) {
+ return false;
+ }
return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
}
diff --git a/com/android/internal/os/BatteryStatsImpl.java b/com/android/internal/os/BatteryStatsImpl.java
index 0bd29815..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 = 166 + (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;
@@ -341,8 +341,8 @@ public class BatteryStatsImpl extends BatteryStats {
protected final TimeBase mOnBatteryTimeBase = new TimeBase();
// These are the objects that will want to do something when the device
- // is unplugged from power *and* the screen is off.
- final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+ // is unplugged from power *and* the screen is off or doze.
+ protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
// Set to true when we want to distribute CPU across wakelocks for the next
// CPU update, even if we aren't currently running wake locks.
@@ -436,8 +436,12 @@ public class BatteryStatsImpl extends BatteryStats {
public boolean mRecordAllHistory;
boolean mNoAutoReset;
- int mScreenState = Display.STATE_UNKNOWN;
- StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected int mScreenState = Display.STATE_UNKNOWN;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenDozeTimer;
int mScreenBrightnessBin = -1;
final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -583,12 +587,16 @@ public class BatteryStatsImpl extends BatteryStats {
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
int mDischargeScreenOffUnplugLevel;
+ int mDischargeScreenDozeUnplugLevel;
int mDischargeAmountScreenOn;
int mDischargeAmountScreenOnSinceCharge;
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ int mDischargeAmountScreenDoze;
+ int mDischargeAmountScreenDozeSinceCharge;
private LongSamplingCounter mDischargeScreenOffCounter;
+ private LongSamplingCounter mDischargeScreenDozeCounter;
private LongSamplingCounter mDischargeCounter;
static final int MAX_LEVEL_STEPS = 200;
@@ -673,13 +681,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
- public LongCounter getDischargeScreenOffCoulombCounter() {
- return mDischargeScreenOffCounter;
+ public long getMahDischarge(int which) {
+ return mDischargeCounter.getCountLocked(which);
+ }
+
+ @Override
+ public long getMahDischargeScreenOff(int which) {
+ return mDischargeScreenOffCounter.getCountLocked(which);
}
@Override
- public LongCounter getDischargeCoulombCounter() {
- return mDischargeCounter;
+ public long getMahDischargeScreenDoze(int which) {
+ return mDischargeScreenDozeCounter.getCountLocked(which);
}
@Override
@@ -3573,8 +3586,9 @@ public class BatteryStatsImpl extends BatteryStats {
mActiveHistoryStates2 = 0xffffffff;
}
- public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
+ public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
+ final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState);
final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
final boolean updateOnBatteryScreenOffTimeBase =
(unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -3591,20 +3605,22 @@ public class BatteryStatsImpl extends BatteryStats {
updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
}
if (DEBUG_ENERGY_CPU) {
- Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
+ Slog.d(TAG, "Updating cpu time because screen is now "
+ + Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
updateCpuTimeLocked();
mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
- mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
- for (int i = mUidStats.size() - 1; i >= 0; --i) {
- final Uid u = mUidStats.valueAt(i);
- if (updateOnBatteryTimeBase) {
- u.updateOnBatteryBgTimeBase(uptime, realtime);
+ if (updateOnBatteryTimeBase) {
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime);
}
- if (updateOnBatteryScreenOffTimeBase) {
- u.updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
+ }
+ if (updateOnBatteryScreenOffTimeBase) {
+ mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
}
}
}
@@ -3864,8 +3880,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void setPretendScreenOff(boolean pretendScreenOff) {
- mPretendScreenOff = pretendScreenOff;
- noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ if (mPretendScreenOff != pretendScreenOff) {
+ mPretendScreenOff = pretendScreenOff;
+ noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ }
}
private String mInitialAcquireWakeName;
@@ -4195,54 +4213,58 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- if (state == Display.STATE_ON) {
- // Screen turning on.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+
+ boolean updateHistory = false;
+ if (isScreenDoze(state)) {
+ mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.startRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ } else if (isScreenDoze(oldState)) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ }
+ if (isScreenOn(state)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
}
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false,
- mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Fake a wake lock, so we consider the device waked as long
- // as the screen is on.
- noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
- elapsedRealtime, uptime);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(false, true);
- }
- } else if (oldState == Display.STATE_ON) {
- // Screen turning off or dozing.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ updateHistory = true;
+ } else if (isScreenOn(oldState)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
-
+ updateHistory = true;
+ }
+ if (updateHistory) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+ + Display.stateToString(state));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ if (isScreenOn(state)) {
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
+ mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+ // Fake a wake lock, so we consider the device waked as long as the screen is on.
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
+ } else if (isScreenOn(oldState)) {
noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL,
elapsedRealtime, uptime);
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(true, false);
- }
+ }
+ // Update discharge amounts.
+ if (mOnBatteryInternal) {
+ updateDischargeScreenLevelsLocked(oldState, state);
}
}
}
@@ -5391,6 +5413,14 @@ public class BatteryStatsImpl extends BatteryStats {
return mScreenOnTimer.getCountLocked(which);
}
+ @Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) {
+ return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getScreenDozeCount(int which) {
+ return mScreenDozeTimer.getCountLocked(which);
+ }
+
@Override public long getScreenBrightnessTime(int brightnessBin,
long elapsedRealtimeUs, int which) {
return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
@@ -8829,6 +8859,7 @@ public class BatteryStatsImpl extends BatteryStats {
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase);
@@ -8887,6 +8918,7 @@ public class BatteryStatsImpl extends BatteryStats {
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
long uptime = mClocks.uptimeMillis() * 1000;
@@ -9430,8 +9462,16 @@ public class BatteryStatsImpl extends BatteryStats {
return mCharging;
}
- public boolean isScreenOn() {
- return mScreenState == Display.STATE_ON;
+ public boolean isScreenOn(int state) {
+ return state == Display.STATE_ON;
+ }
+
+ public boolean isScreenOff(int state) {
+ return state == Display.STATE_OFF;
+ }
+
+ public boolean isScreenDoze(int state) {
+ return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND;
}
void initTimes(long uptime, long realtime) {
@@ -9451,9 +9491,12 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mDischargeAmountScreenDoze = 0;
+ mDischargeAmountScreenDozeSinceCharge = 0;
mDischargeStepTracker.init();
mChargeStepTracker.init();
mDischargeScreenOffCounter.reset(false);
+ mDischargeScreenDozeCounter.reset(false);
mDischargeCounter.reset(false);
}
@@ -9471,15 +9514,22 @@ public class BatteryStatsImpl extends BatteryStats {
mOnBatteryTimeBase.reset(uptime, realtime);
mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
- if (mScreenState == Display.STATE_ON) {
+ if (isScreenOn(mScreenState)) {
mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(mScreenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
+ mDischargeAmountScreenDoze = 0;
}
initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
@@ -9490,6 +9540,7 @@ public class BatteryStatsImpl extends BatteryStats {
mStartCount = 0;
initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
mScreenOnTimer.reset(false);
+ mScreenDozeTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false);
}
@@ -9626,33 +9677,52 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) {
- if (oldScreenOn) {
+ void updateDischargeScreenLevelsLocked(int oldState, int newState) {
+ updateOldDischargeScreenLevelLocked(oldState);
+ updateNewDischargeScreenLevelLocked(newState);
+ }
+
+ private void updateOldDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOn += diff;
mDischargeAmountScreenOnSinceCharge += diff;
}
- } else {
+ } else if (isScreenDoze(state)) {
+ int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel;
+ if (diff > 0) {
+ mDischargeAmountScreenDoze += diff;
+ mDischargeAmountScreenDozeSinceCharge += diff;
+ }
+ } else if (isScreenOff(state)){
int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOff += diff;
mDischargeAmountScreenOffSinceCharge += diff;
}
}
- if (newScreenOn) {
+ }
+
+ private void updateNewDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
mDischargeScreenOffUnplugLevel = 0;
- } else {
+ mDischargeScreenDozeUnplugLevel = 0;
+ } else if (isScreenDoze(state)){
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenOff(state)) {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
public void pullPendingStateUpdatesLocked() {
if (mOnBatteryInternal) {
- final boolean screenOn = mScreenState == Display.STATE_ON;
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
+ updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
}
}
@@ -10785,8 +10855,8 @@ public class BatteryStatsImpl extends BatteryStats {
return false;
}
- void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
- final int oldStatus, final int level, final int chargeUAh) {
+ protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
+ final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
@@ -10794,7 +10864,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long uptime = mSecUptime * 1000;
final long realtime = mSecRealtime * 1000;
- final boolean screenOn = mScreenState == Display.STATE_ON;
+ final int screenState = mScreenState;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
@@ -10870,16 +10940,23 @@ public class BatteryStatsImpl extends BatteryStats {
}
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
- if (screenOn) {
+ if (isScreenOn(screenState)) {
mDischargeScreenOnUnplugLevel = level;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(screenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = level;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = level;
}
mDischargeAmountScreenOn = 0;
+ mDischargeAmountScreenDoze = 0;
mDischargeAmountScreenOff = 0;
- updateTimeBasesLocked(true, !screenOn, uptime, realtime);
+ updateTimeBasesLocked(true, screenState, uptime, realtime);
} else {
mLastChargingStateLevel = level;
mOnBattery = mOnBatteryInternal = false;
@@ -10894,8 +10971,8 @@ public class BatteryStatsImpl extends BatteryStats {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
- updateTimeBasesLocked(false, !screenOn, uptime, realtime);
+ updateDischargeScreenLevelsLocked(screenState, screenState);
+ updateTimeBasesLocked(false, screenState, uptime, realtime);
mChargeStepTracker.init();
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
@@ -11012,6 +11089,9 @@ public class BatteryStatsImpl extends BatteryStats {
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
@@ -11054,6 +11134,9 @@ public class BatteryStatsImpl extends BatteryStats {
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
changed = true;
@@ -11362,10 +11445,11 @@ public class BatteryStatsImpl extends BatteryStats {
return dischargeAmount;
}
+ @Override
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11373,10 +11457,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ @Override
public int getDischargeAmountScreenOnSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOnSinceCharge;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11384,24 +11469,52 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ @Override
public int getDischargeAmountScreenOff() {
synchronized(this) {
int val = mDischargeAmountScreenOff;
- if (mOnBattery && mScreenState != Display.STATE_ON
+ if (mOnBattery && isScreenOff(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
- return val;
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDoze();
}
}
+ @Override
public int getDischargeAmountScreenOffSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOffSinceCharge;
- if (mOnBattery && mScreenState != Display.STATE_ON
+ if (mOnBattery && isScreenOff(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDozeSinceCharge();
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenDoze() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenDoze;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
+ }
+ return val;
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenDozeSinceCharge() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenDozeSinceCharge;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
+ }
return val;
}
}
@@ -11759,12 +11872,14 @@ public class BatteryStatsImpl extends BatteryStats {
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
mDischargeCounter.readSummaryFromParcelLocked(in);
mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
+ mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in);
int NPKG = in.readInt();
if (NPKG > 0) {
mDailyPackageChanges = new ArrayList<>(NPKG);
@@ -11787,6 +11902,7 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer.readSummaryFromParcelLocked(in);
+ mScreenDozeTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
}
@@ -12180,12 +12296,14 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
+ out.writeInt(getDischargeAmountScreenDozeSinceCharge());
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeSummaryFromParcelLocked(out);
mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
+ mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out);
if (mDailyPackageChanges != null) {
final int NPKG = mDailyPackageChanges.size();
out.writeInt(NPKG);
@@ -12203,6 +12321,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeLong(mNextMaxDailyDeadline);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
@@ -12635,6 +12754,7 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase, in);
@@ -12728,10 +12848,13 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDoze = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
- mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTime = in.readLong();
mRpmStats.clear();
@@ -12848,6 +12971,7 @@ public class BatteryStatsImpl extends BatteryStats {
mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mScreenOnTimer.writeToParcel(out, uSecRealtime);
+ mScreenDozeTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
@@ -12910,10 +13034,13 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mDischargeAmountScreenDoze);
+ out.writeInt(mDischargeAmountScreenDozeSinceCharge);
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeToParcel(out);
mDischargeScreenOffCounter.writeToParcel(out);
+ mDischargeScreenDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTime);
out.writeInt(mRpmStats.size());
@@ -13020,8 +13147,10 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println("mOnBatteryScreenOffTimeBase:");
mOnBatteryScreenOffTimeBase.dump(pw, " ");
Printer pr = new PrintWriterPrinter(pw);
- pr.println("*** Screen timer:");
+ pr.println("*** Screen on timer:");
mScreenOnTimer.logState(pr, " ");
+ pr.println("*** Screen doze timer:");
+ mScreenDozeTimer.logState(pr, " ");
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
pr.println("*** Screen brightness #" + i + ":");
mScreenBrightnessTimer[i].logState(pr, " ");
diff --git a/com/android/internal/os/Zygote.java b/com/android/internal/os/Zygote.java
index 5ee0918c..cbc63cf8 100644
--- a/com/android/internal/os/Zygote.java
+++ b/com/android/internal/os/Zygote.java
@@ -49,6 +49,11 @@ public final class Zygote {
/** Make the code Java debuggable by turning off some optimizations. */
public static final int DEBUG_JAVA_DEBUGGABLE = 1 << 8;
+ /** Turn off the verifier. */
+ public static final int DISABLE_VERIFIER = 1 << 9;
+ /** Only use oat files located in /system. Otherwise use dex/jar/apk . */
+ public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/com/android/internal/telephony/CallForwardInfo.java b/com/android/internal/telephony/CallForwardInfo.java
index dccf3066..e40028fc 100644
--- a/com/android/internal/telephony/CallForwardInfo.java
+++ b/com/android/internal/telephony/CallForwardInfo.java
@@ -16,12 +16,16 @@
package com.android.internal.telephony;
+import android.telecom.Log;
+
/**
* See also RIL_CallForwardInfo in include/telephony/ril.h
*
* {@hide}
*/
public class CallForwardInfo {
+ private static final String TAG = "CallForwardInfo";
+
public int status; /*1 = active, 0 = not active */
public int reason; /* from TS 27.007 7.11 "reason" */
public int serviceClass; /* Saum of CommandsInterface.SERVICE_CLASS */
@@ -31,9 +35,9 @@ public class CallForwardInfo {
@Override
public String toString() {
- return super.toString() + (status == 0 ? " not active " : " active ")
- + " reason: " + reason
- + " serviceClass: " + serviceClass + " " + timeSeconds + " seconds";
-
+ return "[CallForwardInfo: status=" + (status == 0 ? " not active " : " active ")
+ + ", reason= " + reason
+ + ", serviceClass= " + serviceClass + ", timeSec= " + timeSeconds + " seconds"
+ + ", number=" + Log.pii(number) + "]";
}
}
diff --git a/com/android/internal/telephony/CarrierKeyDownloadManager.java b/com/android/internal/telephony/CarrierKeyDownloadManager.java
index bca337d8..606f7ffd 100644
--- a/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony;
-import static android.preference.PreferenceManager.getDefaultSharedPreferences;
-
import android.app.AlarmManager;
import android.app.DownloadManager;
import android.app.PendingIntent;
@@ -34,22 +32,30 @@ import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Base64;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.org.bouncycastle.util.io.pem.PemReader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
import java.util.Date;
+import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+
/**
* This class contains logic to get Certificates and keep them current.
* The class will be instantiated by various Phone implementations.
@@ -68,16 +74,19 @@ public class CarrierKeyDownloadManager {
private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX =
"com.android.internal.telephony.carrier_key_download_alarm";
- private int mKeyAvailability = 0;
+ @VisibleForTesting
+ public int mKeyAvailability = 0;
public static final String MNC = "MNC";
public static final String MCC = "MCC";
private static final String SEPARATOR = ":";
- private static final String JSON_KEY = "key";
- private static final String JSON_TYPE = "type";
- private static final String JSON_IDENTIFIER = "identifier";
- private static final String JSON_EXPIRATION_DATE = "expiration-date";
+ private static final String JSON_CERTIFICATE = "certificate";
+ // 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";
+ private static final String JSON_IDENTIFIER = "key-identifier";
private static final String JSON_CARRIER_KEYS = "carrier-keys";
private static final String JSON_TYPE_VALUE_WLAN = "WLAN";
private static final String JSON_TYPE_VALUE_EPDG = "EPDG";
@@ -89,7 +98,7 @@ public class CarrierKeyDownloadManager {
private final Phone mPhone;
private final Context mContext;
- private final DownloadManager mDownloadManager;
+ public final DownloadManager mDownloadManager;
private String mURL;
public CarrierKeyDownloadManager(Phone phone) {
@@ -173,14 +182,11 @@ public class CarrierKeyDownloadManager {
}
/**
- * this method resets the alarm. Starts by cleaning up the existing alarms.
- * We look at the earliest expiration date, and setup an alarms X days prior.
- * If the expiration date is in the past, we'll setup an alarm to run the next day. This
- * could happen if the download has failed.
+ * this method returns the date to be used to decide on when to start downloading the key.
+ * from the carrier.
**/
- private void resetRenewalAlarm() {
- cleanupRenewalAlarms();
- int slotId = mPhone.getPhoneId();
+ @VisibleForTesting
+ public long getExpirationDate() {
long minExpirationDate = Long.MAX_VALUE;
for (int key_type : CARRIER_KEY_TYPES) {
if (!isKeyEnabled(key_type)) {
@@ -204,6 +210,20 @@ public class CarrierKeyDownloadManager {
} else {
minExpirationDate = minExpirationDate - DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
}
+ return minExpirationDate;
+ }
+
+ /**
+ * this method resets the alarm. Starts by cleaning up the existing alarms.
+ * We look at the earliest expiration date, and setup an alarms X days prior.
+ * If the expiration date is in the past, we'll setup an alarm to run the next day. This
+ * could happen if the download has failed.
+ **/
+ @VisibleForTesting
+ public void resetRenewalAlarm() {
+ cleanupRenewalAlarms();
+ int slotId = mPhone.getPhoneId();
+ long minExpirationDate = getExpirationDate();
Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate));
final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
@@ -225,21 +245,30 @@ public class CarrierKeyDownloadManager {
}
/**
+ * Returns the sim operator.
+ **/
+ @VisibleForTesting
+ public String getSimOperator() {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.getSimOperator(mPhone.getSubId());
+ }
+
+ /**
* checks if the download was sent by this particular instance. We do this by including the
* slot id in the key. If no value is found, we know that the download was not for this
* instance of the phone.
**/
- private boolean isValidDownload(String mccMnc) {
+ @VisibleForTesting
+ public boolean isValidDownload(String mccMnc) {
String mccCurrent = "";
String mncCurrent = "";
String mccSource = "";
String mncSource = "";
- final TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId());
- if (TextUtils.isEmpty(networkOperator) || TextUtils.isEmpty(mccMnc)) {
- Log.e(LOG_TAG, "networkOperator or mcc/mnc is empty");
+ String simOperator = getSimOperator();
+ if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) {
+ Log.e(LOG_TAG, "simOperator or mcc/mnc is empty");
return false;
}
@@ -248,8 +277,8 @@ public class CarrierKeyDownloadManager {
mncSource = splitValue[1];
Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource);
- mccCurrent = networkOperator.substring(0, 3);
- mncCurrent = networkOperator.substring(3);
+ mccCurrent = simOperator.substring(0, 3);
+ mncCurrent = simOperator.substring(3);
Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent);
if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) {
@@ -348,19 +377,20 @@ public class CarrierKeyDownloadManager {
* Converts the string into a json object to retreive the nodes. The Json should have 3 nodes,
* including the Carrier public key, the key type and the key identifier. Once the nodes have
* been extracted, they get persisted to the database. Sample:
- * "carrier-keys": [ { "key": "",
- * "type": WLAN,
- * "identifier": "",
- * "expiration-date": 1502577746000
+ * "carrier-keys": [ { "certificate": "",
+ * "key-type": "WLAN",
+ * "key-identifier": ""
* } ]
* @param jsonStr the json string.
- * @param mccMnc contains the mcc, mnc
+ * @param mccMnc contains the mcc, mnc.
*/
- private void parseJsonAndPersistKey(String jsonStr, String mccMnc) {
+ @VisibleForTesting
+ public void parseJsonAndPersistKey(String jsonStr, String mccMnc) {
if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) {
Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty");
return;
}
+ PemReader reader = null;
try {
String mcc = "";
String mnc = "";
@@ -369,10 +399,16 @@ public class CarrierKeyDownloadManager {
mnc = splitValue[1];
JSONObject jsonObj = new JSONObject(jsonStr);
JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS);
-
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
- String carrierKey = key.getString(JSON_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)) {
+ cert = key.getString(JSON_CERTIFICATE);
+ } else {
+ cert = key.getString(JSON_CERTIFICATE_ALTERNATE);
+ }
String typeString = key.getString(JSON_TYPE);
int type = UNINITIALIZED_KEY_TYPE;
if (typeString.equals(JSON_TYPE_VALUE_WLAN)) {
@@ -380,13 +416,27 @@ public class CarrierKeyDownloadManager {
} else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) {
type = TelephonyManager.KEY_TYPE_EPDG;
}
- long expiration_date = key.getLong(JSON_EXPIRATION_DATE);
String identifier = key.getString(JSON_IDENTIFIER);
- savePublicKey(carrierKey, type, identifier, expiration_date,
- mcc, mnc);
+ ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes());
+ Reader fReader = new BufferedReader(new InputStreamReader(inStream));
+ reader = new PemReader(fReader);
+ Pair<PublicKey, Long> keyInfo =
+ getKeyInformation(reader.readPemObject().getContent());
+ reader.close();
+ savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc);
}
} catch (final JSONException e) {
Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ }
}
}
@@ -394,8 +444,8 @@ public class CarrierKeyDownloadManager {
* introspects the mKeyAvailability bitmask
* @return true if the digit at position k is 1, else false.
*/
-
- private boolean isKeyEnabled(int keyType) {
+ @VisibleForTesting
+ public boolean isKeyEnabled(int keyType) {
//since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
int returnValue = (mKeyAvailability >> (keyType - 1)) & 1;
return (returnValue == 1) ? true : false;
@@ -427,15 +477,13 @@ public class CarrierKeyDownloadManager {
private boolean downloadKey() {
Log.d(LOG_TAG, "starting download from: " + mURL);
- final TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String mcc = "";
String mnc = "";
- String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId());
+ String simOperator = getSimOperator();
- if (!TextUtils.isEmpty(networkOperator)) {
- mcc = networkOperator.substring(0, 3);
- mnc = networkOperator.substring(3);
+ if (!TextUtils.isEmpty(simOperator)) {
+ mcc = simOperator.substring(0, 3);
+ mnc = simOperator.substring(3);
Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
} else {
Log.e(LOG_TAG, "mcc, mnc: is empty");
@@ -461,11 +509,35 @@ public class CarrierKeyDownloadManager {
return true;
}
- private void savePublicKey(String key, int type, String identifier, long expirationDate,
+ /**
+ * Save the public key
+ * @param certificate certificate that contains the public key.
+ * @return Pair containing the Public Key and the expiration date.
+ **/
+ @VisibleForTesting
+ public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception {
+ InputStream inStream = new ByteArrayInputStream(certificate);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
+ Pair<PublicKey, Long> keyInformation =
+ new Pair(cert.getPublicKey(), cert.getNotAfter().getTime());
+ return keyInformation;
+ }
+
+ /**
+ * Save the public key
+ * @param publicKey public key.
+ * @param type key-type.
+ * @param identifier which is an opaque string.
+ * @param expirationDate expiration date of the key.
+ * @param mcc
+ * @param mnc
+ **/
+ @VisibleForTesting
+ public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate,
String mcc, String mnc) {
- byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT);
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier,
- keyBytes, new Date(expirationDate));
+ publicKey, new Date(expirationDate));
mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
}
}
diff --git a/com/android/internal/telephony/CarrierServiceStateTracker.java b/com/android/internal/telephony/CarrierServiceStateTracker.java
index 8df201e5..77a39eb9 100644
--- a/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -79,13 +79,8 @@ public class CarrierServiceStateTracker extends Handler {
switch (msg.what) {
case CARRIER_EVENT_VOICE_REGISTRATION:
case CARRIER_EVENT_DATA_REGISTRATION:
- handleConfigChanges();
- break;
case CARRIER_EVENT_VOICE_DEREGISTRATION:
case CARRIER_EVENT_DATA_DEREGISTRATION:
- if (isRadioOffOrAirplaneMode()) {
- break;
- }
handleConfigChanges();
break;
case NOTIFICATION_EMERGENCY_NETWORK:
@@ -317,8 +312,8 @@ public class CarrierServiceStateTracker extends Handler {
Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
+ "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
+ "," + mSST.isRadioOn());
- if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered()
- || isGlobalMode()) {
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
+ || isRadioOffOrAirplaneMode()) {
return false;
}
return true;
diff --git a/com/android/internal/telephony/ClientWakelockTracker.java b/com/android/internal/telephony/ClientWakelockTracker.java
index 5bec60ba..fa71e769 100644
--- a/com/android/internal/telephony/ClientWakelockTracker.java
+++ b/com/android/internal/telephony/ClientWakelockTracker.java
@@ -18,10 +18,10 @@ package com.android.internal.telephony;
import android.os.SystemClock;
import android.telephony.ClientRequestStats;
-import android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -119,13 +119,13 @@ public class ClientWakelockTracker {
return false;
}
- void dumpClientRequestTracker() {
- Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------");
+ void dumpClientRequestTracker(PrintWriter pw) {
+ pw.println("-------mClients---------------");
synchronized (mClients) {
for (String key : mClients.keySet()) {
- Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key);
- Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString());
+ pw.println("Client : " + key);
+ pw.println(mClients.get(key).toString());
}
}
}
-} \ No newline at end of file
+}
diff --git a/com/android/internal/telephony/Connection.java b/com/android/internal/telephony/Connection.java
index 245f76ce..8c54a31b 100644
--- a/com/android/internal/telephony/Connection.java
+++ b/com/android/internal/telephony/Connection.java
@@ -34,6 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
* {@hide}
*/
public abstract class Connection {
+ private static final String TAG = "Connection";
public interface PostDialListener {
void onPostDialWait();
@@ -836,6 +837,16 @@ public abstract class Connection {
public void setConnectionExtras(Bundle extras) {
if (extras != null) {
mExtras = new Bundle(extras);
+
+ int previousCount = mExtras.size();
+ // Prevent vendors from passing in extras other than primitive types and android API
+ // parcelables.
+ mExtras = mExtras.filterValues();
+ int filteredCount = mExtras.size();
+ if (filteredCount != previousCount) {
+ Rlog.i(TAG, "setConnectionExtras: filtering " + (previousCount - filteredCount)
+ + " invalid extras.");
+ }
} else {
mExtras = null;
}
diff --git a/com/android/internal/telephony/DefaultPhoneNotifier.java b/com/android/internal/telephony/DefaultPhoneNotifier.java
index c13e5408..98c0a32e 100644
--- a/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -125,6 +125,9 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
int subId = sender.getSubId();
try {
if (mRegistry != null) {
+ Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive="
+ + sender.getCallForwardingIndicator());
+
mRegistry.notifyCallForwardingChangedForSubscriber(subId,
sender.getCallForwardingIndicator());
}
diff --git a/com/android/internal/telephony/GsmCdmaPhone.java b/com/android/internal/telephony/GsmCdmaPhone.java
index d95d0183..ad078d67 100644
--- a/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/com/android/internal/telephony/GsmCdmaPhone.java
@@ -286,7 +286,7 @@ public class GsmCdmaPhone extends Phone {
tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM);
mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
} else {
- mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
+ mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
// This is needed to handle phone process crashes
mIsPhoneInEcmState = getInEcmMode();
if (mIsPhoneInEcmState) {
@@ -505,7 +505,7 @@ public class GsmCdmaPhone extends Phone {
ret = PhoneConstants.DataState.DISCONNECTED;
} else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
- && (isPhoneTypeCdma() ||
+ && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
(isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) {
// If we're out of service, open TCP sockets may still work
// but no data will flow
@@ -1063,7 +1063,7 @@ public class GsmCdmaPhone extends Phone {
boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
.getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
- boolean imsUseEnabled = isImsUseEnabled()
+ boolean useImsForCall = isImsUseEnabled()
&& imsPhone != null
&& (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
(imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState)))
@@ -1083,7 +1083,7 @@ public class GsmCdmaPhone extends Phone {
boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
if (DBG) {
- logd("imsUseEnabled=" + imsUseEnabled
+ logd("useImsForCall=" + useImsForCall
+ ", useImsForEmergency=" + useImsForEmergency
+ ", useImsForUt=" + useImsForUt
+ ", isUt=" + isUt
@@ -1100,13 +1100,13 @@ public class GsmCdmaPhone extends Phone {
Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
- if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
+ if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
try {
if (DBG) logd("Trying IMS PS call");
return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
} catch (CallStateException e) {
if (DBG) logd("IMS PS call exception " + e +
- "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
+ "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
// Do not throw a CallStateException and instead fall back to Circuit switch
// for emergency calls and MMI codes.
if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
@@ -2549,6 +2549,7 @@ public class GsmCdmaPhone extends Phone {
private void processIccRecordEvents(int eventCode) {
switch (eventCode) {
case IccRecords.EVENT_CFI:
+ logi("processIccRecordEvents: EVENT_CFI");
notifyCallForwardingIndicator();
break;
}
diff --git a/com/android/internal/telephony/InboundSmsHandler.java b/com/android/internal/telephony/InboundSmsHandler.java
index 59195f83..391de500 100644
--- a/com/android/internal/telephony/InboundSmsHandler.java
+++ b/com/android/internal/telephony/InboundSmsHandler.java
@@ -158,6 +158,17 @@ public abstract class InboundSmsHandler extends StateMachine {
/** New SMS received as an AsyncResult. */
public static final int EVENT_INJECT_SMS = 8;
+ /** Update tracker object; used only in waiting state */
+ private static final int EVENT_UPDATE_TRACKER = 9;
+
+ /** Timeout in case state machine is stuck in a state for too long; used only in waiting
+ * state */
+ private static final int EVENT_STATE_TIMEOUT = 10;
+
+ /** Timeout duration for EVENT_STATE_TIMEOUT */
+ @VisibleForTesting
+ public static final int STATE_TIMEOUT = 30000;
+
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
@@ -450,6 +461,7 @@ public abstract class InboundSmsHandler extends StateMachine {
// if any broadcasts were sent, transition to waiting state
InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
if (processMessagePart(inboundSmsTracker)) {
+ sendMessage(EVENT_UPDATE_TRACKER, inboundSmsTracker);
transitionTo(mWaitingState);
} else {
// if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -493,18 +505,41 @@ public abstract class InboundSmsHandler extends StateMachine {
* {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
*/
private class WaitingState extends State {
+ private InboundSmsTracker mTracker;
+
+ @Override
+ public void enter() {
+ if (DBG) log("entering Waiting state");
+ mTracker = null;
+ sendMessageDelayed(EVENT_STATE_TIMEOUT, STATE_TIMEOUT);
+ }
+
@Override
public void exit() {
if (DBG) log("exiting Waiting state");
// Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
// to give any receivers time to take their own wake locks
setWakeLockTimeout(WAKELOCK_TIMEOUT);
+ if (VDBG) {
+ if (hasMessages(EVENT_STATE_TIMEOUT)) {
+ log("exiting Waiting state: removing EVENT_STATE_TIMEOUT from message queue");
+ }
+ if (hasMessages(EVENT_UPDATE_TRACKER)) {
+ log("exiting Waiting state: removing EVENT_UPDATE_TRACKER from message queue");
+ }
+ }
+ removeMessages(EVENT_STATE_TIMEOUT);
+ removeMessages(EVENT_UPDATE_TRACKER);
}
@Override
public boolean processMessage(Message msg) {
log("WaitingState.processMessage:" + msg.what);
switch (msg.what) {
+ case EVENT_UPDATE_TRACKER:
+ mTracker = (InboundSmsTracker) msg.obj;
+ return HANDLED;
+
case EVENT_BROADCAST_SMS:
// defer until the current broadcast completes
deferMessage(msg);
@@ -520,6 +555,18 @@ public abstract class InboundSmsHandler extends StateMachine {
// not ready to return to idle; ignore
return HANDLED;
+ case EVENT_STATE_TIMEOUT:
+ // stuck in WaitingState for too long; drop the message and exit this state
+ if (mTracker != null) {
+ log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; dropping message");
+ dropSms(new SmsBroadcastReceiver(mTracker));
+ } else {
+ log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; mTracker is null "
+ + "- sending EVENT_BROADCAST_COMPLETE");
+ sendMessage(EVENT_BROADCAST_COMPLETE);
+ }
+ return HANDLED;
+
default:
// parent state handles the other message types
return NOT_HANDLED;
diff --git a/com/android/internal/telephony/Phone.java b/com/android/internal/telephony/Phone.java
index 28e45565..6acc8743 100644
--- a/com/android/internal/telephony/Phone.java
+++ b/com/android/internal/telephony/Phone.java
@@ -56,6 +56,7 @@ import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.VoLteServiceState;
import android.text.TextUtils;
+import android.util.Log;
import com.android.ims.ImsCall;
import com.android.ims.ImsConfig;
@@ -226,6 +227,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
// Key used to read/write "disable DNS server check" pref (used for testing)
private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
+ // Integer used to let the calling application know that the we are ignoring auto mode switch.
+ private static final int ALREADY_IN_AUTO_SELECTION = 1;
+
/**
* This method is invoked when the Phone exits Emergency Callback Mode.
*/
@@ -1205,6 +1209,11 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mCi.setNetworkSelectionModeAutomatic(msg);
} else {
Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+ // let the calling application know that the we are ignoring automatic mode switch.
+ if (nsm.message != null) {
+ nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION;
+ }
+
ar.userObj = nsm;
handleSetSelectNetwork(ar);
}
@@ -1789,7 +1798,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED :
IccRecords.CALL_FORWARDING_STATUS_DISABLED;
int subId = getSubId();
- Rlog.d(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
+ Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
" in pref " + CF_STATUS + subId);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -1831,6 +1840,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) {
callForwardingIndicator = getCallForwardingIndicatorFromSharedPref();
}
+ Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null
+ ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag="
+ + getCallForwardingIndicatorFromSharedPref());
return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED);
}
diff --git a/com/android/internal/telephony/RIL.java b/com/android/internal/telephony/RIL.java
index 8bb2125d..84c2b659 100644
--- a/com/android/internal/telephony/RIL.java
+++ b/com/android/internal/telephony/RIL.java
@@ -4774,7 +4774,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
- mClientWakelockTracker.dumpClientRequestTracker();
+ mClientWakelockTracker.dumpClientRequestTracker(pw);
}
public List<ClientRequestStats> getClientRequestStats() {
diff --git a/com/android/internal/telephony/ServiceStateTracker.java b/com/android/internal/telephony/ServiceStateTracker.java
index b3794402..c34cbb29 100644
--- a/com/android/internal/telephony/ServiceStateTracker.java
+++ b/com/android/internal/telephony/ServiceStateTracker.java
@@ -210,6 +210,7 @@ public class ServiceStateTracker extends Handler {
protected static final int EVENT_ALL_DATA_DISCONNECTED = 49;
protected static final int EVENT_PHONE_TYPE_SWITCHED = 50;
protected static final int EVENT_RADIO_POWER_FROM_CARRIER = 51;
+ protected static final int EVENT_SIM_NOT_INSERTED = 52;
protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -354,6 +355,14 @@ public class ServiceStateTracker extends Handler {
}
// update voicemail count and notify message waiting changed
mPhone.updateVoiceMail();
+
+ // cancel notifications if we see SIM_NOT_INSERTED (This happens on bootup before
+ // the SIM is first detected and then subsequently on SIM removals)
+ if (mSubscriptionController.getSlotIndex(subId)
+ == SubscriptionManager.SIM_NOT_INSERTED) {
+ // this is handled on the main thread to mitigate racing with setNotification().
+ sendMessage(obtainMessage(EVENT_SIM_NOT_INSERTED));
+ }
}
}
};
@@ -446,12 +455,15 @@ public class ServiceStateTracker extends Handler {
public static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service
public static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service
public static final int CS_REJECT_CAUSE_ENABLED = 2001; // Notify MM rejection cause
- public static final int CS_REJECT_CAUSE_DISABLED = 2002; // Cancel MM rejection cause
/** Notification id. */
public static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted
public static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted
public static final int CS_REJECT_CAUSE_NOTIFICATION = 111; // Id to update and cancel MM
// rejection cause
+
+ /** To identify whether EVENT_SIM_READY is received or not */
+ private boolean mIsSimReady = false;
+
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1064,6 +1076,11 @@ public class ServiceStateTracker extends Handler {
case EVENT_ICC_CHANGED:
onUpdateIccAvailability();
+ if (mUiccApplcation != null
+ && mUiccApplcation.getState() != AppState.APPSTATE_READY) {
+ mIsSimReady = false;
+ updateSpnDisplay();
+ }
break;
case EVENT_GET_CELL_INFO_LIST: {
@@ -1121,6 +1138,7 @@ public class ServiceStateTracker extends Handler {
// Reset the mPreviousSubId so we treat a SIM power bounce
// as a first boot. See b/19194287
mOnSubscriptionsChangedListener.mPreviousSubId.set(-1);
+ mIsSimReady = true;
pollState();
// Signal strength polling stops when radio is off
queueNextSignalStrengthPoll();
@@ -1298,6 +1316,14 @@ public class ServiceStateTracker extends Handler {
}
break;
+ case EVENT_SIM_NOT_INSERTED:
+ if (DBG) log("EVENT_SIM_NOT_INSERTED");
+ cancelAllNotifications();
+ mMdn = null;
+ mMin = null;
+ mIsMinInfoReady = false;
+ break;
+
case EVENT_ALL_DATA_DISCONNECTED:
int dds = SubscriptionManager.getDefaultDataSubscriptionId();
ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
@@ -2222,7 +2248,12 @@ public class ServiceStateTracker extends Handler {
if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE
|| combinedRegState == ServiceState.STATE_EMERGENCY_ONLY) {
showPlmn = true;
- if (mEmergencyOnly) {
+
+ // Force display no service
+ final boolean forceDisplayNoService = mPhone.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_display_no_service_when_sim_unready)
+ && !mIsSimReady;
+ if (mEmergencyOnly && !forceDisplayNoService) {
// No service but emergency call allowed
plmn = Resources.getSystem().
getText(com.android.internal.R.string.emergency_calls_only).toString();
@@ -2825,7 +2856,7 @@ public class ServiceStateTracker extends Handler {
}
if (hasRejectCauseChanged) {
- setNotification(mRejectCode == 0 ? CS_REJECT_CAUSE_DISABLED : CS_REJECT_CAUSE_ENABLED);
+ setNotification(CS_REJECT_CAUSE_ENABLED);
}
if (hasChanged) {
@@ -3833,6 +3864,18 @@ public class ServiceStateTracker extends Handler {
}
/**
+ * Cancels all notifications posted to NotificationManager. These notifications for restricted
+ * state and rejection cause for cs registration are no longer valid after the SIM has been
+ * removed.
+ */
+ private void cancelAllNotifications() {
+ if (DBG) log("setNotification: cancelAllNotifications");
+ NotificationManager notificationManager = (NotificationManager)
+ mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancelAll();
+ }
+
+ /**
* Post a notification to NotificationManager for restricted state and
* rejection cause for cs registration
*
@@ -3907,17 +3950,14 @@ public class ServiceStateTracker extends Handler {
notificationId = CS_REJECT_CAUSE_NOTIFICATION;
int resId = selectResourceForRejectCode(mRejectCode);
if (0 == resId) {
- // cancel notification because current reject code is not handled.
- notifyType = CS_REJECT_CAUSE_DISABLED;
+ loge("setNotification: mRejectCode=" + mRejectCode + " is not handled.");
+ return;
} else {
icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn;
title = Resources.getSystem().getString(resId);
details = null;
}
break;
- case CS_REJECT_CAUSE_DISABLED:
- notificationId = CS_REJECT_CAUSE_NOTIFICATION;
- break;
}
if (DBG) {
@@ -3941,8 +3981,7 @@ public class ServiceStateTracker extends Handler {
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
- if (notifyType == PS_DISABLED || notifyType == CS_DISABLED
- || notifyType == CS_REJECT_CAUSE_DISABLED) {
+ if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
// cancel previous post notification
notificationManager.cancel(notificationId);
} else {
diff --git a/com/android/internal/telephony/cat/AppInterface.java b/com/android/internal/telephony/cat/AppInterface.java
index c78b7f8c..1f2d3a06 100644
--- a/com/android/internal/telephony/cat/AppInterface.java
+++ b/com/android/internal/telephony/cat/AppInterface.java
@@ -84,6 +84,7 @@ public interface AppInterface {
SET_UP_MENU(0x25),
SET_UP_CALL(0x10),
PROVIDE_LOCAL_INFORMATION(0x26),
+ LANGUAGE_NOTIFICATION(0x35),
OPEN_CHANNEL(0x40),
CLOSE_CHANNEL(0x41),
RECEIVE_DATA(0x42),
diff --git a/com/android/internal/telephony/cat/CatService.java b/com/android/internal/telephony/cat/CatService.java
index a242de43..cd7a7561 100644
--- a/com/android/internal/telephony/cat/CatService.java
+++ b/com/android/internal/telephony/cat/CatService.java
@@ -16,15 +16,21 @@
package com.android.internal.telephony.cat;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.backup.BackupManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources.NotFoundException;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.LocaleList;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -450,6 +456,18 @@ public class CatService extends Handler implements AppInterface {
((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString();
}
break;
+ case LANGUAGE_NOTIFICATION:
+ String language = ((LanguageParams) cmdParams).mLanguage;
+ ResultCode result = ResultCode.OK;
+ if (language != null && language.length() > 0) {
+ try {
+ changeLanguage(language);
+ } catch (RemoteException e) {
+ result = ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS;
+ }
+ }
+ sendTerminalResponse(cmdParams.mCmdDet, result, false, 0, null);
+ return;
case OPEN_CHANNEL:
case CLOSE_CHANNEL:
case RECEIVE_DATA:
@@ -881,8 +899,9 @@ public class CatService extends Handler implements AppInterface {
// This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true).
intent.putExtra(AppInterface.CARD_STATUS, cardPresent);
intent.setComponent(AppInterface.getDefaultSTKApplication());
+ intent.putExtra("SLOT_ID", mSlotId);
CatLog.d(this, "Sending Card Status: "
- + cardState + " " + "cardPresent: " + cardPresent);
+ + cardState + " " + "cardPresent: " + cardPresent + "SLOT_ID: " + mSlotId);
mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
}
@@ -1006,6 +1025,13 @@ public class CatService extends Handler implements AppInterface {
}
break;
case LAUNCH_BROWSER:
+ if (resMsg.mResCode == ResultCode.LAUNCH_BROWSER_ERROR) {
+ // Additional info for Default URL unavailable.
+ resMsg.setAdditionalInfo(0x04);
+ } else {
+ resMsg.mIncludeAdditionalInfo = false;
+ resMsg.mAdditionalInfo = 0;
+ }
break;
// 3GPP TS.102.223: Open Channel alpha confirmation should not send TR
case OPEN_CHANNEL:
@@ -1121,4 +1147,13 @@ public class CatService extends Handler implements AppInterface {
mCmdIf.reportStkServiceIsRunning(null);
}
}
+
+ private void changeLanguage(String language) throws RemoteException {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ Configuration config = am.getConfiguration();
+ config.setLocales(new LocaleList(new Locale(language), LocaleList.getDefault()));
+ config.userSetLocale = true;
+ am.updatePersistentConfiguration(config);
+ BackupManager.dataChanged("com.android.providers.settings");
+ }
}
diff --git a/com/android/internal/telephony/cat/CommandParams.java b/com/android/internal/telephony/cat/CommandParams.java
index 7dfedab8..59cd4148 100644
--- a/com/android/internal/telephony/cat/CommandParams.java
+++ b/com/android/internal/telephony/cat/CommandParams.java
@@ -150,6 +150,15 @@ class CallSetupParams extends CommandParams {
}
}
+class LanguageParams extends CommandParams {
+ String mLanguage;
+
+ LanguageParams(CommandDetails cmdDet, String lang) {
+ super(cmdDet);
+ mLanguage = lang;
+ }
+}
+
class SelectItemParams extends CommandParams {
Menu mMenu = null;
boolean mLoadTitleIcon = false;
diff --git a/com/android/internal/telephony/cat/CommandParamsFactory.java b/com/android/internal/telephony/cat/CommandParamsFactory.java
index 3dd53376..eb928885 100644
--- a/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -19,12 +19,15 @@ package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
+import android.text.TextUtils;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.uicc.IccFileHandler;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+
import static com.android.internal.telephony.cat.CatCmdMessage.
SetupEventListConstants.USER_ACTIVITY_EVENT;
import static com.android.internal.telephony.cat.CatCmdMessage.
@@ -47,6 +50,8 @@ class CommandParamsFactory extends Handler {
private int mIconLoadState = LOAD_NO_ICON;
private RilMessageDecoder mCaller = null;
private boolean mloadIcon = false;
+ private String mSavedLanguage;
+ private String mRequestedLanguage;
// constants
static final int MSG_ID_LOAD_ICON_DONE = 1;
@@ -66,6 +71,10 @@ class CommandParamsFactory extends Handler {
static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
+ // Command Qualifier value for language notification command
+ static final int NON_SPECIFIC_LANGUAGE = 0x00;
+ static final int SPECIFIC_LANGUAGE = 0x01;
+
// As per TS 102.223 Annex C, Structure of CAT communications,
// the APDU length can be max 255 bytes. This leaves only 239 bytes for user
// input string. CMD details TLV + Device IDs TLV + Result TLV + Other
@@ -203,6 +212,9 @@ class CommandParamsFactory extends Handler {
case PROVIDE_LOCAL_INFORMATION:
cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
break;
+ case LANGUAGE_NOTIFICATION:
+ cmdPending = processLanguageNotification(cmdDet, ctlvs);
+ break;
case OPEN_CHANNEL:
case CLOSE_CHANNEL:
case RECEIVE_DATA:
@@ -1014,6 +1026,67 @@ class CommandParamsFactory extends Handler {
return false;
}
+ /**
+ * Processes LANGUAGE_NOTIFICATION proactive command from the SIM card.
+ *
+ * The SPECIFIC_LANGUAGE notification sets the specified language.
+ * The NON_SPECIFIC_LANGUAGE notification restores the last specifically set language.
+ *
+ * @param cmdDet Command Details object retrieved from the proactive command object
+ * @param ctlvs List of ComprehensionTlv objects following Command Details
+ * object and Device Identities object within the proactive command
+ * @return false. This function always returns false meaning that the command
+ * processing is not pending and additional asynchronous processing
+ * is not required.
+ */
+ private boolean processLanguageNotification(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+ throws ResultException {
+ CatLog.d(this, "process Language Notification");
+
+ String desiredLanguage = null;
+ String currentLanguage = Locale.getDefault().getLanguage();
+ switch (cmdDet.commandQualifier) {
+ case NON_SPECIFIC_LANGUAGE:
+ if (!TextUtils.isEmpty(mSavedLanguage) && (!TextUtils.isEmpty(mRequestedLanguage)
+ && mRequestedLanguage.equals(currentLanguage))) {
+ CatLog.d(this, "Non-specific language notification changes the language "
+ + "setting back to " + mSavedLanguage);
+ desiredLanguage = mSavedLanguage;
+ }
+
+ mSavedLanguage = null;
+ mRequestedLanguage = null;
+ break;
+ case SPECIFIC_LANGUAGE:
+ ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.LANGUAGE, ctlvs);
+ if (ctlv != null) {
+ int valueLen = ctlv.getLength();
+ if (valueLen != 2) {
+ throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
+ }
+
+ byte[] rawValue = ctlv.getRawValue();
+ int valueIndex = ctlv.getValueIndex();
+ desiredLanguage = GsmAlphabet.gsm8BitUnpackedToString(rawValue, valueIndex, 2);
+
+ if (TextUtils.isEmpty(mSavedLanguage) || (!TextUtils.isEmpty(mRequestedLanguage)
+ && !mRequestedLanguage.equals(currentLanguage))) {
+ mSavedLanguage = currentLanguage;
+ }
+ mRequestedLanguage = desiredLanguage;
+ CatLog.d(this, "Specific language notification changes the language setting to "
+ + mRequestedLanguage);
+ }
+ break;
+ default:
+ CatLog.d(this, "LN[" + cmdDet.commandQualifier + "] Command Not Supported");
+ break;
+ }
+
+ mCmdParams = new LanguageParams(cmdDet, desiredLanguage);
+ return false;
+ }
+
private boolean processBIPClient(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
AppInterface.CommandType commandType =
diff --git a/com/android/internal/telephony/cdma/SmsMessage.java b/com/android/internal/telephony/cdma/SmsMessage.java
index 629173df..7a53ef63 100644
--- a/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/com/android/internal/telephony/cdma/SmsMessage.java
@@ -161,7 +161,7 @@ public class SmsMessage extends SmsMessageBase {
// Second byte is the MSG_LEN, length of the message
// See 3GPP2 C.S0023 3.4.27
- int size = data[1];
+ int size = data[1] & 0xFF;
// Note: Data may include trailing FF's. That's OK; message
// should still parse correctly.
diff --git a/com/android/internal/telephony/dataconnection/DcTracker.java b/com/android/internal/telephony/dataconnection/DcTracker.java
index f9b00178..fb756cd9 100644
--- a/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -149,8 +149,6 @@ public class DcTracker extends Handler {
private static final boolean DATA_STALL_SUSPECTED = true;
private static final boolean DATA_STALL_NOT_SUSPECTED = false;
- private String RADIO_RESET_PROPERTY = "gsm.radioreset";
-
private static final String INTENT_RECONNECT_ALARM =
"com.android.internal.telephony.data-reconnect";
private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
@@ -2246,7 +2244,7 @@ public class DcTracker extends Handler {
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// Get current sub id.
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ int subId = mPhone.getSubId();
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
if (DBG) {
@@ -4546,13 +4544,11 @@ public class DcTracker extends Handler {
public static final int CLEANUP = 1;
public static final int REREGISTER = 2;
public static final int RADIO_RESTART = 3;
- public static final int RADIO_RESTART_WITH_PROP = 4;
private static boolean isAggressiveRecovery(int value) {
return ((value == RecoveryAction.CLEANUP) ||
(value == RecoveryAction.REREGISTER) ||
- (value == RecoveryAction.RADIO_RESTART) ||
- (value == RecoveryAction.RADIO_RESTART_WITH_PROP));
+ (value == RecoveryAction.RADIO_RESTART));
}
}
@@ -4598,23 +4594,6 @@ public class DcTracker extends Handler {
EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
mSentSinceLastRecv);
if (DBG) log("restarting radio");
- putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
- restartRadio();
- break;
- case RecoveryAction.RADIO_RESTART_WITH_PROP:
- // This is in case radio restart has not recovered the data.
- // It will set an additional "gsm.radioreset" property to tell
- // RIL or system to take further action.
- // The implementation of hard reset recovery action is up to OEM product.
- // Once RADIO_RESET property is consumed, it is expected to set back
- // to false by RIL.
- EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
- if (DBG) log("restarting radio with gsm.radioreset to true");
- SystemProperties.set(RADIO_RESET_PROPERTY, "true");
- // give 1 sec so property change can be notified.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {}
restartRadio();
putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
break;
diff --git a/com/android/internal/telephony/gsm/GsmSmsAddress.java b/com/android/internal/telephony/gsm/GsmSmsAddress.java
index 2fbf7ed5..bd8c83e6 100644
--- a/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.gsm;
import android.telephony.PhoneNumberUtils;
+
import java.text.ParseException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsAddress;
@@ -71,8 +72,11 @@ public class GsmSmsAddress extends SmsAddress {
// Make sure the final unused BCD digit is 0xf
origBytes[length - 1] |= 0xf0;
}
- address = PhoneNumberUtils.calledPartyBCDToString(origBytes,
- OFFSET_TOA, length - OFFSET_TOA);
+ address = PhoneNumberUtils.calledPartyBCDToString(
+ origBytes,
+ OFFSET_TOA,
+ length - OFFSET_TOA,
+ PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY);
// And restore origBytes
origBytes[length - 1] = lastByte;
diff --git a/com/android/internal/telephony/gsm/SmsMessage.java b/com/android/internal/telephony/gsm/SmsMessage.java
index d4098d94..1ca19e01 100644
--- a/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/com/android/internal/telephony/gsm/SmsMessage.java
@@ -535,8 +535,8 @@ public class SmsMessage extends SmsMessageBase {
} else {
// SC address
try {
- ret = PhoneNumberUtils
- .calledPartyBCDToString(mPdu, mCur, len);
+ ret = PhoneNumberUtils.calledPartyBCDToString(
+ mPdu, mCur, len, PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY);
} catch (RuntimeException tr) {
Rlog.d(LOG_TAG, "invalid SC address: ", tr);
ret = null;
diff --git a/com/android/internal/telephony/imsphone/ImsPhone.java b/com/android/internal/telephony/imsphone/ImsPhone.java
index 45dc0b27..03d83dfc 100644
--- a/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1038,6 +1038,9 @@ public class ImsPhone extends ImsPhoneBase {
break;
case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
error = CommandException.Error.RADIO_NOT_AVAILABLE;
+ break;
+ case ImsReasonInfo.CODE_FDN_BLOCKED:
+ error = CommandException.Error.FDN_CHECK_FAILURE;
default:
break;
}
diff --git a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index ab30878b..f837b563 100644
--- a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -2566,7 +2566,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
&& targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
&& targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
if (isHandoverFromWifi && imsCall.isVideoCall()) {
- if (mNotifyHandoverVideoFromWifiToLTE) {
+ if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
log("onCallHandover :: notifying of WIFI to LTE handover.");
conn.onConnectionEvent(
TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
@@ -2575,7 +2575,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (!mIsDataEnabled && mIsViLteDataMetered) {
// Call was downgraded from WIFI to LTE and data is metered; downgrade the
// call now.
- downgradeVideoCall(ImsReasonInfo.CODE_DATA_DISABLED, conn);
+ downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
}
}
} else {
@@ -3535,8 +3535,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
// If the carrier supports downgrading to voice, then we can simply issue a
// downgrade to voice instead of terminating the call.
modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
- } else if (mSupportPauseVideo) {
- // The carrier supports video pause signalling, so pause the video.
+ } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
+ // The carrier supports video pause signalling, so pause the video if we didn't just
+ // lose wifi; in that case just disconnect.
mShouldUpdateImsConfigOnDisconnect = true;
conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
} else {
diff --git a/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 4e3957ed..9c99055c 100644
--- a/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -40,6 +40,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import com.android.ims.ImsException;
+import com.android.ims.ImsReasonInfo;
import com.android.ims.ImsSsInfo;
import com.android.ims.ImsUtInterface;
import com.android.internal.telephony.CallForwardInfo;
@@ -1172,6 +1173,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
private CharSequence getErrorMessage(AsyncResult ar) {
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error err = ((CommandException) (ar.exception)).getCommandError();
+ if (err == CommandException.Error.FDN_CHECK_FAILURE) {
+ Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
+ return mContext.getText(com.android.internal.R.string.mmiFdnError);
+ }
+ }
+
return mContext.getText(com.android.internal.R.string.mmiError);
}
@@ -1216,18 +1225,15 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
sb.append(mContext.getText(
com.android.internal.R.string.passwordIncorrect));
+ } else if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
+ sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
} else if (err.getMessage() != null) {
sb.append(err.getMessage());
} else {
sb.append(mContext.getText(com.android.internal.R.string.mmiError));
}
- } else {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ } else if (ar.exception instanceof ImsException) {
+ sb.append(getImsErrorMessage(ar));
}
} else if (isActivate()) {
mState = State.COMPLETE;
@@ -1360,12 +1366,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
mState = State.FAILED;
if (ar.exception instanceof ImsException) {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ sb.append(getImsErrorMessage(ar));
}
else {
sb.append(getErrorMessage(ar));
@@ -1421,21 +1422,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
StringBuilder sb = new StringBuilder(getScString());
sb.append("\n");
+ mState = State.FAILED;
if (ar.exception != null) {
- mState = State.FAILED;
-
if (ar.exception instanceof ImsException) {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ sb.append(getImsErrorMessage(ar));
} else {
sb.append(getErrorMessage(ar));
}
} else {
- mState = State.FAILED;
ImsSsInfo ssInfo = null;
if (ar.result instanceof Bundle) {
Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
@@ -1486,12 +1480,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
mState = State.FAILED;
if (ar.exception instanceof ImsException) {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ sb.append(getImsErrorMessage(ar));
} else {
sb.append(getErrorMessage(ar));
}
@@ -1525,14 +1514,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
mState = State.FAILED;
if (ar.exception != null) {
-
if (ar.exception instanceof ImsException) {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ sb.append(getImsErrorMessage(ar));
}
} else {
Bundle ssInfo = (Bundle) ar.result;
@@ -1623,12 +1606,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
mState = State.FAILED;
if (ar.exception instanceof ImsException) {
- ImsException error = (ImsException) ar.exception;
- if (error.getMessage() != null) {
- sb.append(error.getMessage());
- } else {
- sb.append(getErrorMessage(ar));
- }
+ sb.append(getImsErrorMessage(ar));
} else {
sb.append(getErrorMessage(ar));
}
@@ -1676,6 +1654,17 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return sb;
}
+ private CharSequence getImsErrorMessage(AsyncResult ar) {
+ ImsException error = (ImsException) ar.exception;
+ if (error.getCode() == ImsReasonInfo.CODE_FDN_BLOCKED) {
+ return mContext.getText(com.android.internal.R.string.mmiFdnError);
+ } else if (error.getMessage() != null) {
+ return error.getMessage();
+ } else {
+ return getErrorMessage(ar);
+ }
+ }
+
@Override
public ResultReceiver getUssdCallbackReceiver() {
return this.mCallbackReceiver;
diff --git a/com/android/internal/telephony/uicc/AdnRecord.java b/com/android/internal/telephony/uicc/AdnRecord.java
index 203236ca..4414cafb 100644
--- a/com/android/internal/telephony/uicc/AdnRecord.java
+++ b/com/android/internal/telephony/uicc/AdnRecord.java
@@ -19,8 +19,8 @@ package com.android.internal.telephony.uicc;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
import android.telephony.Rlog;
+import android.text.TextUtils;
import com.android.internal.telephony.GsmAlphabet;
@@ -248,7 +248,8 @@ public class AdnRecord implements Parcelable {
Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
return null;
} else {
- bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber);
+ bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+ mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
System.arraycopy(bcdNumber, 0, adnString,
footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
@@ -289,7 +290,10 @@ public class AdnRecord implements Parcelable {
}
mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
- extRecord, 2, 0xff & extRecord[1]);
+ extRecord,
+ 2,
+ 0xff & extRecord[1],
+ PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
// We don't support ext record chaining.
@@ -327,7 +331,10 @@ public class AdnRecord implements Parcelable {
// the ME (see note 2)."
mNumber = PhoneNumberUtils.calledPartyBCDToString(
- record, footerOffset + 1, numberLength);
+ record,
+ footerOffset + 1,
+ numberLength,
+ PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
mExtRecord = 0xff & record[record.length - 1];
diff --git a/com/android/internal/telephony/uicc/SIMRecords.java b/com/android/internal/telephony/uicc/SIMRecords.java
index 724b4781..dad1ee2b 100644
--- a/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/com/android/internal/telephony/uicc/SIMRecords.java
@@ -563,7 +563,8 @@ public class SIMRecords extends IccRecords {
// Spec reference for EF_CFIS contents, TS 51.011 section 10.3.46.
if (enable && !TextUtils.isEmpty(dialNumber)) {
logv("EF_CFIS: updating cf number, " + Rlog.pii(LOG_TAG, dialNumber));
- byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(dialNumber);
+ byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+ dialNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
System.arraycopy(bcdNumber, 0, mEfCfis, CFIS_TON_NPI_OFFSET, bcdNumber.length);
diff --git a/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index e50f40c0..bfa458b8 100644
--- a/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -54,7 +54,10 @@ public class UiccCarrierPrivilegeRules extends Handler {
private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
private static final boolean DBG = false;
- private static final String AID = "A00000015141434C00";
+ private static final String ARAM_AID = "A00000015141434C00";
+ private static final String ARAD_AID = "A00000015144414300";
+ private static final int ARAM = 1;
+ private static final int ARAD = 0;
private static final int CLA = 0x80;
private static final int COMMAND = 0xCA;
private static final int P1 = 0xFF;
@@ -184,18 +187,21 @@ public class UiccCarrierPrivilegeRules extends Handler {
private String mStatusMessage; // Only used for debugging.
private int mChannelId; // Channel Id for communicating with UICC.
private int mRetryCount; // Number of retries for open logical channel.
+ private boolean mCheckedRules = false; // Flag that used to mark whether get rules from ARA-D.
+ private int mAIDInUse; // Message component to identify which AID is currently in-use.
private final Runnable mRetryRunnable = new Runnable() {
@Override
public void run() {
- openChannel();
+ openChannel(mAIDInUse);
}
};
- private void openChannel() {
+ private void openChannel(int aidId) {
// Send open logical channel request.
+ String aid = (aidId == ARAD) ? ARAD_AID : ARAM_AID;
int p2 = 0x00;
- mUiccCard.iccOpenLogicalChannel(AID, p2, /* supported p2 value */
- obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+ mUiccCard.iccOpenLogicalChannel(aid, p2, /* supported p2 value */
+ obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, 0, aidId, null));
}
public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
@@ -207,7 +213,9 @@ public class UiccCarrierPrivilegeRules extends Handler {
mRules = "";
mAccessRules = new ArrayList<>();
- openChannel();
+ // Open logical channel with ARA_D.
+ mAIDInUse = ARAD;
+ openChannel(mAIDInUse);
}
/**
@@ -391,6 +399,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
+ mAIDInUse = msg.arg2; // 0 means ARA-D and 1 means ARA-M.
switch (msg.what) {
@@ -400,23 +409,34 @@ public class UiccCarrierPrivilegeRules extends Handler {
if (ar.exception == null && ar.result != null) {
mChannelId = ((int[]) ar.result)[0];
mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3,
- DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
- mChannelId));
+ DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId,
+ mAIDInUse));
} else {
// MISSING_RESOURCE could be due to logical channels temporarily unavailable,
// so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY
&& ((CommandException) (ar.exception)).getCommandError()
- == CommandException.Error.MISSING_RESOURCE) {
+ == CommandException.Error.MISSING_RESOURCE) {
mRetryCount++;
removeCallbacks(mRetryRunnable);
postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
} else {
- // if rules cannot be read from ARA applet,
- // fallback to PKCS15-based ARF.
- log("No ARA, try ARF next.");
- mUiccPkcs15 = new UiccPkcs15(mUiccCard,
- obtainMessage(EVENT_PKCS15_READ_DONE));
+ if (mAIDInUse == ARAD) {
+ // Open logical channel with ARA_M.
+ mRules = "";
+ openChannel(1);
+ }
+ if (mAIDInUse == ARAM) {
+ if (mCheckedRules) {
+ updateState(STATE_LOADED, "Success!");
+ } else {
+ // if rules cannot be read from both ARA_D and ARA_M applet,
+ // fallback to PKCS15-based ARF.
+ log("No ARA, try ARF next.");
+ mUiccPkcs15 = new UiccPkcs15(mUiccCard,
+ obtainMessage(EVENT_PKCS15_READ_DONE));
+ }
+ }
}
}
break;
@@ -432,34 +452,49 @@ public class UiccCarrierPrivilegeRules extends Handler {
mRules += IccUtils.bytesToHexString(response.payload)
.toUpperCase(Locale.US);
if (isDataComplete()) {
- mAccessRules = parseRules(mRules);
- updateState(STATE_LOADED, "Success!");
+ mAccessRules.addAll(parseRules(mRules));
+ if (mAIDInUse == ARAD) {
+ mCheckedRules = true;
+ } else {
+ updateState(STATE_LOADED, "Success!");
+ }
} else {
mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
P1, P2_EXTENDED_DATA, P3, DATA,
obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
- mChannelId));
+ mChannelId, mAIDInUse));
break;
}
} catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
- updateState(STATE_ERROR, "Error parsing rules: " + ex);
+ if (mAIDInUse == ARAM) {
+ updateState(STATE_ERROR, "Error parsing rules: " + ex);
+ }
}
} else {
- String errorMsg = "Invalid response: payload=" + response.payload
- + " sw1=" + response.sw1 + " sw2=" + response.sw2;
- updateState(STATE_ERROR, errorMsg);
+ if (mAIDInUse == ARAM) {
+ String errorMsg = "Invalid response: payload=" + response.payload
+ + " sw1=" + response.sw1 + " sw2=" + response.sw2;
+ updateState(STATE_ERROR, errorMsg);
+ }
}
} else {
- updateState(STATE_ERROR, "Error reading value from SIM.");
+ if (mAIDInUse == ARAM) {
+ updateState(STATE_ERROR, "Error reading value from SIM.");
+ }
}
mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
- EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
+ EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse));
mChannelId = -1;
break;
case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
+ if (mAIDInUse == ARAD) {
+ // Close logical channel with ARA_D and then open logical channel with ARA_M.
+ mRules = "";
+ openChannel(1);
+ }
break;
case EVENT_PKCS15_READ_DONE:
@@ -492,7 +527,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
String lengthBytes = allRules.parseLength(mRules);
log("isDataComplete lengthBytes: " + lengthBytes);
if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
- allRules.length) {
+ allRules.length) {
log("isDataComplete yes");
return true;
} else {
@@ -522,7 +557,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
if (accessRule != null) {
accessRules.add(accessRule);
} else {
- Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
+ Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
}
}
return accessRules;
@@ -644,15 +679,15 @@ public class UiccCarrierPrivilegeRules extends Handler {
* Converts state into human readable format.
*/
private String getStateString(int state) {
- switch (state) {
- case STATE_LOADING:
- return "STATE_LOADING";
- case STATE_LOADED:
- return "STATE_LOADED";
- case STATE_ERROR:
- return "STATE_ERROR";
- default:
- return "UNKNOWN";
- }
+ switch (state) {
+ case STATE_LOADING:
+ return "STATE_LOADING";
+ case STATE_LOADED:
+ return "STATE_LOADED";
+ case STATE_ERROR:
+ return "STATE_ERROR";
+ default:
+ return "UNKNOWN";
+ }
}
-}
+} \ No newline at end of file
diff --git a/com/android/internal/util/BitUtils.java b/com/android/internal/util/BitUtils.java
index 28f12ebf..ba80aeae 100644
--- a/com/android/internal/util/BitUtils.java
+++ b/com/android/internal/util/BitUtils.java
@@ -93,6 +93,10 @@ public final class BitUtils {
return s & 0xffff;
}
+ public static int uint16(byte hi, byte lo) {
+ return ((hi & 0xff) << 8) | (lo & 0xff);
+ }
+
public static long uint32(int i) {
return i & 0xffffffffL;
}
diff --git a/com/android/internal/util/RingBuffer.java b/com/android/internal/util/RingBuffer.java
new file mode 100644
index 00000000..ad84353f
--- /dev/null
+++ b/com/android/internal/util/RingBuffer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static com.android.internal.util.Preconditions.checkArgumentPositive;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+/**
+ * A simple ring buffer structure with bounded capacity backed by an array.
+ * Events can always be added at the logical end of the buffer. If the buffer is
+ * full, oldest events are dropped when new events are added.
+ * {@hide}
+ */
+public class RingBuffer<T> {
+
+ // Array for storing events.
+ private final T[] mBuffer;
+ // Cursor keeping track of the logical end of the array. This cursor never
+ // wraps and instead keeps track of the total number of append() operations.
+ private long mCursor = 0;
+
+ public RingBuffer(Class<T> c, int capacity) {
+ checkArgumentPositive(capacity, "A RingBuffer cannot have 0 capacity");
+ // Java cannot create generic arrays without a runtime hint.
+ mBuffer = (T[]) Array.newInstance(c, capacity);
+ }
+
+ public int size() {
+ return (int) Math.min(mBuffer.length, (long) mCursor);
+ }
+
+ public void append(T t) {
+ mBuffer[indexOf(mCursor++)] = t;
+ }
+
+ public T[] toArray() {
+ // Only generic way to create a T[] from another T[]
+ T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass());
+ // Reverse iteration from youngest event to oldest event.
+ long inCursor = mCursor - 1;
+ int outIdx = out.length - 1;
+ while (outIdx >= 0) {
+ out[outIdx--] = (T) mBuffer[indexOf(inCursor--)];
+ }
+ return out;
+ }
+
+ private int indexOf(long cursor) {
+ return (int) Math.abs(cursor % mBuffer.length);
+ }
+}
diff --git a/com/android/internal/widget/LinearLayoutManager.java b/com/android/internal/widget/LinearLayoutManager.java
index d82c7466..0000a74f 100644
--- a/com/android/internal/widget/LinearLayoutManager.java
+++ b/com/android/internal/widget/LinearLayoutManager.java
@@ -168,10 +168,6 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". Defaults to vertical orientation.
- *
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
*/
public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
diff --git a/com/android/internal/widget/LockPatternUtils.java b/com/android/internal/widget/LockPatternUtils.java
index b8ef82b8..4be6b28a 100644
--- a/com/android/internal/widget/LockPatternUtils.java
+++ b/com/android/internal/widget/LockPatternUtils.java
@@ -303,7 +303,7 @@ public class LockPatternUtils {
}
public void reportFailedPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
@@ -311,7 +311,7 @@ public class LockPatternUtils {
}
public void reportSuccessfulPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
@@ -319,21 +319,21 @@ public class LockPatternUtils {
}
public void reportPasswordLockout(int timeoutMs, int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getTrustManager().reportUnlockLockout(timeoutMs, userId);
}
public int getCurrentFailedPasswordAttempts(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return 0;
}
return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
}
public int getMaximumFailedPasswordsForWipe(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return 0;
}
return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
@@ -1774,11 +1774,12 @@ public class LockPatternUtils {
return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
}
- public static boolean userOwnsFrpCredential(UserInfo info) {
- return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled();
+ public static boolean userOwnsFrpCredential(Context context, UserInfo info) {
+ return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(context);
}
- public static boolean frpCredentialEnabled() {
- return FRP_CREDENTIAL_ENABLED;
+ public static boolean frpCredentialEnabled(Context context) {
+ return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
}
}
diff --git a/com/android/internal/widget/Magnifier.java b/com/android/internal/widget/Magnifier.java
new file mode 100644
index 00000000..86e7b38a
--- /dev/null
+++ b/com/android/internal/widget/Magnifier.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.PixelCopy;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Android magnifier widget. Can be used by any view which is attached to window.
+ */
+public final class Magnifier {
+ private static final String LOG_TAG = "magnifier";
+ // The view for which this magnifier is attached.
+ private final View mView;
+ // The window containing the magnifier.
+ private final PopupWindow mWindow;
+ // The center coordinates of the window containing the magnifier.
+ private final Point mWindowCoords = new Point();
+ // The width of the window containing the magnifier.
+ private final int mWindowWidth;
+ // The height of the window containing the magnifier.
+ private final int mWindowHeight;
+ // The bitmap used to display the contents of the magnifier.
+ private final Bitmap mBitmap;
+ // The center coordinates of the content that is to be magnified.
+ private final Point mCenterZoomCoords = new Point();
+ // The callback of the pixel copy request will be invoked on this Handler when
+ // the copy is finished.
+ private final Handler mPixelCopyHandler = Handler.getMain();
+
+ /**
+ * Initializes a magnifier.
+ *
+ * @param view the view for which this magnifier is attached
+ */
+ @UiThread
+ public Magnifier(@NonNull View view) {
+ mView = Preconditions.checkNotNull(view);
+ final Context context = mView.getContext();
+ final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+ mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
+ mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
+ final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
+
+ mWindow = new PopupWindow(context);
+ mWindow.setContentView(content);
+ mWindow.setWidth(mWindowWidth);
+ mWindow.setHeight(mWindowHeight);
+ mWindow.setElevation(elevation);
+ mWindow.setTouchable(false);
+ mWindow.setBackgroundDrawable(null);
+
+ mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888);
+ getImageView().setImageBitmap(mBitmap);
+ }
+
+ /**
+ * Shows the magnifier on the screen.
+ *
+ * @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=10) float scale) {
+ maybeResizeBitmap(scale);
+ configureCoordinates(centerXOnScreen, centerYOnScreen);
+ performPixelCopy();
+
+ if (mWindow.isShowing()) {
+ mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+ mWindow.getHeight());
+ } else {
+ mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
+ mWindowCoords.x, mWindowCoords.y);
+ }
+ }
+
+ /**
+ * Dismisses the magnifier from the screen.
+ */
+ public void dismiss() {
+ mWindow.dismiss();
+ }
+
+ /**
+ * @return the height of the magnifier window.
+ */
+ public int getHeight() {
+ return mWindowHeight;
+ }
+
+ /**
+ * @return the width of the magnifier window.
+ */
+ public int getWidth() {
+ return mWindowWidth;
+ }
+
+ private void maybeResizeBitmap(float scale) {
+ final int bitmapWidth = (int) (mWindowWidth / scale);
+ final int bitmapHeight = (int) (mWindowHeight / scale);
+ 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) {
+ mCenterZoomCoords.x = (int) posXOnScreen;
+ mCenterZoomCoords.y = (int) posYOnScreen;
+
+ 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 * (availableTopSpace > 0 ? -1 : 1);
+ }
+
+ private void performPixelCopy() {
+ int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
+ // Clamp startX value to avoid distorting the rendering of the magnifier content.
+ if (startX < 0) {
+ startX = 0;
+ } else if (startX + mBitmap.getWidth() > mView.getWidth()) {
+ startX = mView.getWidth() - mBitmap.getWidth();
+ }
+
+ final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
+ final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
+
+ if (viewRootImpl != null && viewRootImpl.mSurface != null
+ && viewRootImpl.mSurface.isValid()) {
+ PixelCopy.request(
+ viewRootImpl.mSurface,
+ new Rect(startX, startY, startX + mBitmap.getWidth(),
+ startY + mBitmap.getHeight()),
+ mBitmap,
+ result -> getImageView().invalidate(),
+ mPixelCopyHandler);
+ } else {
+ Log.d(LOG_TAG, "Could not perform PixelCopy request");
+ }
+ }
+
+ private ImageView getImageView() {
+ return mWindow.getContentView().findViewById(R.id.magnifier_image);
+ }
+}
diff --git a/com/android/keyguard/KeyguardDisplayManager.java b/com/android/keyguard/KeyguardDisplayManager.java
index 8de1d317..2bc0e45c 100644
--- a/com/android/keyguard/KeyguardDisplayManager.java
+++ b/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.app.Presentation;
import android.content.Context;
import android.content.DialogInterface;
@@ -28,16 +30,21 @@ import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+// TODO(multi-display): Support multiple external displays
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
private static boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private final ViewMediatorCallback mCallback;
+ private final MediaRouter mMediaRouter;
+ private final Context mContext;
+
Presentation mPresentation;
- private MediaRouter mMediaRouter;
- private Context mContext;
private boolean mShowing;
- public KeyguardDisplayManager(Context context) {
+ public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
mContext = context;
+ mCallback = callback;
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
}
@@ -90,6 +97,7 @@ public class KeyguardDisplayManager {
};
protected void updateDisplays(boolean showing) {
+ Presentation originalPresentation = mPresentation;
if (showing) {
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
@@ -121,6 +129,13 @@ public class KeyguardDisplayManager {
mPresentation = null;
}
}
+
+ // mPresentation is only updated when the display changes
+ if (mPresentation != originalPresentation) {
+ final int displayId = mPresentation != null
+ ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;
+ mCallback.onSecondaryDisplayShowingChanged(displayId);
+ }
}
private final static class KeyguardPresentation extends Presentation {
diff --git a/com/android/keyguard/KeyguardSimPinView.java b/com/android/keyguard/KeyguardSimPinView.java
index 7225ba99..432b4061 100644
--- a/com/android/keyguard/KeyguardSimPinView.java
+++ b/com/android/keyguard/KeyguardSimPinView.java
@@ -66,7 +66,11 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
// again when the PUK locked SIM is re-entered.
case ABSENT: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // onSimStateChanged callback can fire when the SIM PIN lock is not currently
+ // active and mCallback is null.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/com/android/keyguard/KeyguardSimPukView.java b/com/android/keyguard/KeyguardSimPukView.java
index 171cf236..7f79008b 100644
--- a/com/android/keyguard/KeyguardSimPukView.java
+++ b/com/android/keyguard/KeyguardSimPukView.java
@@ -72,7 +72,11 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
// move into the READY state and the PUK lock keyguard should be removed.
case READY: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // mCallback can be null if onSimStateChanged callback is called when keyguard
+ // isn't active.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/com/android/keyguard/KeyguardUpdateMonitor.java b/com/android/keyguard/KeyguardUpdateMonitor.java
index d95402cd..d83a6c60 100644
--- a/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
@@ -452,6 +454,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
*/
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
+ updateFingerprintListeningState();
}
/**
@@ -1069,6 +1072,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
cb.onDreamingStateChanged(mIsDreaming);
}
}
+ updateFingerprintListeningState();
}
/**
@@ -1772,7 +1776,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
public void onTaskStackChangedBackground() {
try {
ActivityManager.StackInfo info = ActivityManager.getService().getStackInfo(
- ActivityManager.StackId.ASSISTANT_STACK_ID);
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
if (info == null) {
return;
}
diff --git a/com/android/keyguard/ViewMediatorCallback.java b/com/android/keyguard/ViewMediatorCallback.java
index 327d2189..b194de43 100644
--- a/com/android/keyguard/ViewMediatorCallback.java
+++ b/com/android/keyguard/ViewMediatorCallback.java
@@ -88,4 +88,9 @@ public interface ViewMediatorCallback {
* {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
*/
int getBouncerPromptReason();
+
+ /**
+ * Invoked when the secondary display showing a keyguard window changes.
+ */
+ void onSecondaryDisplayShowingChanged(int displayId);
}
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/layoutlib/bridge/android/BridgeContext.java b/com/android/layoutlib/bridge/android/BridgeContext.java
index 4c6c9d48..4a75be98 100644
--- a/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,7 +40,6 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Notification;
import android.app.SystemServiceRegistry_Accessor;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -86,7 +85,6 @@ import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.Display;
import android.view.DisplayAdjustments;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -610,45 +608,35 @@ public class BridgeContext extends Context {
@Override
public Object getSystemService(String service) {
- if (LAYOUT_INFLATER_SERVICE.equals(service)) {
- return mBridgeInflater;
- }
-
- if (TEXT_SERVICES_MANAGER_SERVICE.equals(service)) {
- // we need to return a valid service to avoid NPE
- return TextServicesManager.getInstance();
- }
+ switch (service) {
+ case LAYOUT_INFLATER_SERVICE:
+ return mBridgeInflater;
- if (WINDOW_SERVICE.equals(service)) {
- return mWindowManager;
- }
-
- // needed by SearchView
- if (INPUT_METHOD_SERVICE.equals(service)) {
- return null;
- }
+ case TEXT_SERVICES_MANAGER_SERVICE:
+ // we need to return a valid service to avoid NPE
+ return TextServicesManager.getInstance();
- if (POWER_SERVICE.equals(service)) {
- return new PowerManager(this, new BridgePowerManager(), new Handler());
- }
+ case WINDOW_SERVICE:
+ return mWindowManager;
- if (DISPLAY_SERVICE.equals(service)) {
- return mDisplayManager;
- }
+ case POWER_SERVICE:
+ return new PowerManager(this, new BridgePowerManager(), new Handler());
- if (ACCESSIBILITY_SERVICE.equals(service)) {
- return AccessibilityManager.getInstance(this);
- }
+ case DISPLAY_SERVICE:
+ return mDisplayManager;
- if (AUTOFILL_MANAGER_SERVICE.equals(service)) {
- return null;
- }
+ case ACCESSIBILITY_SERVICE:
+ return AccessibilityManager.getInstance(this);
- if (AUDIO_SERVICE.equals(service)) {
- return null;
+ case INPUT_METHOD_SERVICE: // needed by SearchView
+ case AUTOFILL_MANAGER_SERVICE:
+ case AUDIO_SERVICE:
+ case TEXT_CLASSIFICATION_SERVICE:
+ return null;
+ default:
+ assert false : "Unsupported Service: " + service;
}
- assert false : "Unsupported Service: " + service;
return null;
}
@@ -657,13 +645,13 @@ public class BridgeContext extends Context {
return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
}
- @Override
- public final BridgeTypedArray obtainStyledAttributes(int[] attrs) {
- return obtainStyledAttributes(0, attrs);
- }
- @Override
- public final BridgeTypedArray obtainStyledAttributes(int resId, int[] attrs)
+ /**
+ * Same as Context#obtainStyledAttributes. We do not override the base method to give the
+ * original Context the chance to override the theme when needed.
+ */
+ @Nullable
+ public final BridgeTypedArray internalObtainStyledAttributes(int resId, int[] attrs)
throws Resources.NotFoundException {
StyleResourceValue style = null;
// get the StyleResourceValue based on the resId;
@@ -715,13 +703,12 @@ public class BridgeContext extends Context {
return typeArrayAndPropertiesPair.getFirst();
}
- @Override
- public final BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
- return obtainStyledAttributes(set, attrs, 0, 0);
- }
-
- @Override
- public BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
+ /**
+ * Same as Context#obtainStyledAttributes. We do not override the base method to give the
+ * original Context the chance to override the theme when needed.
+ */
+ @Nullable
+ public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs,
int defStyleAttr, int defStyleRes) {
PropertiesMap defaultPropMap = null;
diff --git a/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index cdcf0ea1..bc77685e 100644
--- a/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -26,6 +26,7 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,11 +34,18 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.View;
import android.widget.FrameLayout;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.List;
+
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.resources.ResourceType.MENU;
/**
@@ -50,6 +58,7 @@ public class AppCompatActionBar extends BridgeActionBar {
private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
// This is used on v23.1.1 and later.
private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar";
+
private Class<?> mWindowActionBarClass;
/**
@@ -90,6 +99,7 @@ public class AppCompatActionBar extends BridgeActionBar {
constructorParams, constructorArgs);
mWindowActionBarClass = mWindowDecorActionBar == null ? null :
mWindowDecorActionBar.getClass();
+ inflateMenus();
setupActionBar();
} catch (Exception e) {
Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
@@ -165,6 +175,51 @@ public class AppCompatActionBar extends BridgeActionBar {
}
}
+ private void inflateMenus() {
+ List<String> menuNames = getCallBack().getMenuIdNames();
+ if (menuNames.isEmpty()) {
+ return;
+ }
+
+ if (menuNames.size() > 1) {
+ // Supporting multiple menus means that we would need to instantiate our own supportlib
+ // MenuInflater instances using reflection
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Support Toolbar does not currently support multiple menus in the preview.",
+ null, null, null);
+ }
+
+ String name = menuNames.get(0);
+ int id;
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ // Framework menu.
+ name = name.substring(ANDROID_NS_NAME_PREFIX.length());
+ id = mBridgeContext.getFrameworkResourceValue(MENU, name, -1);
+ } else {
+ // Project menu.
+ id = mBridgeContext.getProjectResourceValue(MENU, name, -1);
+ }
+ if (id < 1) {
+ return;
+ }
+ // Get toolbar decorator
+ Object mDecorToolbar = getFieldValue(mWindowDecorActionBar, "mDecorToolbar");
+ if (mDecorToolbar == null) {
+ return;
+ }
+
+ Class<?> mDecorToolbarClass = mDecorToolbar.getClass();
+ Context themedContext = (Context)invoke(
+ getMethod(mWindowActionBarClass, "getThemedContext"),
+ mWindowDecorActionBar);
+ MenuInflater inflater = new MenuInflater(themedContext);
+ Menu menuBuilder = (Menu)invoke(getMethod(mDecorToolbarClass, "getMenu"), mDecorToolbar);
+ inflater.inflate(id, menuBuilder);
+
+ // Set the actual menu
+ invoke(findMethod(mDecorToolbarClass, "setMenu"), mDecorToolbar, menuBuilder, null);
+ }
+
@Override
public void createMenuPopup() {
// it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection.
@@ -181,13 +236,53 @@ public class AppCompatActionBar extends BridgeActionBar {
return null;
}
+ /**
+ * Same as getMethod but doesn't require the parameterTypes. This allows us to call methods
+ * without having to get all the types for the parameters when we do not need them
+ */
@Nullable
- private static Object invoke(Method method, Object owner, Object... args) {
+ private static Method findMethod(@Nullable Class<?> owner, @NotNull String name) {
+ if (owner == null) {
+ return null;
+ }
+ for (Method method : owner.getMethods()) {
+ if (name.equals(method.getName())) {
+ return method;
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private static Object getFieldValue(@Nullable Object instance, @NotNull String name) {
+ if (instance == null) {
+ return null;
+ }
+
+ Class<?> instanceClass = instance.getClass();
try {
- return method == null ? null : method.invoke(owner, args);
- } catch (InvocationTargetException e) {
+ Field field = instanceClass.getDeclaredField(name);
+ boolean accesible = field.isAccessible();
+ if (!accesible) {
+ field.setAccessible(true);
+ }
+ try {
+ return field.get(instance);
+ } finally {
+ field.setAccessible(accesible);
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
- } catch (IllegalAccessException e) {
+ }
+ return null;
+ }
+
+ @Nullable
+ private static Object invoke(@Nullable Method method, Object owner, Object... args) {
+ try {
+ return method == null ? null : method.invoke(owner, args);
+ } catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
diff --git a/com/android/layoutlib/bridge/impl/GcSnapshot.java b/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 3ad859c6..7526e090 100644
--- a/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -19,7 +19,6 @@ package com.android.layoutlib.bridge.impl;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
-import android.annotation.NonNull;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
import android.graphics.ColorFilter_Delegate;
@@ -40,13 +39,11 @@ import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
-import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
import java.util.ArrayList;
/**
@@ -69,7 +66,7 @@ public class GcSnapshot {
private final int mFlags;
/** list of layers. The first item in the list is always the */
- private final ArrayList<Layer> mLayers = new ArrayList<>();
+ private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
/** temp transform in case transformation are set before a Graphics2D exists */
private AffineTransform mTransform = null;
@@ -85,13 +82,6 @@ public class GcSnapshot {
private final Layer mLocalLayer;
private final Paint_Delegate mLocalLayerPaint;
private final Rect mLayerBounds;
- /**
- * Cached buffer to be used for tinting operations. This buffer is usually used many times
- * and there is no need to creating it every time.
- */
- private SoftReference<BufferedImage> mCachedLayerBuffer = new SoftReference<>(null);
- private Rectangle2D.Float mCachedClipRect = new Rectangle2D.Float();
-
public interface Drawable {
void draw(Graphics2D graphics, Paint_Delegate paint);
@@ -310,11 +300,12 @@ public class GcSnapshot {
Layer baseLayer = mLayers.get(0);
// create the image for the layer
- BufferedImage layerImage =
- baseLayer.getGraphics().getDeviceConfiguration().createCompatibleImage(
- baseLayer.getImage().getWidth(), baseLayer.getImage().getHeight(),
- (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
- Transparency.TRANSLUCENT : Transparency.OPAQUE);
+ BufferedImage layerImage = new BufferedImage(
+ baseLayer.getImage().getWidth(),
+ baseLayer.getImage().getHeight(),
+ (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
+ BufferedImage.TYPE_INT_ARGB :
+ BufferedImage.TYPE_INT_RGB);
// create a graphics for it so that drawing can be done.
Graphics2D layerGraphics = layerImage.createGraphics();
@@ -361,8 +352,6 @@ public class GcSnapshot {
}
public void dispose() {
- mCachedLayerBuffer.clear();
-
for (Layer layer : mLayers) {
layer.getGraphics().dispose();
}
@@ -538,12 +527,7 @@ public class GcSnapshot {
}
public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
- if (mCachedClipRect == null) {
- mCachedClipRect = new Rectangle2D.Float(left, top, right - left, bottom - top);
- } else {
- mCachedClipRect.setRect(left, top, right - left, bottom - top);
- }
- return clip(mCachedClipRect, regionOp);
+ return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
}
/**
@@ -656,16 +640,17 @@ public class GcSnapshot {
height = layer.getImage().getHeight();
}
- // get a Graphics2D object configured with the drawing parameters, but no shader.
- Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
- true /*compositeOnly*/, forceMode);
-
- // Create or re-use a temporary image to which the color filter will be applied.
- BufferedImage image = getTemporaryBuffer(configuredGraphics, width, height);
+ // Create a temporary image to which the color filter will be applied.
+ BufferedImage image = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
// Configure the Graphics2D object with drawing parameters and shader.
- Graphics2D imageGraphics = createCustomGraphics(imageBaseGraphics, paint, compositeOnly,
+ Graphics2D imageGraphics = createCustomGraphics(
+ imageBaseGraphics, paint, compositeOnly,
AlphaComposite.SRC_OVER);
+ // get a Graphics2D object configured with the drawing parameters, but no shader.
+ Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
+ true /*compositeOnly*/, forceMode);
try {
// The main draw operation.
// We translate the operation to take into account that the rendering does not
@@ -692,28 +677,6 @@ public class GcSnapshot {
}
}
- /**
- * Returns a temporary buffer sized width * height and configured with the given
- * {@link Graphics2D} device configuration.
- */
- @NonNull
- private BufferedImage getTemporaryBuffer(@NonNull Graphics2D configuredGraphics,
- int width, int height) {
- BufferedImage cachedImage = mCachedLayerBuffer.get();
- if (cachedImage == null ||
- width > cachedImage.getWidth() || height > cachedImage.getHeight() ||
- !configuredGraphics.getDeviceConfiguration().getColorModel()
- .isCompatibleSampleModel(cachedImage.getSampleModel())) {
- // The current cached image is not valid or does not exist
- cachedImage = configuredGraphics.getDeviceConfiguration()
- .createCompatibleImage(width, height);
- mCachedLayerBuffer = new SoftReference<>(cachedImage);
- } else {
- cachedImage = cachedImage.getSubimage(0, 0, width, height);
- }
- return cachedImage;
- }
-
private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
Layer layer) {
try {
diff --git a/com/android/providers/settings/SettingsProtoDumpUtil.java b/com/android/providers/settings/SettingsProtoDumpUtil.java
index ec6f8319..67fb4d9f 100644
--- a/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -720,9 +720,6 @@ class SettingsProtoDumpUtil {
Settings.Global.DEVICE_IDLE_CONSTANTS,
GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
dumpSetting(s, p,
- Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
- GlobalSettingsProto.DEVICE_IDLE_CONSTANTS_WATCH);
- dumpSetting(s, p,
Settings.Global.APP_IDLE_CONSTANTS,
GlobalSettingsProto.APP_IDLE_CONSTANTS);
dumpSetting(s, p,
diff --git a/com/android/providers/settings/SettingsProvider.java b/com/android/providers/settings/SettingsProvider.java
index a463db6f..36f9b840 100644
--- a/com/android/providers/settings/SettingsProvider.java
+++ b/com/android/providers/settings/SettingsProvider.java
@@ -2896,7 +2896,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 149;
+ private static final int SETTINGS_VERSION = 150;
private final int mUserId;
@@ -3470,9 +3470,25 @@ public class SettingsProvider extends ContentProvider {
true, SettingsState.SYSTEM_PACKAGE_NAME);
}
}
-
currentVersion = 149;
}
+
+ if (currentVersion == 149) {
+ // Version 150: Set a default value for mobile data always on
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting currentSetting = globalSettings.getSettingLocked(
+ Settings.Global.MOBILE_DATA_ALWAYS_ON);
+ if (currentSetting.isNull()) {
+ globalSettings.insertSettingLocked(
+ Settings.Global.MOBILE_DATA_ALWAYS_ON,
+ getContext().getResources().getBoolean(
+ R.bool.def_mobile_data_always_on) ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 150;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/com/android/server/BatteryService.java b/com/android/server/BatteryService.java
index 83bd9ebd..5106c8d7 100644
--- a/com/android/server/BatteryService.java
+++ b/com/android/server/BatteryService.java
@@ -20,6 +20,7 @@ import android.app.ActivityManagerInternal;
import android.database.ContentObserver;
import android.os.BatteryStats;
+import android.os.PowerManager;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
@@ -291,6 +292,8 @@ public final class BatteryService extends SystemService {
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.putExtra(Intent.EXTRA_REASON,
+ PowerManager.SHUTDOWN_LOW_BATTERY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
@@ -310,6 +313,8 @@ public final class BatteryService extends SystemService {
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.putExtra(Intent.EXTRA_REASON,
+ PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
diff --git a/com/android/server/ConnectivityService.java b/com/android/server/ConnectivityService.java
index bfe50404..348c7997 100644
--- a/com/android/server/ConnectivityService.java
+++ b/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@ package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -90,6 +91,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -128,7 +130,6 @@ import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.MockableSystemProperties;
-import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkMonitor;
@@ -781,6 +782,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworksDefined++; // used only in the log() statement below.
}
+ // Do the same for Ethernet, since it's often not specified in the configs, although many
+ // devices can use it via USB host adapters.
+ if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
+ mNetworksDefined++;
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -2205,7 +2213,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
}
@@ -2282,9 +2290,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
- NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+ NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
- mNetworkForRequestId.remove(request.requestId);
+ clearNetworkForRequest(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
@@ -2360,7 +2368,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
rematchAllNetworksAndRequests(null, 0);
- if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
+ if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
}
}
@@ -2415,7 +2423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ getNetworkForRequest(nri.request.requestId).getCurrentScore() <
nai.getCurrentScoreAsValidated())) {
return false;
}
@@ -2442,7 +2450,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (mNetworkRequests.get(nri.request) == null) {
return;
}
- if (mNetworkForRequestId.get(nri.request.requestId) != null) {
+ if (getNetworkForRequest(nri.request.requestId) != null) {
return;
}
if (VDBG || (DBG && nri.request.isRequest())) {
@@ -2482,7 +2490,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
if (nai != null) {
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
nai.removeRequest(nri.request.requestId);
@@ -2499,7 +2507,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
wasKept = true;
}
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -4296,7 +4304,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
* and the are the highest scored network available.
* the are keyed off the Requests requestId.
*/
- // TODO: Yikes, this is accessed on multiple threads: add synchronization.
+ // NOTE: Accessed on multiple threads, must be synchronized on itself.
+ @GuardedBy("mNetworkForRequestId")
private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
new SparseArray<NetworkAgentInfo>();
@@ -4326,8 +4335,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
+ private NetworkAgentInfo getNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ return mNetworkForRequestId.get(requestId);
+ }
+ }
+
+ private void clearNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.remove(requestId);
+ }
+ }
+
+ private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.put(requestId, nai);
+ }
+ }
+
private NetworkAgentInfo getDefaultNetwork() {
- return mNetworkForRequestId.get(mDefaultRequest.requestId);
+ return getNetworkForRequest(mDefaultRequest.requestId);
}
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
@@ -4881,7 +4908,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// requests or not, and doesn't affect the network's score.
if (nri.request.isListen()) continue;
- final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId);
final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) {
if (VDBG) {
@@ -4913,7 +4940,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG) log(" accepting network in place of null");
}
newNetwork.unlingerRequest(nri.request);
- mNetworkForRequestId.put(nri.request.requestId, newNetwork);
+ setNetworkForRequest(nri.request.requestId, newNetwork);
if (!newNetwork.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
}
@@ -4947,7 +4974,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
newNetwork.removeRequest(nri.request.requestId);
if (currentNetwork == newNetwork) {
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
@@ -5522,6 +5549,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
}
+ @VisibleForTesting
+ public boolean hasService(String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
diff --git a/com/android/server/DeviceIdleController.java b/com/android/server/DeviceIdleController.java
index abbc89e4..2d9baf61 100644
--- a/com/android/server/DeviceIdleController.java
+++ b/com/android/server/DeviceIdleController.java
@@ -307,6 +307,12 @@ public class DeviceIdleController extends SystemService
*/
private int[] mTempWhitelistAppIdArray = new int[0];
+ /**
+ * Apps in the system whitelist that have been taken out (probably because the user wanted to).
+ * They can be restored back by calling restoreAppToSystemWhitelist(String).
+ */
+ private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+
private static final int EVENT_NULL = 0;
private static final int EVENT_NORMAL = 1;
private static final int EVENT_LIGHT_IDLE = 2;
@@ -760,17 +766,15 @@ public class DeviceIdleController extends SystemService
public long NOTIFICATION_WHITELIST_DURATION;
private final ContentResolver mResolver;
- private final boolean mHasWatch;
+ private final boolean mSmallBatteryDevice;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler, ContentResolver resolver) {
super(handler);
mResolver = resolver;
- mHasWatch = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WATCH);
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
- : Settings.Global.DEVICE_IDLE_CONSTANTS),
+ mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
false, this);
updateConstants();
}
@@ -784,8 +788,7 @@ public class DeviceIdleController extends SystemService
synchronized (DeviceIdleController.this) {
try {
mParser.setString(Settings.Global.getString(mResolver,
- mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
- : Settings.Global.DEVICE_IDLE_CONSTANTS));
+ Settings.Global.DEVICE_IDLE_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -815,7 +818,7 @@ public class DeviceIdleController extends SystemService
MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
- long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
+ long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
@@ -825,7 +828,7 @@ public class DeviceIdleController extends SystemService
LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
- long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
+ long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? idleAfterInactiveTimeout
: (idleAfterInactiveTimeout / 10));
@@ -1162,6 +1165,38 @@ public class DeviceIdleController extends SystemService
}
}
+ @Override public void removeSystemPowerWhitelistApp(String name) {
+ if (DEBUG) {
+ Slog.d(TAG, "removeAppFromSystemWhitelist(name = " + name + ")");
+ }
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeSystemPowerWhitelistAppInternal(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override public void restoreSystemPowerWhitelistApp(String name) {
+ if (DEBUG) {
+ Slog.d(TAG, "restoreAppToSystemWhitelist(name = " + name + ")");
+ }
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ restoreSystemPowerWhitelistAppInternal(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public String[] getRemovedSystemPowerWhitelistApps() {
+ return getRemovedSystemPowerWhitelistAppsInternal();
+ }
+
@Override public String[] getSystemPowerWhitelistExceptIdle() {
return getSystemPowerWhitelistExceptIdleInternal();
}
@@ -1504,6 +1539,42 @@ public class DeviceIdleController extends SystemService
}
}
+ void resetSystemPowerWhitelistInternal() {
+ synchronized (this) {
+ mPowerSaveWhitelistApps.putAll(mRemovedFromSystemWhitelistApps);
+ mRemovedFromSystemWhitelistApps.clear();
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ }
+ }
+
+ public boolean restoreSystemPowerWhitelistAppInternal(String name) {
+ synchronized (this) {
+ if (!mRemovedFromSystemWhitelistApps.containsKey(name)) {
+ return false;
+ }
+ mPowerSaveWhitelistApps.put(name, mRemovedFromSystemWhitelistApps.remove(name));
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ return true;
+ }
+ }
+
+ public boolean removeSystemPowerWhitelistAppInternal(String name) {
+ synchronized (this) {
+ if (!mPowerSaveWhitelistApps.containsKey(name)) {
+ return false;
+ }
+ mRemovedFromSystemWhitelistApps.put(name, mPowerSaveWhitelistApps.remove(name));
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ return true;
+ }
+ }
+
public boolean addPowerSaveWhitelistExceptIdleInternal(String name) {
synchronized (this) {
try {
@@ -1565,6 +1636,17 @@ public class DeviceIdleController extends SystemService
}
}
+ public String[] getRemovedSystemPowerWhitelistAppsInternal() {
+ synchronized (this) {
+ int size = mRemovedFromSystemWhitelistApps.size();
+ final String[] apps = new String[size];
+ for (int i = 0; i < size; i++) {
+ apps[i] = mRemovedFromSystemWhitelistApps.keyAt(i);
+ }
+ return apps;
+ }
+ }
+
public String[] getUserPowerWhitelistInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistUserApps.size();
@@ -2481,21 +2563,31 @@ public class DeviceIdleController extends SystemService
}
String tagName = parser.getName();
- if (tagName.equals("wl")) {
- String name = parser.getAttributeValue(null, "n");
- if (name != null) {
- try {
- ApplicationInfo ai = pm.getApplicationInfo(name,
- PackageManager.MATCH_ANY_USER);
- mPowerSaveWhitelistUserApps.put(ai.packageName,
- UserHandle.getAppId(ai.uid));
- } catch (PackageManager.NameNotFoundException e) {
+ switch (tagName) {
+ case "wl":
+ String name = parser.getAttributeValue(null, "n");
+ if (name != null) {
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(name,
+ PackageManager.MATCH_ANY_USER);
+ mPowerSaveWhitelistUserApps.put(ai.packageName,
+ UserHandle.getAppId(ai.uid));
+ } catch (PackageManager.NameNotFoundException e) {
+ }
}
- }
- } else {
- Slog.w(TAG, "Unknown element under <config>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
+ break;
+ case "un-wl":
+ final String packageName = parser.getAttributeValue(null, "n");
+ if (mPowerSaveWhitelistApps.containsKey(packageName)) {
+ mRemovedFromSystemWhitelistApps.put(packageName,
+ mPowerSaveWhitelistApps.remove(packageName));
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unknown element under <config>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ break;
}
}
@@ -2556,6 +2648,11 @@ public class DeviceIdleController extends SystemService
out.attribute(null, "n", name);
out.endTag(null, "wl");
}
+ for (int i = 0; i < mRemovedFromSystemWhitelistApps.size(); i++) {
+ out.startTag(null, "un-wl");
+ out.attribute(null, "n", mRemovedFromSystemWhitelistApps.keyAt(i));
+ out.endTag(null, "un-wl");
+ }
out.endTag(null, "config");
out.endDocument();
}
@@ -2584,6 +2681,13 @@ public class DeviceIdleController extends SystemService
pw.println(" Print currently whitelisted apps.");
pw.println(" whitelist [package ...]");
pw.println(" Add (prefix with +) or remove (prefix with -) packages.");
+ pw.println(" sys-whitelist [package ...|reset]");
+ pw.println(" Prefix the package with '-' to remove it from the system whitelist or '+'"
+ + " to put it back in the system whitelist.");
+ pw.println(" Note that only packages that were"
+ + " earlier removed from the system whitelist can be added back.");
+ pw.println(" reset will reset the whitelist to the original state");
+ pw.println(" Prints the system whitelist if no arguments are specified");
pw.println(" except-idle-whitelist [package ...|reset]");
pw.println(" Prefix the package with '+' to add it to whitelist or "
+ "'=' to check if it is already whitelisted");
@@ -2944,6 +3048,50 @@ public class DeviceIdleController extends SystemService
} finally {
Binder.restoreCallingIdentity(token);
}
+ } else if ("sys-whitelist".equals(cmd)) {
+ String arg = shell.getNextArg();
+ if (arg != null) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(arg)) {
+ resetSystemPowerWhitelistInternal();
+ } else {
+ do {
+ if (arg.length() < 1
+ || (arg.charAt(0) != '-' && arg.charAt(0) != '+')) {
+ pw.println("Package must be prefixed with + or - " + arg);
+ return -1;
+ }
+ final char op = arg.charAt(0);
+ final String pkg = arg.substring(1);
+ switch (op) {
+ case '+':
+ if (restoreSystemPowerWhitelistAppInternal(pkg)) {
+ pw.println("Restored " + pkg);
+ }
+ break;
+ case '-':
+ if (removeSystemPowerWhitelistAppInternal(pkg)) {
+ pw.println("Removed " + pkg);
+ }
+ break;
+ }
+ } while ((arg = shell.getNextArg()) != null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ synchronized (this) {
+ for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+ pw.print(mPowerSaveWhitelistApps.keyAt(j));
+ pw.print(",");
+ pw.println(mPowerSaveWhitelistApps.valueAt(j));
+ }
+ }
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
@@ -3027,6 +3175,14 @@ public class DeviceIdleController extends SystemService
pw.println(mPowerSaveWhitelistApps.keyAt(i));
}
}
+ size = mRemovedFromSystemWhitelistApps.size();
+ if (size > 0) {
+ pw.println(" Removed from whitelist system apps:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.println(mRemovedFromSystemWhitelistApps.keyAt(i));
+ }
+ }
size = mPowerSaveWhitelistUserApps.size();
if (size > 0) {
pw.println(" Whitelist user apps:");
diff --git a/com/android/server/DiskStatsService.java b/com/android/server/DiskStatsService.java
index 800081e5..2d2c6b0b 100644
--- a/com/android/server/DiskStatsService.java
+++ b/com/android/server/DiskStatsService.java
@@ -202,6 +202,8 @@ public class DiskStatsService extends Binder {
JSONObject json = new JSONObject(jsonString);
pw.print("App Size: ");
pw.println(json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+ pw.print("App Data Size: ");
+ pw.println(json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY));
pw.print("App Cache Size: ");
pw.println(json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
pw.print("Photos Size: ");
@@ -220,6 +222,8 @@ public class DiskStatsService extends Binder {
pw.println(json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY));
pw.print("App Sizes: ");
pw.println(json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY));
+ pw.print("App Data Sizes: ");
+ pw.println(json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY));
pw.print("Cache Sizes: ");
pw.println(json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY));
} catch (IOException | JSONException e) {
@@ -235,6 +239,8 @@ public class DiskStatsService extends Binder {
proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE,
json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+ proto.write(DiskStatsCachedValuesProto.AGG_APPS_DATA_SIZE,
+ json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY));
proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE,
json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE,
@@ -252,22 +258,26 @@ public class DiskStatsService extends Binder {
JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+ JSONArray appDataSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
final int len = packageNamesArray.length();
- if (len == appSizesArray.length() && len == cacheSizesArray.length()) {
+ if (len == appSizesArray.length()
+ && len == appDataSizesArray.length()
+ && len == cacheSizesArray.length()) {
for (int i = 0; i < len; i++) {
long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES);
proto.write(DiskStatsAppSizesProto.PACKAGE_NAME,
packageNamesArray.getString(i));
proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i));
+ proto.write(DiskStatsAppSizesProto.APP_DATA_SIZE, appDataSizesArray.getLong(i));
proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i));
proto.end(packageToken);
}
} else {
- Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray "
- + "are not the same");
+ Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray, appDataSizesArray "
+ + " and cacheSizesArray are not the same");
}
proto.end(cachedValuesToken);
diff --git a/com/android/server/IpSecService.java b/com/android/server/IpSecService.java
index 30568313..2e1f142a 100644
--- a/com/android/server/IpSecService.java
+++ b/com/android/server/IpSecService.java
@@ -33,6 +33,7 @@ import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse;
+import android.net.NetworkUtils;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
@@ -42,11 +43,14 @@ import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -54,6 +58,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicInteger;
+
import libcore.io.IoUtils;
/** @hide */
@@ -252,7 +257,11 @@ public class IpSecService extends IIpSecService.Stub {
return (mReferenceCount.get() > 0);
}
- public void checkOwnerOrSystemAndThrow() {
+ /**
+ * Ensures that the caller is either the owner of this resource or has the system UID and
+ * throws a SecurityException otherwise.
+ */
+ public void checkOwnerOrSystem() {
if (uid != Binder.getCallingUid()
&& android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
throw new SecurityException("Only the owner may access managed resources!");
@@ -335,12 +344,12 @@ public class IpSecService extends IIpSecService.Stub {
private class ManagedResourceArray<T extends ManagedResource> {
SparseArray<T> mArray = new SparseArray<>();
- T get(int key) {
+ T getAndCheckOwner(int key) {
T val = mArray.get(key);
// The value should never be null unless the resource doesn't exist
// (since we do not allow null resources to be added).
if (val != null) {
- val.checkOwnerOrSystemAndThrow();
+ val.checkOwnerOrSystem();
}
return val;
}
@@ -405,12 +414,8 @@ public class IpSecService extends IIpSecService.Stub {
.ipSecDeleteSecurityAssociation(
mResourceId,
direction,
- (mConfig.getLocalAddress() != null)
- ? mConfig.getLocalAddress().getHostAddress()
- : "",
- (mConfig.getRemoteAddress() != null)
- ? mConfig.getRemoteAddress().getHostAddress()
- : "",
+ mConfig.getLocalAddress(),
+ mConfig.getRemoteAddress(),
spi);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -638,11 +643,45 @@ public class IpSecService extends IIpSecService.Stub {
}
}
+ /**
+ * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
+ * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
+ */
+ private static void checkInetAddress(String inetAddress) {
+ if (TextUtils.isEmpty(inetAddress)) {
+ throw new IllegalArgumentException("Unspecified address");
+ }
+
+ InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
+
+ if (checkAddr.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
+ }
+ }
+
+ /**
+ * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
+ * DIRECTION_IN or DIRECTION_OUT
+ */
+ private static void checkDirection(int direction) {
+ switch (direction) {
+ case IpSecTransform.DIRECTION_OUT:
+ case IpSecTransform.DIRECTION_IN:
+ return;
+ }
+ throw new IllegalArgumentException("Invalid Direction: " + direction);
+ }
+
@Override
/** Get a new SPI and maintain the reservation in the system server */
public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
int direction, String remoteAddress, int requestedSpi, IBinder binder)
throws RemoteException {
+ checkDirection(direction);
+ checkInetAddress(remoteAddress);
+ /* requestedSpi can be anything in the int range, so no check is needed. */
+ checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
+
int resourceId = mNextResourceId.getAndIncrement();
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -651,9 +690,7 @@ public class IpSecService extends IIpSecService.Stub {
try {
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
return new IpSecSpiResponse(
- IpSecManager.Status.RESOURCE_UNAVAILABLE,
- INVALID_RESOURCE_ID,
- spi);
+ IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
spi =
mSrvConfig
@@ -686,7 +723,7 @@ public class IpSecService extends IIpSecService.Stub {
throws RemoteException {
// We want to non-destructively get so that we can check credentials before removing
// this from the records.
- T record = resArray.get(resourceId);
+ T record = resArray.getAndCheckOwner(resourceId);
if (record == null) {
throw new IllegalArgumentException(
@@ -751,6 +788,8 @@ public class IpSecService extends IIpSecService.Stub {
throw new IllegalArgumentException(
"Specified port number must be a valid non-reserved UDP port");
}
+ checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+
int resourceId = mNextResourceId.getAndIncrement();
FileDescriptor sockFd = null;
try {
@@ -792,6 +831,68 @@ public class IpSecService extends IIpSecService.Stub {
}
/**
+ * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
+ * IllegalArgumentException if they are not.
+ */
+ private void checkIpSecConfig(IpSecConfig config) {
+ if (config.getLocalAddress() == null) {
+ throw new IllegalArgumentException("Invalid null Local InetAddress");
+ }
+
+ if (config.getRemoteAddress() == null) {
+ throw new IllegalArgumentException("Invalid null Remote InetAddress");
+ }
+
+ switch (config.getMode()) {
+ case IpSecTransform.MODE_TRANSPORT:
+ if (!config.getLocalAddress().isEmpty()) {
+ throw new IllegalArgumentException("Non-empty Local Address");
+ }
+ // Must be valid, and not a wildcard
+ checkInetAddress(config.getRemoteAddress());
+ break;
+ case IpSecTransform.MODE_TUNNEL:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid IpSecTransform.mode: " + config.getMode());
+ }
+
+ switch (config.getEncapType()) {
+ case IpSecTransform.ENCAP_NONE:
+ break;
+ case IpSecTransform.ENCAP_ESPINUDP:
+ case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
+ if (mUdpSocketRecords.getAndCheckOwner(
+ config.getEncapSocketResourceId()) == null) {
+ throw new IllegalStateException(
+ "No Encapsulation socket for Resource Id: "
+ + config.getEncapSocketResourceId());
+ }
+
+ int port = config.getEncapRemotePort();
+ if (port <= 0 || port > 0xFFFF) {
+ throw new IllegalArgumentException("Invalid remote UDP port: " + port);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
+ }
+
+ for (int direction : DIRECTIONS) {
+ IpSecAlgorithm crypt = config.getEncryption(direction);
+ IpSecAlgorithm auth = config.getAuthentication(direction);
+ if (crypt == null && auth == null) {
+ throw new IllegalArgumentException("Encryption and Authentication are both null");
+ }
+
+ if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
+ throw new IllegalStateException("No SPI for specified Resource Id");
+ }
+ }
+ }
+
+ /**
* Create a transport mode transform, which represent two security associations (one in each
* direction) in the kernel. The transform will be cached by the system server and must be freed
* when no longer needed. It is possible to free one, deleting the SA from underneath sockets
@@ -801,17 +902,19 @@ public class IpSecService extends IIpSecService.Stub {
@Override
public synchronized IpSecTransformResponse createTransportModeTransform(
IpSecConfig c, IBinder binder) throws RemoteException {
+ checkIpSecConfig(c);
+ checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
int resourceId = mNextResourceId.getAndIncrement();
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
- // TODO: Basic input validation here since it's coming over the Binder
+
int encapType, encapLocalPort = 0, encapRemotePort = 0;
UdpSocketRecord socketRecord = null;
encapType = c.getEncapType();
if (encapType != IpSecTransform.ENCAP_NONE) {
- socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId());
+ socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
encapLocalPort = socketRecord.getPort();
encapRemotePort = c.getEncapRemotePort();
}
@@ -820,23 +923,18 @@ public class IpSecService extends IIpSecService.Stub {
IpSecAlgorithm auth = c.getAuthentication(direction);
IpSecAlgorithm crypt = c.getEncryption(direction);
- spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction));
+ spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
int spi = spis[direction].getSpi();
try {
- mSrvConfig.getNetdInstance()
+ mSrvConfig
+ .getNetdInstance()
.ipSecAddSecurityAssociation(
resourceId,
c.getMode(),
direction,
- (c.getLocalAddress() != null)
- ? c.getLocalAddress().getHostAddress()
- : "",
- (c.getRemoteAddress() != null)
- ? c.getRemoteAddress().getHostAddress()
- : "",
- (c.getNetwork() != null)
- ? c.getNetwork().getNetworkHandle()
- : 0,
+ c.getLocalAddress(),
+ c.getRemoteAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
spi,
(auth != null) ? auth.getName() : "",
(auth != null) ? auth.getKey() : null,
@@ -879,7 +977,7 @@ public class IpSecService extends IIpSecService.Stub {
// Synchronize liberally here because we are using ManagedResources in this block
TransformRecord info;
// FIXME: this code should be factored out into a security check + getter
- info = mTransformRecords.get(resourceId);
+ info = mTransformRecords.getAndCheckOwner(resourceId);
if (info == null) {
throw new IllegalArgumentException("Transform " + resourceId + " is not active");
@@ -899,12 +997,8 @@ public class IpSecService extends IIpSecService.Stub {
socket.getFileDescriptor(),
resourceId,
direction,
- (c.getLocalAddress() != null)
- ? c.getLocalAddress().getHostAddress()
- : "",
- (c.getRemoteAddress() != null)
- ? c.getRemoteAddress().getHostAddress()
- : "",
+ c.getLocalAddress(),
+ c.getRemoteAddress(),
info.getSpiRecord(direction).getSpi());
}
} catch (ServiceSpecificException e) {
diff --git a/com/android/server/LocationManagerService.java b/com/android/server/LocationManagerService.java
index 340d672d..0fd59eaa 100644
--- a/com/android/server/LocationManagerService.java
+++ b/com/android/server/LocationManagerService.java
@@ -18,7 +18,6 @@ package com.android.server;
import android.app.ActivityManager;
import android.annotation.NonNull;
-import android.content.pm.PackageManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.content.PackageMonitor;
@@ -56,6 +55,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
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.ResolveInfo;
import android.content.pm.Signature;
diff --git a/com/android/server/NetworkManagementService.java b/com/android/server/NetworkManagementService.java
index 2f95aa2c..ba3afc31 100644
--- a/com/android/server/NetworkManagementService.java
+++ b/com/android/server/NetworkManagementService.java
@@ -1140,17 +1140,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void setInterfaceIpv6NdOffload(String iface, boolean enable) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute(
- "interface", "ipv6ndoffload", iface, (enable ? "enable" : "disable"));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void addRoute(int netId, RouteInfo route) {
modifyRoute("add", "" + netId, route);
}
@@ -1991,8 +1980,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub
final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
+ final boolean useTls = false;
+ final String tlsHostname = "";
+ final String[] tlsFingerprints = new String[0];
try {
- mNetdService.setResolverConfiguration(netId, servers, domainStrs, params);
+ mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
+ useTls, tlsHostname, tlsFingerprints);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/com/android/server/StorageManagerService.java b/com/android/server/StorageManagerService.java
index c0fcfd07..55391b3e 100644
--- a/com/android/server/StorageManagerService.java
+++ b/com/android/server/StorageManagerService.java
@@ -111,8 +111,6 @@ import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.NativeDaemonConnector.Command;
-import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.pm.PackageManagerService;
import com.android.server.storage.AppFuseBridge;
@@ -138,7 +136,6 @@ import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -161,8 +158,7 @@ import javax.crypto.spec.PBEKeySpec;
* watch for and manage dynamically added storage, such as SD cards and USB mass
* storage. Also decides how storage should be presented to users on the device.
*/
-class StorageManagerService extends IStorageManager.Stub
- implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
+class StorageManagerService extends IStorageManager.Stub implements Watchdog.Monitor {
// Static direct instance pointer for the tightly-coupled idle service to use
static StorageManagerService sSelf = null;
@@ -206,18 +202,12 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- /** Flag to enable binder-based interface to vold */
- private static final boolean ENABLE_BINDER = true;
-
private static final boolean DEBUG_EVENTS = false;
private static final boolean DEBUG_OBB = false;
// Disable this since it messes up long-running cryptfs operations.
private static final boolean WATCHDOG_ENABLE = false;
- /** Flag to enable ASECs */
- private static final boolean ASEC_ENABLE = false;
-
/**
* Our goal is for all Android devices to be usable as development devices,
* which includes the new Direct Boot mode added in N. For devices that
@@ -232,66 +222,9 @@ class StorageManagerService extends IStorageManager.Stub
private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
private static final String TAG_STORAGE_TRIM = "storage_trim";
- private static final String VOLD_TAG = "VoldConnector";
- private static final String CRYPTD_TAG = "CryptdConnector";
-
- /** Maximum number of ASEC containers allowed to be mounted. */
- private static final int MAX_CONTAINERS = 250;
-
/** Magic value sent by MoveTask.cpp */
private static final int MOVE_STATUS_COPY_FINISHED = 82;
- /*
- * Internal vold response code constants
- */
- class VoldResponseCode {
- /*
- * 100 series - Requestion action was initiated; expect another reply
- * before proceeding with a new command.
- */
- public static final int VolumeListResult = 110;
- public static final int AsecListResult = 111;
- public static final int StorageUsersListResult = 112;
- public static final int CryptfsGetfieldResult = 113;
-
- /*
- * 200 series - Requestion action has been successfully completed.
- */
- public static final int ShareStatusResult = 210;
- public static final int AsecPathResult = 211;
- public static final int ShareEnabledResult = 212;
-
- /*
- * 400 series - Command was accepted, but the requested action
- * did not take place.
- */
- public static final int OpFailedNoMedia = 401;
- public static final int OpFailedMediaBlank = 402;
- public static final int OpFailedMediaCorrupt = 403;
- public static final int OpFailedVolNotMounted = 404;
- public static final int OpFailedStorageBusy = 405;
- public static final int OpFailedStorageNotFound = 406;
-
- /*
- * 600 series - Unsolicited broadcasts.
- */
- public static final int DISK_CREATED = 640;
- public static final int DISK_SIZE_CHANGED = 641;
- public static final int DISK_LABEL_CHANGED = 642;
- public static final int DISK_SCANNED = 643;
- public static final int DISK_SYS_PATH_CHANGED = 644;
- public static final int DISK_DESTROYED = 649;
-
- public static final int VOLUME_CREATED = 650;
- public static final int VOLUME_STATE_CHANGED = 651;
- public static final int VOLUME_FS_TYPE_CHANGED = 652;
- public static final int VOLUME_FS_UUID_CHANGED = 653;
- public static final int VOLUME_FS_LABEL_CHANGED = 654;
- public static final int VOLUME_PATH_CHANGED = 655;
- public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
- public static final int VOLUME_DESTROYED = 659;
- }
-
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_PRIMARY = 2;
private static final int VERSION_FIX_PRIMARY = 3;
@@ -453,17 +386,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private static String escapeNull(String arg) {
- if (TextUtils.isEmpty(arg)) {
- return "!";
- } else {
- if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
- throw new IllegalArgumentException(arg);
- }
- return arg;
- }
- }
-
/** List of crypto types.
* These must match CRYPT_TYPE_XXX in cryptfs.h AND their
* corresponding commands in CommandListener.cpp */
@@ -472,12 +394,6 @@ class StorageManagerService extends IStorageManager.Stub
private final Context mContext;
- private final NativeDaemonConnector mConnector;
- private final NativeDaemonConnector mCryptConnector;
-
- private final Thread mConnectorThread;
- private final Thread mCryptConnectorThread;
-
private volatile IVold mVold;
private volatile boolean mSystemReady = false;
@@ -489,20 +405,6 @@ class StorageManagerService extends IStorageManager.Stub
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
- // Two connectors - mConnector & mCryptConnector
- private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
- private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
-
- private final Object mUnmountLock = new Object();
- @GuardedBy("mUnmountLock")
- private CountDownLatch mUnmountSignal;
-
- /**
- * Private hash of currently mounted secure containers.
- * Used as a lock in methods to manipulate secure containers.
- */
- final private HashSet<String> mAsecMountSet = new HashSet<String>();
-
/**
* The size of the crypto algorithm key in bits for OBB files. Currently
* Twofish is used which takes 128-bit keys.
@@ -616,7 +518,7 @@ class StorageManagerService extends IStorageManager.Stub
if (DEBUG_OBB)
Slog.i(TAG, "onServiceDisconnected");
}
- };
+ }
// Used in the ObbActionHandler
private IMediaContainerService mContainerService = null;
@@ -655,13 +557,6 @@ class StorageManagerService extends IStorageManager.Stub
break;
}
case H_FSTRIM: {
- if (!isReady()) {
- Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
- sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj),
- DateUtils.SECOND_IN_MILLIS);
- break;
- }
-
Slog.i(TAG, "Running fstrim idle maintenance");
// Remember when we kicked it off
@@ -687,12 +582,8 @@ class StorageManagerService extends IStorageManager.Stub
final IStorageShutdownObserver obs = (IStorageShutdownObserver) msg.obj;
boolean success = false;
try {
- if (ENABLE_BINDER) {
- mVold.shutdown();
- success = true;
- } else {
- success = mConnector.execute("volume", "shutdown").isClassOk();
- }
+ mVold.shutdown();
+ success = true;
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -711,12 +602,7 @@ class StorageManagerService extends IStorageManager.Stub
break;
}
try {
- if (ENABLE_BINDER) {
- mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
- } else {
- mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
- vol.mountUserId);
- }
+ mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -778,11 +664,7 @@ class StorageManagerService extends IStorageManager.Stub
if (Intent.ACTION_USER_ADDED.equals(action)) {
final UserManager um = mContext.getSystemService(UserManager.class);
final int userSerialNumber = um.getUserSerialNumber(userId);
- if (ENABLE_BINDER) {
- mVold.onUserAdded(userId, userSerialNumber);
- } else {
- mConnector.execute("volume", "user_added", userId, userSerialNumber);
- }
+ mVold.onUserAdded(userId, userSerialNumber);
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
synchronized (mVolumes) {
final int size = mVolumes.size();
@@ -794,11 +676,7 @@ class StorageManagerService extends IStorageManager.Stub
}
}
}
- if (ENABLE_BINDER) {
- mVold.onUserRemoved(userId);
- } else {
- mConnector.execute("volume", "user_removed", userId);
- }
+ mVold.onUserRemoved(userId);
}
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -806,22 +684,6 @@ class StorageManagerService extends IStorageManager.Stub
}
};
- @Override
- public void waitForAsecScan() {
- waitForLatch(mAsecsScanned, "mAsecsScanned");
- }
-
- private void waitForReady() {
- waitForLatch(mConnectedSignal, "mConnectedSignal");
- }
-
- private void waitForLatch(CountDownLatch latch, String condition) {
- try {
- waitForLatch(latch, condition, -1);
- } catch (TimeoutException ignored) {
- }
- }
-
private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
throws TimeoutException {
final long startMillis = SystemClock.elapsedRealtime();
@@ -843,14 +705,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private boolean isReady() {
- try {
- return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return false;
- }
- }
-
private void handleSystemReady() {
initIfReadyAndConnected();
resetIfReadyAndConnected();
@@ -917,19 +771,10 @@ class StorageManagerService extends IStorageManager.Stub
for (UserInfo user : users) {
try {
if (initLocked) {
- if (ENABLE_BINDER) {
- mVold.lockUserKey(user.id);
- } else {
- mCryptConnector.execute("cryptfs", "lock_user_key", user.id);
- }
+ mVold.lockUserKey(user.id);
} else {
- if (ENABLE_BINDER) {
- mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null),
- encodeBytes(null));
- } else {
- mCryptConnector.execute("cryptfs", "unlock_user_key", user.id,
- user.serialNumber, "!", "!");
- }
+ mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null),
+ encodeBytes(null));
}
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -956,26 +801,14 @@ class StorageManagerService extends IStorageManager.Stub
}
try {
- if (ENABLE_BINDER) {
- mVold.reset();
- } else {
- mConnector.execute("volume", "reset");
- }
+ mVold.reset();
// Tell vold about all existing and started users
for (UserInfo user : users) {
- if (ENABLE_BINDER) {
- mVold.onUserAdded(user.id, user.serialNumber);
- } else {
- mConnector.execute("volume", "user_added", user.id, user.serialNumber);
- }
+ mVold.onUserAdded(user.id, user.serialNumber);
}
for (int userId : systemUnlockedUsers) {
- if (ENABLE_BINDER) {
- mVold.onUserStarted(userId);
- } else {
- mConnector.execute("volume", "user_started", userId);
- }
+ mVold.onUserStarted(userId);
}
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -990,11 +823,7 @@ class StorageManagerService extends IStorageManager.Stub
// staging area is ready so it's ready for zygote-forked apps to
// bind mount against.
try {
- if (ENABLE_BINDER) {
- mVold.onUserStarted(userId);
- } else {
- mConnector.execute("volume", "user_started", userId);
- }
+ mVold.onUserStarted(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1020,11 +849,7 @@ class StorageManagerService extends IStorageManager.Stub
Slog.d(TAG, "onCleanupUser " + userId);
try {
- if (ENABLE_BINDER) {
- mVold.onUserStopped(userId);
- } else {
- mConnector.execute("volume", "user_stopped", userId);
- }
+ mVold.onUserStopped(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1050,10 +875,6 @@ class StorageManagerService extends IStorageManager.Stub
return mLastMaintenance;
}
- /**
- * Callback from NativeDaemonConnector
- */
- @Override
public void onDaemonConnected() {
mDaemonConnected = true;
mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
@@ -1063,29 +884,11 @@ class StorageManagerService extends IStorageManager.Stub
initIfReadyAndConnected();
resetIfReadyAndConnected();
- /*
- * Now that we've done our initialization, release
- * the hounds!
- */
- mConnectedSignal.countDown();
- if (mConnectedSignal.getCount() != 0) {
- // More daemons need to connect
- return;
- }
-
// On an encrypted device we can't see system properties yet, so pull
// the system locale out of the mount service.
if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
copyLocaleFromMountService();
}
-
- // Let package manager load internal ASECs.
- if (ASEC_ENABLE) {
- mPms.scanAvailableAsecs();
- }
-
- // Notify people waiting for ASECs to be scanned that it's done.
- mAsecsScanned.countDown();
}
private void copyLocaleFromMountService() {
@@ -1114,148 +917,6 @@ class StorageManagerService extends IStorageManager.Stub
SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
}
- /**
- * Callback from NativeDaemonConnector
- */
- @Override
- public boolean onCheckHoldWakeLock(int code) {
- return false;
- }
-
- /**
- * Callback from NativeDaemonConnector
- */
- @Override
- public boolean onEvent(int code, String raw, String[] cooked) {
- synchronized (mLock) {
- try {
- return onEventLocked(code, raw, cooked);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
- }
-
- private boolean onEventLocked(int code, String raw, String[] cooked) throws RemoteException {
- switch (code) {
- case VoldResponseCode.DISK_CREATED: {
- if (cooked.length != 3) break;
- final String diskId = cooked[1];
- final int flags = Integer.parseInt(cooked[2]);
- mListener.onDiskCreated(diskId, flags);
- break;
- }
- case VoldResponseCode.DISK_SIZE_CHANGED: {
- if (cooked.length != 3) break;
- final DiskInfo disk = mDisks.get(cooked[1]);
- if (disk != null) {
- disk.size = Long.parseLong(cooked[2]);
- }
- break;
- }
- case VoldResponseCode.DISK_LABEL_CHANGED: {
- final DiskInfo disk = mDisks.get(cooked[1]);
- if (disk != null) {
- final StringBuilder builder = new StringBuilder();
- for (int i = 2; i < cooked.length; i++) {
- builder.append(cooked[i]).append(' ');
- }
- disk.label = builder.toString().trim();
- }
- break;
- }
- case VoldResponseCode.DISK_SCANNED: {
- if (cooked.length != 2) break;
- final String diskId = cooked[1];
- mListener.onDiskScanned(diskId);
- break;
- }
- case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
- if (cooked.length != 3) break;
- final DiskInfo disk = mDisks.get(cooked[1]);
- if (disk != null) {
- disk.sysPath = cooked[2];
- }
- break;
- }
- case VoldResponseCode.DISK_DESTROYED: {
- if (cooked.length != 2) break;
- final String diskId = cooked[1];
- mListener.onDiskDestroyed(diskId);
- break;
- }
-
- case VoldResponseCode.VOLUME_CREATED: {
- final String volId = cooked[1];
- final int type = Integer.parseInt(cooked[2]);
- final String diskId = TextUtils.nullIfEmpty(cooked[3]);
- final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
- mListener.onVolumeCreated(volId, type, diskId, partGuid);
- break;
- }
- case VoldResponseCode.VOLUME_STATE_CHANGED: {
- if (cooked.length != 3) break;
- final String volId = cooked[1];
- final int state = Integer.parseInt(cooked[2]);
- mListener.onVolumeStateChanged(volId, state);
- break;
- }
- case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
- if (cooked.length != 3) break;
- final VolumeInfo vol = mVolumes.get(cooked[1]);
- if (vol != null) {
- vol.fsType = cooked[2];
- }
- break;
- }
- case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
- if (cooked.length != 3) break;
- final VolumeInfo vol = mVolumes.get(cooked[1]);
- if (vol != null) {
- vol.fsUuid = cooked[2];
- }
- break;
- }
- case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
- final VolumeInfo vol = mVolumes.get(cooked[1]);
- if (vol != null) {
- final StringBuilder builder = new StringBuilder();
- for (int i = 2; i < cooked.length; i++) {
- builder.append(cooked[i]).append(' ');
- }
- vol.fsLabel = builder.toString().trim();
- }
- // TODO: notify listeners that label changed
- break;
- }
- case VoldResponseCode.VOLUME_PATH_CHANGED: {
- if (cooked.length != 3) break;
- final String volId = cooked[1];
- final String path = cooked[2];
- mListener.onVolumePathChanged(volId, path);
- break;
- }
- case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
- if (cooked.length != 3) break;
- final String volId = cooked[1];
- final String internalPath = cooked[2];
- mListener.onVolumeInternalPathChanged(volId, internalPath);
- break;
- }
- case VoldResponseCode.VOLUME_DESTROYED: {
- if (cooked.length != 2) break;
- final String volId = cooked[1];
- mListener.onVolumeDestroyed(volId);
- break;
- }
- default: {
- Slog.d(TAG, "Unhandled vold event " + code);
- }
- }
-
- return true;
- }
-
private final IVoldListener mListener = new IVoldListener.Stub() {
@Override
public void onDiskCreated(String diskId, int flags) {
@@ -1654,24 +1315,6 @@ class StorageManagerService extends IStorageManager.Stub
LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal);
- /*
- * Create the connection to vold with a maximum queue of twice the
- * amount of containers we'd ever expect to have. This keeps an
- * "asec list" from blocking a thread repeatedly.
- */
-
- mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
- null);
- mConnector.setDebug(true);
- mConnector.setWarnIfHeld(mLock);
- mConnectorThread = new Thread(mConnector, VOLD_TAG);
-
- // Reuse parameters from first connector since they are tested and safe
- mCryptConnector = new NativeDaemonConnector(this, "cryptd",
- MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
- mCryptConnector.setDebug(true);
- mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
-
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1689,8 +1332,6 @@ class StorageManagerService extends IStorageManager.Stub
private void start() {
connect();
- mConnectorThread.start();
- mCryptConnectorThread.start();
}
private void connect() {
@@ -1713,6 +1354,7 @@ class StorageManagerService extends IStorageManager.Stub
mVold = IVold.Stub.asInterface(binder);
try {
mVold.setListener(mListener);
+ onDaemonConnected();
return;
} catch (RemoteException e) {
Slog.w(TAG, "vold listener rejected; trying again", e);
@@ -1864,62 +1506,15 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
- public boolean isUsbMassStorageConnected() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setUsbMassStorageEnabled(boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isUsbMassStorageEnabled() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getVolumeState(String mountPoint) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isExternalStorageEmulated() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int mountVolume(String path) {
- mount(findVolumeIdForPathOrThrow(path));
- return 0;
- }
-
- @Override
- public void unmountVolume(String path, boolean force, boolean removeEncryption) {
- unmount(findVolumeIdForPathOrThrow(path));
- }
-
- @Override
- public int formatVolume(String path) {
- format(findVolumeIdForPathOrThrow(path));
- return 0;
- }
-
- @Override
public void mount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
if (isMountDisallowed(vol)) {
throw new SecurityException("Mounting " + volId + " restricted by policy");
}
try {
- if (ENABLE_BINDER) {
- mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
- } else {
- mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
- }
+ mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1928,31 +1523,10 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void unmount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
-
- // TODO: expand PMS to know about multiple volumes
- if (vol.isPrimaryPhysical()) {
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mUnmountLock) {
- mUnmountSignal = new CountDownLatch(1);
- mPms.updateExternalMediaStatus(false, true);
- waitForLatch(mUnmountSignal, "mUnmountSignal");
- mUnmountSignal = null;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
try {
- if (ENABLE_BINDER) {
- mVold.unmount(vol.id);
- } else {
- mConnector.execute("volume", "unmount", vol.id);
- }
+ mVold.unmount(vol.id);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1961,15 +1535,10 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void format(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
- waitForReady();
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
try {
- if (ENABLE_BINDER) {
- mVold.format(vol.id, "auto");
- } else {
- mConnector.execute("volume", "format", vol.id, "auto");
- }
+ mVold.format(vol.id, "auto");
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1978,7 +1547,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public long benchmark(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
- waitForReady();
// TODO: refactor for callers to provide a listener
try {
@@ -2022,15 +1590,10 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void partitionPublic(String diskId) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
- waitForReady();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
try {
- if (ENABLE_BINDER) {
- mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
- } else {
- mConnector.execute("volume", "partition", diskId, "public");
- }
+ mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -2041,15 +1604,10 @@ class StorageManagerService extends IStorageManager.Stub
public void partitionPrivate(String diskId) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
enforceAdminUser();
- waitForReady();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
try {
- if (ENABLE_BINDER) {
- mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
- } else {
- mConnector.execute("volume", "partition", diskId, "private");
- }
+ mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -2060,15 +1618,10 @@ class StorageManagerService extends IStorageManager.Stub
public void partitionMixed(String diskId, int ratio) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
enforceAdminUser();
- waitForReady();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
try {
- if (ENABLE_BINDER) {
- mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
- } else {
- mConnector.execute("volume", "partition", diskId, "mixed", ratio);
- }
+ mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -2078,7 +1631,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void setVolumeNickname(String fsUuid, String nickname) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
Preconditions.checkNotNull(fsUuid);
synchronized (mLock) {
@@ -2092,7 +1644,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
Preconditions.checkNotNull(fsUuid);
synchronized (mLock) {
@@ -2106,7 +1657,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void forgetVolume(String fsUuid) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
Preconditions.checkNotNull(fsUuid);
@@ -2131,7 +1681,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void forgetAllVolumes() {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
synchronized (mLock) {
for (int i = 0; i < mRecords.size(); i++) {
@@ -2155,11 +1704,7 @@ class StorageManagerService extends IStorageManager.Stub
private void forgetPartition(String partGuid) {
try {
- if (ENABLE_BINDER) {
- mVold.forgetPartition(partGuid);
- } else {
- mConnector.execute("volume", "forget_partition", partGuid);
- }
+ mVold.forgetPartition(partGuid);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -2168,14 +1713,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void fstrim(int flags) {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
- waitForReady();
-
- String cmd;
- if ((flags & StorageManager.FSTRIM_FLAG_DEEP) != 0) {
- cmd = "dodtrim";
- } else {
- cmd = "dotrim";
- }
try {
mVold.fstrim(flags, new IVoldTaskListener.Stub() {
@@ -2212,27 +1749,8 @@ class StorageManagerService extends IStorageManager.Stub
}
private void remountUidExternalStorage(int uid, int mode) {
- waitForReady();
-
- String modeName = "none";
- switch (mode) {
- case Zygote.MOUNT_EXTERNAL_DEFAULT: {
- modeName = "default";
- } break;
- case Zygote.MOUNT_EXTERNAL_READ: {
- modeName = "read";
- } break;
- case Zygote.MOUNT_EXTERNAL_WRITE: {
- modeName = "write";
- } break;
- }
-
try {
- if (ENABLE_BINDER) {
- mVold.remountUid(uid, mode);
- } else {
- mConnector.execute("volume", "remount_uid", uid, modeName);
- }
+ mVold.remountUid(uid, mode);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -2241,7 +1759,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void setDebugFlags(int flags, int mask) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
if (!EMULATE_FBE_SUPPORTED) {
@@ -2331,7 +1848,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
final VolumeInfo from;
final VolumeInfo to;
@@ -2403,33 +1919,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- @Override
- public int[] getStorageUsers(String path) {
- enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
- waitForReady();
- try {
- final String[] r = NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("storage", "users", path),
- VoldResponseCode.StorageUsersListResult);
-
- // FMT: <pid> <process name>
- int[] data = new int[r.length];
- for (int i = 0; i < r.length; i++) {
- String[] tok = r[i].split(" ");
- try {
- data[i] = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
- return new int[0];
- }
- }
- return data;
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Failed to retrieve storage users list", e);
- return new int[0];
- }
- }
-
private void warnOnNotMounted() {
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
@@ -2444,304 +1933,6 @@ class StorageManagerService extends IStorageManager.Stub
Slog.w(TAG, "No primary storage mounted!");
}
- public String[] getSecureContainerList() {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_ACCESS);
- waitForReady();
- warnOnNotMounted();
-
- try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
- } catch (NativeDaemonConnectorException e) {
- return new String[0];
- }
- }
-
- public int createSecureContainer(String id, int sizeMb, String fstype, String key,
- int ownerUid, boolean external) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_CREATE);
- waitForReady();
- warnOnNotMounted();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key),
- ownerUid, external ? "1" : "0");
- } catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
-
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mAsecMountSet) {
- mAsecMountSet.add(id);
- }
- }
- return rc;
- }
-
- @Override
- public int resizeSecureContainer(String id, int sizeMb, String key) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_CREATE);
- waitForReady();
- warnOnNotMounted();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key));
- } catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- return rc;
- }
-
- public int finalizeSecureContainer(String id) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_CREATE);
- warnOnNotMounted();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "finalize", id);
- /*
- * Finalization does a remount, so no need
- * to update mAsecMountSet
- */
- } catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- return rc;
- }
-
- public int fixPermissionsSecureContainer(String id, int gid, String filename) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_CREATE);
- warnOnNotMounted();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "fixperms", id, gid, filename);
- /*
- * Fix permissions does a remount, so no need to update
- * mAsecMountSet
- */
- } catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- return rc;
- }
-
- public int destroySecureContainer(String id, boolean force) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_DESTROY);
- waitForReady();
- warnOnNotMounted();
-
- /*
- * Force a GC to make sure AssetManagers in other threads of the
- * system_server are cleaned up. We have to do this since AssetManager
- * instances are kept as a WeakReference and it's possible we have files
- * open on the external storage.
- */
- Runtime.getRuntime().gc();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- final Command cmd = new Command("asec", "destroy", id);
- if (force) {
- cmd.appendArg("force");
- }
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- }
-
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mAsecMountSet) {
- if (mAsecMountSet.contains(id)) {
- mAsecMountSet.remove(id);
- }
- }
- }
-
- return rc;
- }
-
- public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
- waitForReady();
- warnOnNotMounted();
-
- synchronized (mAsecMountSet) {
- if (mAsecMountSet.contains(id)) {
- return StorageResultCode.OperationFailedStorageMounted;
- }
- }
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid,
- readOnly ? "ro" : "rw");
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- }
-
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mAsecMountSet) {
- mAsecMountSet.add(id);
- }
- }
- return rc;
- }
-
- public int unmountSecureContainer(String id, boolean force) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
- waitForReady();
- warnOnNotMounted();
-
- synchronized (mAsecMountSet) {
- if (!mAsecMountSet.contains(id)) {
- return StorageResultCode.OperationFailedStorageNotMounted;
- }
- }
-
- /*
- * Force a GC to make sure AssetManagers in other threads of the
- * system_server are cleaned up. We have to do this since AssetManager
- * instances are kept as a WeakReference and it's possible we have files
- * open on the external storage.
- */
- Runtime.getRuntime().gc();
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- final Command cmd = new Command("asec", "unmount", id);
- if (force) {
- cmd.appendArg("force");
- }
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- }
-
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mAsecMountSet) {
- mAsecMountSet.remove(id);
- }
- }
- return rc;
- }
-
- public boolean isSecureContainerMounted(String id) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_ACCESS);
- waitForReady();
- warnOnNotMounted();
-
- synchronized (mAsecMountSet) {
- return mAsecMountSet.contains(id);
- }
- }
-
- public int renameSecureContainer(String oldId, String newId) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_RENAME);
- waitForReady();
- warnOnNotMounted();
-
- synchronized (mAsecMountSet) {
- /*
- * Because a mounted container has active internal state which cannot be
- * changed while active, we must ensure both ids are not currently mounted.
- */
- if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
- return StorageResultCode.OperationFailedStorageMounted;
- }
- }
-
- int rc = StorageResultCode.OperationSucceeded;
- try {
- mConnector.execute("asec", "rename", oldId, newId);
- } catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
-
- return rc;
- }
-
- public String getSecureContainerPath(String id) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_ACCESS);
- waitForReady();
- warnOnNotMounted();
-
- final NativeDaemonEvent event;
- try {
- event = mConnector.execute("asec", "path", id);
- event.checkCode(VoldResponseCode.AsecPathResult);
- return event.getMessage();
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageNotFound) {
- Slog.i(TAG, String.format("Container '%s' not found", id));
- return null;
- } else {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- }
- }
-
- public String getSecureContainerFilesystemPath(String id) {
- if (!ASEC_ENABLE) throw new UnsupportedOperationException();
- enforcePermission(android.Manifest.permission.ASEC_ACCESS);
- waitForReady();
- warnOnNotMounted();
-
- final NativeDaemonEvent event;
- try {
- event = mConnector.execute("asec", "fspath", id);
- event.checkCode(VoldResponseCode.AsecPathResult);
- return event.getMessage();
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageNotFound) {
- Slog.i(TAG, String.format("Container '%s' not found", id));
- return null;
- } else {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- }
- }
-
- @Override
- public void finishMediaUpdate() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("no permission to call finishMediaUpdate()");
- }
- if (mUnmountSignal != null) {
- mUnmountSignal.countDown();
- } else {
- Slog.w(TAG, "Odd, nobody asked to unmount?");
- }
- }
-
private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
if (callerUid == android.os.Process.SYSTEM_UID) {
return true;
@@ -2762,10 +1953,10 @@ class StorageManagerService extends IStorageManager.Stub
return callerUid == packageUid;
}
+ @Override
public String getMountedObbPath(String rawPath) {
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
- waitForReady();
warnOnNotMounted();
final ObbState state;
@@ -2777,23 +1968,7 @@ class StorageManagerService extends IStorageManager.Stub
return null;
}
- if (ENABLE_BINDER) {
- return findVolumeByIdOrThrow(state.volId).getPath().getAbsolutePath();
- }
-
- final NativeDaemonEvent event;
- try {
- event = mConnector.execute("obb", "path", state.canonicalPath);
- event.checkCode(VoldResponseCode.AsecPathResult);
- return event.getMessage();
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageNotFound) {
- return null;
- } else {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- }
+ return findVolumeByIdOrThrow(state.volId).getPath().getAbsolutePath();
}
@Override
@@ -2850,28 +2025,10 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
- if (ENABLE_BINDER) {
- try {
- return mVold.fdeComplete();
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "cryptocomplete");
- return Integer.parseInt(event.getMessage());
- } catch (NumberFormatException e) {
- // Bad result - unexpected.
- Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
- return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
- } catch (NativeDaemonConnectorException e) {
- // Something bad happened.
- Slog.w(TAG, "Error in communicating with cryptfs in validating");
+ return mVold.fdeComplete();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
}
}
@@ -2881,8 +2038,6 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
if (TextUtils.isEmpty(password)) {
throw new IllegalArgumentException("password cannot be empty");
}
@@ -2891,55 +2046,27 @@ class StorageManagerService extends IStorageManager.Stub
Slog.i(TAG, "decrypting storage...");
}
- if (ENABLE_BINDER) {
- try {
- mVold.fdeCheckPassword(password);
- mHandler.postDelayed(() -> {
- try {
- mVold.fdeRestart();
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- }
- }, DateUtils.SECOND_IN_MILLIS);
- return 0;
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
-
- final int code = Integer.parseInt(event.getMessage());
- if (code == 0) {
- // Decrypt was successful. Post a delayed message before restarting in order
- // to let the UI to clear itself
- mHandler.postDelayed(new Runnable() {
- public void run() {
- try {
- mCryptConnector.execute("cryptfs", "restart");
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "problem executing in background", e);
- }
- }
- }, 1000); // 1 second
- }
-
- return code;
- } catch (NativeDaemonConnectorException e) {
- // Decryption failed
- return e.getCode();
+ mVold.fdeCheckPassword(password);
+ mHandler.postDelayed(() -> {
+ try {
+ mVold.fdeRestart();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }, DateUtils.SECOND_IN_MILLIS);
+ return 0;
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
}
}
+ @Override
public int encryptStorage(int type, String password) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
password = "";
} else if (TextUtils.isEmpty(password)) {
@@ -2951,17 +2078,7 @@ class StorageManagerService extends IStorageManager.Stub
}
try {
- if (ENABLE_BINDER) {
- mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE);
- } else {
- if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
- mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
- CRYPTO_TYPES[type]);
- } else {
- mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
- CRYPTO_TYPES[type], new SensitiveArg(password));
- }
- }
+ mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE);
} catch (Exception e) {
Slog.wtf(TAG, e);
return -1;
@@ -2974,12 +2091,11 @@ class StorageManagerService extends IStorageManager.Stub
* @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
* @param password The password to set.
*/
+ @Override
public int changeEncryptionPassword(int type, String password) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
password = "";
} else if (TextUtils.isEmpty(password)) {
@@ -2990,23 +2106,12 @@ class StorageManagerService extends IStorageManager.Stub
Slog.i(TAG, "changing encryption password...");
}
- if (ENABLE_BINDER) {
- try {
- mVold.fdeChangePassword(type, password);
- return 0;
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return -1;
- }
- }
-
try {
- NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
- new SensitiveArg(password));
- return Integer.parseInt(event.getMessage());
- } catch (NativeDaemonConnectorException e) {
- // Encryption failed
- return e.getCode();
+ mVold.fdeChangePassword(type, password);
+ return 0;
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return -1;
}
}
@@ -3027,30 +2132,16 @@ class StorageManagerService extends IStorageManager.Stub
throw new IllegalArgumentException("password cannot be empty");
}
- waitForReady();
-
if (DEBUG_EVENTS) {
Slog.i(TAG, "validating encryption password...");
}
- if (ENABLE_BINDER) {
- try {
- mVold.fdeVerifyPassword(password);
- return 0;
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return -1;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
- Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
- return Integer.parseInt(event.getMessage());
- } catch (NativeDaemonConnectorException e) {
- // Encryption failed
- return e.getCode();
+ mVold.fdeVerifyPassword(password);
+ return 0;
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return -1;
}
}
@@ -3063,28 +2154,11 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
- if (ENABLE_BINDER) {
- try {
- return mVold.fdeGetPasswordType();
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return -1;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "getpwtype");
- for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
- if (CRYPTO_TYPES[i].equals(event.getMessage()))
- return i;
- }
-
- throw new IllegalStateException("unexpected return from cryptfs");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ return mVold.fdeGetPasswordType();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return -1;
}
}
@@ -3098,23 +2172,12 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
- if (ENABLE_BINDER) {
- try {
- mVold.fdeSetField(field, contents);
- return;
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mVold.fdeSetField(field, contents);
+ return;
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return;
}
}
@@ -3128,29 +2191,11 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
- if (ENABLE_BINDER) {
- try {
- return mVold.fdeGetField(field);
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return null;
- }
- }
-
- final NativeDaemonEvent event;
try {
- final String[] contents = NativeDaemonEvent.filterMessageList(
- mCryptConnector.executeForList("cryptfs", "getfield", field),
- VoldResponseCode.CryptfsGetfieldResult);
- String result = new String();
- for (String content : contents) {
- result += content;
- }
- return result;
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ return mVold.fdeGetField(field);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return null;
}
}
@@ -3163,23 +2208,11 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
- waitForReady();
-
- if (ENABLE_BINDER) {
- try {
- return mVold.isConvertibleToFbe();
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return false;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE");
- return Integer.parseInt(event.getMessage()) != 0;
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ return mVold.isConvertibleToFbe();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return false;
}
}
@@ -3188,31 +2221,10 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"only keyguard can retrieve password");
- if (!isReady()) {
- return new String();
- }
-
- if (ENABLE_BINDER) {
- try {
- return mVold.fdeGetPassword();
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return null;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "getpw");
- if ("-1".equals(event.getMessage())) {
- // -1 equals no password
- return null;
- }
- return event.getMessage();
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Invalid response to getPassword");
+ return mVold.fdeGetPassword();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
return null;
}
}
@@ -3222,40 +2234,21 @@ class StorageManagerService extends IStorageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"only keyguard can clear password");
- if (!isReady()) {
- return;
- }
-
- if (ENABLE_BINDER) {
- try {
- mVold.fdeClearPassword();
- return;
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- return;
- }
- }
-
- final NativeDaemonEvent event;
try {
- event = mCryptConnector.execute("cryptfs", "clearpw");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mVold.fdeClearPassword();
+ return;
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return;
}
}
@Override
public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.createUserKey(userId, serialNumber, ephemeral);
- } else {
- mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
- ephemeral ? 1 : 0);
- }
+ mVold.createUserKey(userId, serialNumber, ephemeral);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3264,14 +2257,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void destroyUserKey(int userId) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.destroyUserKey(userId);
- } else {
- mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
- }
+ mVold.destroyUserKey(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3295,16 +2283,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.addUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret));
- } else {
- mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber,
- new SensitiveArg(encodeBytes(token)),
- new SensitiveArg(encodeBytes(secret)));
- }
+ mVold.addUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret));
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3316,14 +2297,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void fixateNewestUserKeyAuth(int userId) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.fixateNewestUserKeyAuth(userId);
- } else {
- mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId);
- }
+ mVold.fixateNewestUserKeyAuth(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3332,7 +2308,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
// When a user has secure lock screen, require secret to actually unlock.
@@ -3342,14 +2317,8 @@ class StorageManagerService extends IStorageManager.Stub
}
try {
- if (ENABLE_BINDER) {
- mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
- encodeBytes(secret));
- } else {
- mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
- new SensitiveArg(encodeBytes(token)),
- new SensitiveArg(encodeBytes(secret)));
- }
+ mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
+ encodeBytes(secret));
} catch (Exception e) {
Slog.wtf(TAG, e);
return;
@@ -3369,14 +2338,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void lockUserKey(int userId) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.lockUserKey(userId);
- } else {
- mCryptConnector.execute("cryptfs", "lock_user_key", userId);
- }
+ mVold.lockUserKey(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
return;
@@ -3397,15 +2361,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
- } else {
- mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
- userId, serialNumber, flags);
- }
+ mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3414,15 +2372,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void destroyUserStorage(String volumeUuid, int userId, int flags) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.destroyUserStorage(volumeUuid, userId, flags);
- } else {
- mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
- userId, flags);
- }
+ mVold.destroyUserStorage(volumeUuid, userId, flags);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3431,14 +2383,9 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void secdiscard(String path) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
- waitForReady();
try {
- if (ENABLE_BINDER) {
- mVold.secdiscard(path);
- } else {
- mCryptConnector.execute("cryptfs", "secdiscard", escapeNull(path));
- }
+ mVold.secdiscard(path);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3453,33 +2400,18 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
- if (ENABLE_BINDER) {
- try {
- return new ParcelFileDescriptor(
- mVold.mountAppFuse(uid, Process.myPid(), mountId));
- } catch (Exception e) {
- throw new NativeDaemonConnectorException("Failed to mount", e);
- }
- } else {
- final NativeDaemonEvent event = mConnector.execute(
- "appfuse", "mount", uid, Process.myPid(), mountId);
- opened = true;
- if (event.getFileDescriptors() == null ||
- event.getFileDescriptors().length == 0) {
- throw new NativeDaemonConnectorException("Cannot obtain device FD");
- }
- return new ParcelFileDescriptor(event.getFileDescriptors()[0]);
+ try {
+ return new ParcelFileDescriptor(
+ mVold.mountAppFuse(uid, Process.myPid(), mountId));
+ } catch (Exception e) {
+ throw new NativeDaemonConnectorException("Failed to mount", e);
}
}
@Override
public void close() throws Exception {
if (opened) {
- if (ENABLE_BINDER) {
- mVold.unmountAppFuse(uid, Process.myPid(), mountId);
- } else {
- mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId);
- }
+ mVold.unmountAppFuse(uid, Process.myPid(), mountId);
opened = false;
}
}
@@ -3568,11 +2500,7 @@ class StorageManagerService extends IStorageManager.Stub
}
try {
- if (ENABLE_BINDER) {
- mVold.mkdirs(appPath);
- } else {
- mConnector.execute("volume", "mkdirs", appPath);
- }
+ mVold.mkdirs(appPath);
return 0;
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -4124,7 +3052,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void handleExecute() throws IOException, RemoteException {
- waitForReady();
warnOnNotMounted();
final ObbInfo obbInfo = getObbInfo();
@@ -4174,19 +3101,9 @@ class StorageManagerService extends IStorageManager.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- if (ENABLE_BINDER) {
- mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey,
- mObbState.ownerGid);
- mVold.mount(mObbState.volId, 0, -1);
- } else {
- mConnector.execute("obb", "mount", mObbState.canonicalPath,
- new SensitiveArg(hashedKey), mObbState.ownerGid);
- }
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
+ mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey,
+ mObbState.ownerGid);
+ mVold.mount(mObbState.volId, 0, -1);
} catch (Exception e) {
Slog.w(TAG, e);
rc = StorageResultCode.OperationFailedInternalError;
@@ -4233,7 +3150,6 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void handleExecute() throws IOException {
- waitForReady();
warnOnNotMounted();
final ObbState existingState;
@@ -4255,27 +3171,9 @@ class StorageManagerService extends IStorageManager.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- if (ENABLE_BINDER) {
- mVold.unmount(mObbState.volId);
- mVold.destroyObb(mObbState.volId);
- mObbState.volId = null;
- } else {
- final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
- if (mForceUnmount) {
- cmd.appendArg("force");
- }
- mConnector.execute(cmd);
- }
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
- } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
- // If it's not mounted then we've already won.
- rc = StorageResultCode.OperationSucceeded;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
+ mVold.unmount(mObbState.volId);
+ mVold.destroyObb(mObbState.volId);
+ mObbState.volId = null;
} catch (Exception e) {
Slog.w(TAG, e);
rc = StorageResultCode.OperationFailedInternalError;
@@ -4506,18 +3404,6 @@ class StorageManagerService extends IStorageManager.Stub
}
pw.println();
- pw.println("mConnector:");
- pw.increaseIndent();
- mConnector.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
- pw.println("mCryptConnector:");
- pw.increaseIndent();
- mCryptConnector.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
pw.print("Last maintenance: ");
pw.println(TimeUtils.formatForLogging(mLastMaintenance));
}
@@ -4525,11 +3411,10 @@ class StorageManagerService extends IStorageManager.Stub
/** {@inheritDoc} */
@Override
public void monitor() {
- if (mConnector != null) {
- mConnector.monitor();
- }
- if (mCryptConnector != null) {
- mCryptConnector.monitor();
+ try {
+ mVold.monitor();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
}
}
diff --git a/com/android/server/SystemServer.java b/com/android/server/SystemServer.java
index 57271fa1..92cbd3d5 100644
--- a/com/android/server/SystemServer.java
+++ b/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@ import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
import com.android.server.soundtrigger.SoundTriggerService;
+import com.android.server.stats.StatsCompanionService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
@@ -151,7 +152,7 @@ public final class SystemServer {
* them from the build system somehow.
*/
private static final String BACKUP_MANAGER_SERVICE_CLASS =
- "com.android.server.backup.BackupManagerService$Lifecycle";
+ "com.android.server.backup.RefactoredBackupManagerService$Lifecycle";
private static final String APPWIDGET_SERVICE_CLASS =
"com.android.server.appwidget.AppWidgetService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
@@ -667,9 +668,11 @@ public final class SystemServer {
traceEnd();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
- traceBeginAndSlog("StartWebViewUpdateService");
- mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
- traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ traceBeginAndSlog("StartWebViewUpdateService");
+ mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
+ traceEnd();
+ }
}
/**
@@ -681,6 +684,7 @@ public final class SystemServer {
VibratorService vibrator = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
+ IpSecService ipSecService = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
@@ -1030,6 +1034,15 @@ public final class SystemServer {
reportWtf("starting NetworkManagement Service", e);
}
traceEnd();
+
+ traceBeginAndSlog("StartIpSecService");
+ try {
+ ipSecService = IpSecService.create(context);
+ ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
+ } catch (Throwable e) {
+ reportWtf("starting IpSec Service", e);
+ }
+ traceEnd();
}
if (!disableNonCoreServices && !disableTextServices) {
@@ -1080,6 +1093,14 @@ public final class SystemServer {
traceBeginAndSlog("StartWifiRtt");
mSystemServiceManager.startService("com.android.server.wifi.RttService");
traceEnd();
+
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
+ traceEnd();
+ }
}
if (context.getPackageManager().hasSystemFeature(
@@ -1087,8 +1108,6 @@ public final class SystemServer {
traceBeginAndSlog("StartWifiAware");
mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
traceEnd();
- } else {
- Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
}
if (context.getPackageManager().hasSystemFeature(
@@ -1146,20 +1165,6 @@ public final class SystemServer {
traceEnd();
}
- /*
- * StorageManagerService has a few dependencies: Notification Manager and
- * AppWidget Provider. Make sure StorageManagerService is completely started
- * first before continuing.
- */
- if (storageManager != null && !mOnlyCore) {
- traceBeginAndSlog("WaitForAsecScan");
- try {
- storageManager.waitForAsecScan();
- } catch (RemoteException ignored) {
- }
- traceEnd();
- }
-
traceBeginAndSlog("StartNotificationManager");
mSystemServiceManager.startService(NotificationManagerService.class);
SystemNotificationChannels.createAll(context);
@@ -1209,18 +1214,6 @@ public final class SystemServer {
traceEnd();
}
- // timezone.RulesManagerService will prevent a device starting up if the chain of trust
- // required for safe time zone updates might be broken. RuleManagerService cannot do
- // this check when mOnlyCore == true, so we don't enable the service in this case.
- final boolean startRulesManagerService =
- !mOnlyCore && context.getResources().getBoolean(
- R.bool.config_enableUpdateableTimeZoneRules);
- if (startRulesManagerService) {
- traceBeginAndSlog("StartTimeZoneRulesManagerService");
- mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
-
traceBeginAndSlog("StartAudioService");
mSystemServiceManager.startService(AudioService.Lifecycle.class);
traceEnd();
@@ -1361,6 +1354,19 @@ public final class SystemServer {
}
traceEnd();
+ // timezone.RulesManagerService will prevent a device starting up if the chain of trust
+ // required for safe time zone updates might be broken. RuleManagerService cannot do
+ // this check when mOnlyCore == true, so we don't enable the service in this case.
+ // This service requires that JobSchedulerService is already started when it starts.
+ final boolean startRulesManagerService =
+ !mOnlyCore && context.getResources().getBoolean(
+ R.bool.config_enableUpdateableTimeZoneRules);
+ if (startRulesManagerService) {
+ traceBeginAndSlog("StartTimeZoneRulesManagerService");
+ mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
if (!disableNetwork && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
@@ -1536,6 +1542,11 @@ public final class SystemServer {
traceEnd();
}
+ // Statsd helper
+ traceBeginAndSlog("StartStatsCompanionService");
+ mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
+ traceEnd();
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
@@ -1661,6 +1672,7 @@ public final class SystemServer {
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
+ final IpSecService ipSecServiceF = ipSecService;
final WindowManagerService windowManagerF = wm;
// We now tell the activity manager it is okay to run third party
@@ -1683,10 +1695,10 @@ public final class SystemServer {
traceEnd();
// No dependency on Webview preparation in system server. But this should
- // be completed before allowring 3rd party
+ // be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
Future<?> webviewPrep = null;
- if (!mOnlyCore) {
+ if (!mOnlyCore && mWebViewUpdateService != null) {
webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
TimingsTraceLog traceLog = new TimingsTraceLog(
@@ -1731,6 +1743,13 @@ public final class SystemServer {
.networkScoreAndNetworkManagementServiceReady();
}
traceEnd();
+ traceBeginAndSlog("MakeIpSecServiceReady");
+ try {
+ if (ipSecServiceF != null) ipSecServiceF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making IpSec Service ready", e);
+ }
+ traceEnd();
traceBeginAndSlog("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) networkStatsF.systemReady();
diff --git a/com/android/server/accounts/AccountManagerService.java b/com/android/server/accounts/AccountManagerService.java
index 8ae592f7..4e15e5d9 100644
--- a/com/android/server/accounts/AccountManagerService.java
+++ b/com/android/server/accounts/AccountManagerService.java
@@ -2969,9 +2969,13 @@ public class AccountManagerService
* have users launching arbitrary activities by tricking users to
* interact with malicious notifications.
*/
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
doNotification(
mAccounts,
account,
@@ -3366,9 +3370,13 @@ public class AccountManagerService
Intent intent = null;
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
}
IAccountManagerResponse response;
if (mExpectActivityLaunch && result != null
@@ -4716,9 +4724,7 @@ public class AccountManagerService
* into launching arbitrary intents on the device via by tricking to click authenticator
* supplied entries in the system Settings app.
*/
- protected void checkKeyIntent(
- int authUid,
- Intent intent) throws SecurityException {
+ protected boolean checkKeyIntent(int authUid, Intent intent) {
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
@@ -4727,6 +4733,9 @@ public class AccountManagerService
try {
PackageManager pm = mContext.getPackageManager();
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+ if (resolveInfo == null) {
+ return false;
+ }
ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
int targetUid = targetActivityInfo.applicationInfo.uid;
if (!isExportedSystemActivity(targetActivityInfo)
@@ -4736,9 +4745,10 @@ public class AccountManagerService
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
+ "does not share a signature with the supplying authenticator (%s).";
- throw new SecurityException(
- String.format(tmpl, activityName, pkgName, mAccountType));
+ Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType));
+ return false;
}
+ return true;
} finally {
Binder.restoreCallingIdentity(bid);
}
@@ -4888,9 +4898,13 @@ public class AccountManagerService
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
}
if (result != null
&& !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
@@ -5285,7 +5299,7 @@ public class AccountManagerService
== PackageManager.PERMISSION_GRANTED) {
// Checks runtime permission revocation.
final int opCode = AppOpsManager.permissionToOpCode(perm);
- if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED) {
return true;
}
@@ -5306,7 +5320,7 @@ public class AccountManagerService
Log.v(TAG, " caller uid " + callingUid + " has " + perm);
}
final int opCode = AppOpsManager.permissionToOpCode(perm);
- if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
return true;
}
diff --git a/com/android/server/am/ActiveServices.java b/com/android/server/am/ActiveServices.java
index e0cde72f..2131731d 100644
--- a/com/android/server/am/ActiveServices.java
+++ b/com/android/server/am/ActiveServices.java
@@ -2162,6 +2162,15 @@ public final class ActiveServices {
}
}
+ if (r.fgRequired) {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
+ + " for fg-service launch");
+ }
+ mAm.tempWhitelistUidLocked(r.appInfo.uid,
+ SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch");
+ }
+
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
@@ -3141,7 +3150,7 @@ public final class ActiveServices {
sr.userId, sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr);
} else if (!allowRestart
- || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) {
+ || !mAm.mUserController.isUserRunning(sr.userId, 0)) {
bringDownServiceLocked(sr);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true);
diff --git a/com/android/server/am/ActivityDisplay.java b/com/android/server/am/ActivityDisplay.java
new file mode 100644
index 00000000..8bcbfbef
--- /dev/null
+++ b/com/android/server/am/ActivityDisplay.java
@@ -0,0 +1,414 @@
+/*
+ * 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 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;
+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.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+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.proto.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
+import static com.android.server.am.proto.ActivityDisplayProto.ID;
+
+import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ConfigurationContainer;
+
+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 {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
+ private static final String TAG_STACK = TAG + POSTFIX_STACK;
+
+ static final int POSITION_TOP = Integer.MAX_VALUE;
+ static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
+ private ActivityStackSupervisor mSupervisor;
+ /** Actual Display this object tracks. */
+ int mDisplayId;
+ Display mDisplay;
+
+ /** 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 */
+ final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+
+ /** Array of all UIDs that are present on the display. */
+ private IntArray mDisplayAccessUIDs = new IntArray();
+
+ /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+ final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+ /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+ ActivityManagerInternal.SleepToken mOffToken;
+
+ private boolean mSleeping;
+
+ ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
+ mSupervisor = supervisor;
+ mDisplayId = displayId;
+ final Display display = supervisor.mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalStateException("Display does not exist displayId=" + displayId);
+ }
+ mDisplay = display;
+ }
+
+ void addChild(ActivityStack stack, int position) {
+ if (position == POSITION_BOTTOM) {
+ position = 0;
+ } else if (position == POSITION_TOP) {
+ position = mStacks.size();
+ }
+ if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
+ + " to displayId=" + mDisplayId + " position=" + position);
+ positionChildAt(stack, position);
+ mSupervisor.mService.updateSleepIfNeededLocked();
+ }
+
+ void removeChild(ActivityStack stack) {
+ if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+ + " from displayId=" + mDisplayId);
+ mStacks.remove(stack);
+ mSupervisor.mService.updateSleepIfNeededLocked();
+ }
+
+ void positionChildAtTop(ActivityStack stack) {
+ positionChildAt(stack, mStacks.size());
+ }
+
+ void positionChildAtBottom(ActivityStack stack) {
+ positionChildAt(stack, 0);
+ }
+
+ private void positionChildAt(ActivityStack stack, int position) {
+ mStacks.remove(stack);
+ mStacks.add(getTopInsertPosition(stack, position), stack);
+ }
+
+ private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
+ int position = mStacks.size();
+ if (position > 0) {
+ final ActivityStack topStack = mStacks.get(position - 1);
+ if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
+ // If the top stack is always on top, we move this stack just below it.
+ position--;
+ }
+ }
+ return Math.min(position, candidatePosition);
+ }
+
+ <T extends ActivityStack> T getStack(int stackId) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.mStackId == stackId) {
+ return (T) stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the topmost stack on the display that is compatible with the input windowing mode and
+ * activity type. {@code null} means no compatible stack on the display.
+ * @see ConfigurationContainer#isCompatible(int, int)
+ */
+ <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ 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?
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return (T) stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see #getStack(int, int)
+ * @see #createStack(int, int, boolean)
+ */
+ <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+ boolean onTop) {
+ T stack = getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ return createStack(windowingMode, activityType, onTop);
+ }
+
+ /**
+ * Creates a stack matching the input windowing mode and activity type on this display.
+ * @param windowingMode The windowing mode the stack should be created in. If
+ * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
+ * be created in {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+ * @param activityType The activityType the stack should be created in. If
+ * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
+ * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+ * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+ * @return The newly created stack.
+ */
+ <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+
+ if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+ // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
+ // anything else should be passing it in anyways...
+ activityType = ACTIVITY_TYPE_STANDARD;
+ }
+
+ if (activityType != ACTIVITY_TYPE_STANDARD) {
+ // For now there can be only one stack of a particular non-standard activity type on a
+ // display. So, get that ignoring whatever windowing mode it is currently in.
+ T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ + activityType + " already on display=" + this + ". Can't have multiple.");
+ }
+ }
+
+ final ActivityManagerService service = mSupervisor.mService;
+ if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+ service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
+ service.mSupportsPictureInPicture, activityType)) {
+ throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ + windowingMode);
+ }
+
+ 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 = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ 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
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType)) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ 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);
+
+ if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally need to be on
+ // the other side of the docked stack and we make visibility decisions based on that.
+ // TODO: Not sure if this is needed after we change to calculate visibility based on
+ // stack z-order vs. id.
+ getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop);
+ }
+
+ return stack;
+ }
+
+ @VisibleForTesting
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ switch (windowingMode) {
+ case WINDOWING_MODE_PINNED:
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ default:
+ return (T) new ActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ }
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ if (windowingModes == null || windowingModes.length == 0) {
+ return;
+ }
+
+ for (int j = windowingModes.length - 1 ; j >= 0; --j) {
+ final int windowingMode = windowingModes[j];
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ continue;
+ }
+ if (stack.getWindowingMode() != windowingMode) {
+ continue;
+ }
+ mSupervisor.removeStackLocked(stack.mStackId);
+ }
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ if (activityTypes == null || activityTypes.length == 0) {
+ return;
+ }
+
+ for (int j = activityTypes.length - 1 ; j >= 0; --j) {
+ final int activityType = activityTypes[j];
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getActivityType() == activityType) {
+ mSupervisor.removeStackLocked(stack.mStackId);
+ }
+ }
+ }
+ }
+
+ /** 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) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getWindowingMode() == excludeWindowingMode) {
+ continue;
+ }
+ if (stack.shouldBeVisible(null /* starting */)) {
+ return stack.getActivityType();
+ }
+ }
+ return ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ ActivityStack getSplitScreenStack() {
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasSplitScreenStack() {
+ return getSplitScreenStack() != null;
+ }
+
+ PinnedActivityStack getPinnedStack() {
+ return getStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasPinnedStack() {
+ return getPinnedStack() != null;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+ }
+
+ @Override
+ protected int getChildCount() {
+ return mStacks.size();
+ }
+
+ @Override
+ protected ConfigurationContainer getChildAt(int index) {
+ return mStacks.get(index);
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return mSupervisor;
+ }
+
+ boolean isPrivate() {
+ return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+ }
+
+ boolean isUidPresent(int uid) {
+ for (ActivityStack stack : mStacks) {
+ if (stack.isUidPresent(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Update and get all UIDs that are present on the display and have access to it. */
+ IntArray getPresentUIDs() {
+ mDisplayAccessUIDs.clear();
+ for (ActivityStack stack : mStacks) {
+ stack.getPresentUIDs(mDisplayAccessUIDs);
+ }
+ return mDisplayAccessUIDs;
+ }
+
+ boolean shouldDestroyContentOnRemove() {
+ return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+ }
+
+ boolean shouldSleep() {
+ return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+ && (mSupervisor.mService.mRunningVoice == null);
+ }
+
+ boolean isSleeping() {
+ return mSleeping;
+ }
+
+ void setIsSleeping(boolean asleep) {
+ mSleeping = asleep;
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER);
+ proto.write(ID, mDisplayId);
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.writeToProto(proto, STACKS);
+ }
+ proto.end(token);
+ }
+}
diff --git a/com/android/server/am/ActivityManagerService.java b/com/android/server/am/ActivityManagerService.java
index 02eb3b43..e6fe6204 100644
--- a/com/android/server/am/ActivityManagerService.java
+++ b/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,14 @@ 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;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -145,7 +153,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
@@ -163,10 +170,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
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.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
@@ -177,8 +182,8 @@ 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_ACTIVITY_RELAUNCH;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
@@ -259,8 +264,8 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
@@ -349,6 +354,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -402,6 +408,7 @@ import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.utils.PriorityDump;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
@@ -682,11 +689,6 @@ public class ActivityManagerService extends IActivityManager.Stub
ActivityInfo mLastAddedTaskActivity;
/**
- * List of packages whitelisted by DevicePolicyManager for locktask. Indexed by userId.
- */
- SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
-
- /**
* The package name of the DeviceOwner. This package is not permitted to have its data cleared.
*/
String mDeviceOwnerName;
@@ -712,9 +714,45 @@ public class ActivityManagerService extends IActivityManager.Stub
@VisibleForTesting
long mWaitForNetworkTimeoutMs;
+ /**
+ * Helper class which parses out priority arguments and dumps sections according to their
+ * priority. If priority arguments are omitted, function calls the legacy dump command.
+ */
+ private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, new String[] {"activities"});
+ }
+
+ @Override
+ public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, new String[] {"settings"});
+ doDump(fd, pw, new String[] {"intents"});
+ doDump(fd, pw, new String[] {"broadcasts"});
+ doDump(fd, pw, new String[] {"providers"});
+ doDump(fd, pw, new String[] {"permissions"});
+ doDump(fd, pw, new String[] {"services"});
+ doDump(fd, pw, new String[] {"recents"});
+ doDump(fd, pw, new String[] {"lastanr"});
+ doDump(fd, pw, new String[] {"starter"});
+ if (mAssociations.size() > 0) {
+ 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
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, args);
+ }
+ };
+
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
- && !mKeyguardController.isKeyguardShowing()
+ && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
&& !(UserManager.isDeviceInDemoMode(mContext)
&& mUserController.getCurrentUser().isDemo());
}
@@ -1653,45 +1691,34 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
- static final int REPORT_USER_SWITCH_MSG = 34;
- static final int CONTINUE_USER_SWITCH_MSG = 35;
- static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
static final int REQUEST_ALL_PSS_MSG = 39;
- static final int START_PROFILES_MSG = 40;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
- static final int SYSTEM_USER_START_MSG = 42;
- static final int SYSTEM_USER_CURRENT_MSG = 43;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
- static final int START_USER_SWITCH_UI_MSG = 46;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
static final int DISMISS_DIALOG_UI_MSG = 48;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
static final int DELETE_DUMPHEAP_MSG = 51;
- static final int FOREGROUND_PROFILE_CHANGED_MSG = 52;
static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
static final int REPORT_TIME_TRACKER_MSG = 54;
- static final int REPORT_USER_SWITCH_COMPLETE_MSG = 55;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
static final int IDLE_UIDS_MSG = 58;
- static final int SYSTEM_USER_UNLOCK_MSG = 59;
static final int LOG_STACK_STATE = 60;
static final int VR_MODE_CHANGE_MSG = 61;
static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 62;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
- static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64;
static final int NOTIFY_VR_SLEEPING_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
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 USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 71;
- static final int START_USER_SWITCH_FG_MSG = 712;
+ 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;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1904,10 +1931,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
break;
}
- case START_USER_SWITCH_UI_MSG: {
- mUserController.showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj);
- break;
- }
case DISMISS_DIALOG_UI_MSG: {
final Dialog d = (Dialog) msg.obj;
d.dismiss();
@@ -1923,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;
@@ -2136,26 +2170,6 @@ public class ActivityManagerService extends IActivityManager.Stub
thread.start();
break;
}
- case START_USER_SWITCH_FG_MSG: {
- mUserController.startUserInForeground(msg.arg1);
- break;
- }
- case REPORT_USER_SWITCH_MSG: {
- mUserController.dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
- break;
- }
- case CONTINUE_USER_SWITCH_MSG: {
- mUserController.continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
- break;
- }
- case USER_SWITCH_TIMEOUT_MSG: {
- mUserController.timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
- break;
- }
- case USER_SWITCH_CALLBACKS_TIMEOUT_MSG: {
- mUserController.timeoutUserSwitchCallbacks(msg.arg1, msg.arg2);
- break;
- }
case IMMERSIVE_MODE_LOCK_MSG: {
final boolean nextState = (msg.arg1 != 0);
if (mUpdateLock.isHeld() != nextState) {
@@ -2180,12 +2194,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
break;
}
- case START_PROFILES_MSG: {
- synchronized (ActivityManagerService.this) {
- mUserController.startProfilesLocked();
- }
- break;
- }
case UPDATE_TIME_PREFERENCE_MSG: {
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
@@ -2204,35 +2212,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
break;
}
- case SYSTEM_USER_START_MSG: {
- mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
- Integer.toString(msg.arg1), msg.arg1);
- mSystemServiceManager.startUser(msg.arg1);
- break;
- }
- case SYSTEM_USER_UNLOCK_MSG: {
- final int userId = msg.arg1;
- mSystemServiceManager.unlockUser(userId);
- synchronized (ActivityManagerService.this) {
- mRecentTasks.loadUserRecentsLocked(userId);
- }
- if (userId == UserHandle.USER_SYSTEM) {
- startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- }
- installEncryptionUnawareProviders(userId);
- mUserController.finishUserUnlocked((UserState) msg.obj);
- break;
- }
- case SYSTEM_USER_CURRENT_MSG: {
- mBatteryStatsService.noteEvent(
- BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
- Integer.toString(msg.arg2), msg.arg2);
- mBatteryStatsService.noteEvent(
- BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
- Integer.toString(msg.arg1), msg.arg1);
- mSystemServiceManager.switchUser(msg.arg1);
- break;
- }
case ENTER_ANIMATION_COMPLETE_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
@@ -2372,19 +2351,10 @@ public class ActivityManagerService extends IActivityManager.Stub
mMemWatchDumpUid = -1;
}
} break;
- case FOREGROUND_PROFILE_CHANGED_MSG: {
- mUserController.dispatchForegroundProfileChanged(msg.arg1);
- } break;
case REPORT_TIME_TRACKER_MSG: {
AppTimeTracker tracker = (AppTimeTracker)msg.obj;
tracker.deliverResult(mContext);
} break;
- case REPORT_USER_SWITCH_COMPLETE_MSG: {
- mUserController.dispatchUserSwitchComplete(msg.arg1);
- } break;
- case REPORT_LOCKED_BOOT_COMPLETE_MSG: {
- mUserController.dispatchLockedBootComplete(msg.arg1);
- } break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
try {
@@ -2409,17 +2379,16 @@ public class ActivityManagerService extends IActivityManager.Stub
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
}
}
} break;
case NOTIFY_VR_SLEEPING_MSG: {
notifyVrManagerOfSleepState(msg.arg1 != 0);
} break;
+ case NOTIFY_VR_KEYGUARD_MSG: {
+ notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+ } break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -2568,10 +2537,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
public void setWindowManager(WindowManagerService wm) {
- mWindowManager = wm;
- mStackSupervisor.setWindowManager(wm);
- mActivityStarter.setWindowManager(wm);
- mLockTaskController.setWindowManager(wm);
+ synchronized (this) {
+ mWindowManager = wm;
+ mStackSupervisor.setWindowManager(wm);
+ mActivityStarter.setWindowManager(wm);
+ mLockTaskController.setWindowManager(wm);
+ }
}
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
@@ -2589,6 +2560,14 @@ public class ActivityManagerService extends IActivityManager.Stub
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
+ private final PriorityDump.PriorityDumper mPriorityDumper =
+ new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
+ }
+ };
+
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@@ -2597,7 +2576,7 @@ public class ActivityManagerService extends IActivityManager.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
- mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
@@ -2631,19 +2610,27 @@ public class ActivityManagerService extends IActivityManager.Stub
static class CpuBinder extends Binder {
ActivityManagerService mActivityManagerService;
+ private final PriorityDump.PriorityDumper mPriorityDumper =
+ new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
+ "cpuinfo", pw)) return;
+ synchronized (mActivityManagerService.mProcessCpuTracker) {
+ pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
+ pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
+ SystemClock.uptimeMillis()));
+ }
+ }
+ };
+
CpuBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
- "cpuinfo", pw)) return;
- synchronized (mActivityManagerService.mProcessCpuTracker) {
- pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
- pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
- SystemClock.uptimeMillis()));
- }
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
@@ -3152,9 +3139,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
- mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
- mHandler.obtainMessage(
- FOREGROUND_PROFILE_CHANGED_MSG, r.userId, 0).sendToTarget();
+ mUserController.sendForegroundProfileChanged(r.userId);
}
mLastResumedActivity = r;
@@ -3259,7 +3244,8 @@ public class ActivityManagerService extends IActivityManager.Stub
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");
- moveTaskToStack(r.getTask().taskId, FULLSCREEN_WORKSPACE_STACK_ID, 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));
@@ -3278,6 +3264,19 @@ public class ActivityManagerService extends IActivityManager.Stub
vrService.onSleepStateChanged(isSleeping);
}
+ private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
+ }
+
+ private void notifyVrManagerOfKeyguardState(boolean isShowing) {
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+ if (vrService == null) {
+ return;
+ }
+ vrService.onKeyguardStateChanged(isShowing);
+ }
+
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -3874,6 +3873,12 @@ public class ActivityManagerService extends IActivityManager.Stub
mNativeDebuggingApp = null;
}
+ if (app.info.isPrivilegedApp() &&
+ !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ runtimeFlags |= Zygote.DISABLE_VERIFIER;
+ runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+ }
+
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
@@ -4156,15 +4161,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- void enforceShellRestriction(String restriction, int userHandle) {
- if (Binder.getCallingUid() == SHELL_UID) {
- if (userHandle < 0 || mUserController.hasUserRestriction(restriction, userHandle)) {
- throw new SecurityException("Shell does not have permission to access user "
- + userHandle);
- }
- }
- }
-
@Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
@@ -5432,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));
@@ -5465,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
@@ -6188,7 +6202,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
- if (mUserController.isUserRunningLocked(user, 0)) {
+ if (mUserController.isUserRunning(user, 0)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
finishForceStopPackageLocked(packageName, pkgUid);
}
@@ -7346,33 +7360,32 @@ public class ActivityManagerService extends IActivityManager.Stub
startProcessLocked(procs.get(ip), "on-hold", null);
}
}
+ if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ return;
+ }
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
+ mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
+ // Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
- if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- // Start looking for apps that are abusing wake locks.
- Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
- mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
- // Tell anyone interested that we are done booting!
- SystemProperties.set("sys.boot_completed", "1");
-
- // And trigger dev.bootcomplete if we are not showing encryption progress
- if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
+ // And trigger dev.bootcomplete if we are not showing encryption progress
+ if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
|| "".equals(SystemProperties.get("vold.encrypt_progress"))) {
- SystemProperties.set("dev.bootcomplete", "1");
- }
- mUserController.sendBootCompletedLocked(
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(),
- true, false);
- }
- }
- });
- scheduleStartProfilesLocked();
+ SystemProperties.set("dev.bootcomplete", "1");
}
+ mUserController.sendBootCompleted(
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+ }
+ }
+ });
+ mUserController.scheduleStartProfiles();
}
}
@@ -8112,7 +8125,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
- final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+ final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -8227,11 +8240,6 @@ public class ActivityManagerService extends IActivityManager.Stub
+ ": Current activity does not support picture-in-picture.");
}
- if (!StackId.isAllowedToEnterPictureInPicture(r.getStack().getStackId())) {
- throw new IllegalStateException(caller
- + ": Activities on the home, assistant, or recents stack not supported");
- }
-
if (params.hasSetAspectRatio()
&& !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
params.getAspectRatio())) {
@@ -9869,8 +9877,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (tr.mBounds != null) {
rti.bounds = new Rect(tr.mBounds);
}
- rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen();
+ rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
rti.resizeMode = tr.mResizeMode;
+ rti.configuration.setTo(tr.getConfiguration());
ActivityRecord base = null;
ActivityRecord top = null;
@@ -10048,7 +10057,7 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"getTaskDescription()");
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
return tr.lastTaskDescription;
}
@@ -10161,7 +10170,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
- taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
+ taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
return;
@@ -10188,21 +10197,23 @@ public class ActivityManagerService extends IActivityManager.Stub
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- int stackId = task.getStackId();
+ ActivityStack stack = task.getStack();
if (!task.getWindowConfiguration().canResizeTask()) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
- if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
- stackId = FREEFORM_WORKSPACE_STACK_ID;
+ if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+ } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
}
// Reparent the task to the right stack if necessary
boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
- if (stackId != task.getStackId()) {
+ if (stack != task.getStack()) {
// Defer resume until the task is resized below
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
DEFER_RESUME, "resizeTask");
preserveWindow = false;
}
@@ -10224,7 +10235,7 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
return rect;
@@ -10256,7 +10267,7 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
+ MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
return;
@@ -10275,7 +10286,7 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
+ MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskThumbnailTransition: taskId=" + taskId + " not found");
return;
@@ -10295,7 +10306,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final TaskRecord task;
synchronized (this) {
task = mStackSupervisor.anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
@@ -10375,14 +10386,45 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void removeStack(int stackId) {
enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException("Removing home or recents stack is not allowed.");
+ synchronized (this) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack != null && !stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException(
+ "Removing non-standard stack is not allowed.");
+ }
+ mStackSupervisor.removeStackLocked(stackId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
+ }
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ @Override
+ public void removeStacksInWindowingModes(int[] windowingModes) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStackLocked(stackId);
+ mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public void removeStacksWithActivityTypes(int[] activityTypes) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()");
+ synchronized (this) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10532,13 +10574,15 @@ public class ActivityManagerService extends IActivityManager.Stub
public int createStackOnDisplay(int displayId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (this) {
- final int stackId = mStackSupervisor.getNextStackId();
- final ActivityStack stack =
- mStackSupervisor.createStackOnDisplay(stackId, displayId, true /*onTop*/);
- if (stack == null) {
+ final ActivityDisplay display =
+ mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
+ if (display == null) {
return INVALID_STACK_ID;
}
- return stack.mStackId;
+ // TODO(multi-display): Have the caller pass in the windowing mode and activity type.
+ final ActivityStack stack = display.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/);
+ return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
@@ -10570,8 +10614,12 @@ public class ActivityManagerService extends IActivityManager.Stub
"exitFreeformMode: You can only go fullscreen from freeform.");
}
+ final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
- r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ r.getTask().reparent(fullscreenStack, ON_TOP,
REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode");
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10580,29 +10628,35 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException(
- "moveTaskToStack: Attempt to move task " + taskId + " to stack " + stackId);
- }
+ public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (this) {
- long ident = Binder.clearCallingIdentity();
+ final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
- Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+ Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
return;
}
- if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
- + " to stackId=" + stackId + " toTop=" + toTop);
- if (stackId == DOCKED_STACK_ID) {
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ + " to windowingMode=" + windowingMode + " toTop=" + toTop);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
null /* initialBounds */);
}
- task.reparent(stackId, toTop,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack");
+
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task "
+ + taskId + " to non-standard windowin mode=" + windowingMode);
+ }
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final ActivityStack stack = display.getOrCreateStack(windowingMode,
+ task.getStack().getActivityType(), toTop);
+ // TODO: We should just change the windowing mode for the task vs. creating and
+ // moving it to a stack.
+ task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+ "moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10610,49 +10664,42 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void swapDockedAndFullscreenStack() throws RemoteException {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "swapDockedAndFullscreenStack()");
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack fullscreenStack = mStackSupervisor.getStack(
- FULLSCREEN_WORKSPACE_STACK_ID);
- final TaskRecord topTask = fullscreenStack != null ? fullscreenStack.topTask()
- : null;
- final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
- final ArrayList<TaskRecord> tasks = dockedStack != null ? dockedStack.getAllTasks()
- : null;
- if (topTask == null || tasks == null || tasks.size() == 0) {
- Slog.w(TAG,
- "Unable to swap tasks, either docked or fullscreen stack is empty.");
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
return;
}
- // TODO: App transition
- mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false);
-
- // Defer the resume until we move all the docked tasks to the fullscreen stack below
- topTask.reparent(DOCKED_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
- DEFER_RESUME, "swapDockedAndFullscreenStack - DOCKED_STACK");
- final int size = tasks.size();
- for (int i = 0; i < size; i++) {
- final int id = tasks.get(i).taskId;
- if (id == topTask.taskId) {
- continue;
- }
-
- // Defer the resume until after all the tasks have been moved
- tasks.get(i).reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, DEFER_RESUME,
- "swapDockedAndFullscreenStack - FULLSCREEN_STACK");
+ 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 */);
}
- // Because we deferred the resume to avoid conflicts with stack switches while
- // resuming, we need to do it after all the tasks are moved.
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
-
- mWindowManager.executeAppTransition();
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ 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);
+ }
+ task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+ "moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10685,13 +10732,18 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
return false;
}
-
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final ActivityStack stack = display.getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(),
+ toTop);
+
// Defer resuming until we move the home stack to the front below
- final boolean moved = task.reparent(DOCKED_STACK_ID, toTop,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ final boolean moved = task.reparent(stack, toTop,
REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
"moveTaskToDockedStack");
if (moved) {
@@ -10705,6 +10757,66 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * Dismisses split-screen multi-window mode.
+ * @param toTop If true the current primary split-screen stack will be placed or left on top.
+ */
+ @Override
+ public void dismissSplitScreenMode(boolean toTop) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityStack stack =
+ mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
+ if (toTop) {
+ mStackSupervisor.resizeStackLocked(stack.mStackId, null /* destBounds */,
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ true /* preserveWindows */, true /* allowResizeInDockedMode */,
+ !DEFER_RESUME);
+ } else {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Dismisses Pip
+ * @param animate True if the dismissal should be animated.
+ * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+ * default animation duration should be used.
+ */
+ @Override
+ public void dismissPip(boolean animate, int animationDuration) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final PinnedActivityStack stack =
+ mStackSupervisor.getDefaultDisplay().getPinnedStack();
+
+ if (stack == null) {
+ return;
+ }
+ if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ throw new IllegalArgumentException("Stack: " + stack
+ + " doesn't support animated resize.");
+ }
+ if (animate) {
+ stack.animateResizePinnedStack(null /* sourceHintBounds */,
+ null /* destBounds */, animationDuration, false /* fromFullscreen */);
+ } else {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
* Moves the top activity in the input stackId to the pinned stack.
*
* @param stackId Id of stack to move the top activity to pinned stack.
@@ -10739,17 +10851,16 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
if (animate) {
- if (stackId == PINNED_STACK_ID) {
- final PinnedActivityStack pinnedStack =
- mStackSupervisor.getStack(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */,
- destBounds, animationDuration, false /* fromFullscreen */);
- }
- } else {
+ final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+ if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalArgumentException("Stack: " + stackId
+ " doesn't support animated resize.");
}
+ stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+ animationDuration, false /* fromFullscreen */);
} else {
mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
@@ -10801,11 +10912,6 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException(
- "positionTaskInStack: Attempt to change the position of task "
- + taskId + " in/to home/recents stack");
- }
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
@@ -10817,8 +10923,16 @@ public class ActivityManagerService extends IActivityManager.Stub
+ taskId);
}
- final ActivityStack stack = mStackSupervisor.getStack(stackId, CREATE_IF_NEEDED,
- !ON_TOP);
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ if (stack == null) {
+ throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+ + stackId);
+ }
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+ + " the position of task " + taskId + " in/to non-standard stack");
+ }
// TODO: Have the callers of this API call a separate reparent method if that is
// what they intended to do vs. having this method also do reparenting.
@@ -10827,8 +10941,8 @@ public class ActivityManagerService extends IActivityManager.Stub
stack.positionChildAt(task, position);
} else {
// Reparent to new stack.
- task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE,
- !ANIMATE, !DEFER_RESUME, "positionTaskInStack");
+ task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+ !DEFER_RESUME, "positionTaskInStack");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10850,12 +10964,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public StackInfo getStackInfo(int stackId) {
+ public StackInfo getStackInfo(int windowingMode, int activityType) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- return mStackSupervisor.getStackInfoLocked(stackId);
+ return mStackSupervisor.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10882,7 +10996,6 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void updateLockTaskPackages(int userId, String[] packages) {
- // TODO: move this into LockTaskController
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
@@ -10891,8 +11004,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
Arrays.toString(packages));
- mLockTaskPackages.put(userId, packages);
- mLockTaskController.onLockTaskPackagesUpdated();
+ mLockTaskController.updateLockTaskPackages(userId, packages);
}
}
@@ -10908,11 +11020,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// When a task is locked, dismiss the pinned stack if it exists
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
// isAppPinning is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
@@ -11139,7 +11247,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId);
+ int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId);
if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
@@ -11527,7 +11635,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Make sure that the user who owns this provider is running. If not,
// we don't want to allow it to run.
- if (!mUserController.isUserRunningLocked(userId, 0)) {
+ if (!mUserController.isUserRunning(userId, 0)) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
@@ -12073,7 +12181,7 @@ public class ActivityManagerService extends IActivityManager.Stub
//mUsageStatsService.monitorPackages();
}
- private void startPersistentApps(int matchFlags) {
+ void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
synchronized (this) {
@@ -12094,7 +12202,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* When a user is unlocked, we need to install encryption-unaware providers
* belonging to any running apps.
*/
- private void installEncryptionUnawareProviders(int userId) {
+ void installEncryptionUnawareProviders(int userId) {
// We're only interested in providers that are encryption unaware, and
// we don't care about uninstalled apps, since there's no way they're
// running at this point.
@@ -12157,9 +12265,7 @@ public class ActivityManagerService extends IActivityManager.Stub
int callingPid = Binder.getCallingPid();
long ident = 0;
boolean clearedIdentity = false;
- synchronized (this) {
- userId = mUserController.unsafeConvertIncomingUserLocked(userId);
- }
+ userId = mUserController.unsafeConvertIncomingUser(userId);
if (canClearIdentity(callingPid, callingUid, userId)) {
clearedIdentity = true;
ident = Binder.clearCallingIdentity();
@@ -12399,19 +12505,14 @@ public class ActivityManagerService extends IActivityManager.Stub
void onWakefulnessChanged(int wakefulness) {
synchronized(this) {
+ boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
+ boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
mWakefulness = wakefulness;
- // Also update state in a special way for running foreground services UI.
- switch (mWakefulness) {
- case PowerManagerInternal.WAKEFULNESS_ASLEEP:
- case PowerManagerInternal.WAKEFULNESS_DREAMING:
- case PowerManagerInternal.WAKEFULNESS_DOZING:
- mServices.updateScreenStateLocked(false /* screenOn */);
- break;
- case PowerManagerInternal.WAKEFULNESS_AWAKE:
- default:
- mServices.updateScreenStateLocked(true /* screenOn */);
- break;
+ if (wasAwake != isAwake) {
+ // Also update state in a special way for running foreground services UI.
+ mServices.updateScreenStateLocked(isAwake);
+ sendNotifyVrManagerOfSleepState(!isAwake);
}
}
}
@@ -12447,7 +12548,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
if (wasSleeping) {
- sendNotifyVrManagerOfSleepState(false);
updateOomAdjLocked();
}
} else if (!mSleeping && shouldSleep) {
@@ -12457,7 +12557,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
mStackSupervisor.goingToSleepLocked();
- sendNotifyVrManagerOfSleepState(true);
updateOomAdjLocked();
}
}
@@ -12551,7 +12650,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void setLockScreenShown(boolean showing) {
+ public void setLockScreenShown(boolean showing, int secondaryDisplayShowing) {
if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
@@ -12561,11 +12660,12 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(this) {
long ident = Binder.clearCallingIdentity();
try {
- mKeyguardController.setKeyguardShown(showing);
+ mKeyguardController.setKeyguardShown(showing, secondaryDisplayShowing);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ sendNotifyVrManagerOfKeyguardState(showing);
}
@Override
@@ -12584,7 +12684,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mUserController.shouldConfirmCredentials(userId)) {
if (mKeyguardController.isKeyguardLocked()) {
// Showing launcher to avoid user entering credential twice.
- final int currentUserId = mUserController.getCurrentUserIdLocked();
+ final int currentUserId = mUserController.getCurrentUserId();
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
mStackSupervisor.lockAllProfileTasks(userId);
@@ -13982,10 +14082,10 @@ public class ActivityManagerService extends IActivityManager.Stub
mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
|| Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
- final boolean supportsPictureInPicture =
- mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext);
+ final boolean supportsPictureInPicture = supportsMultiWindow &&
+ mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final boolean supportsSplitScreenMultiWindow =
ActivityManager.supportsSplitScreenMultiWindow(mContext);
final boolean supportsMultiDisplay = mContext.getPackageManager()
@@ -14164,9 +14264,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
retrieveSettings();
- final int currentUserId;
+ final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
- currentUserId = mUserController.getCurrentUserIdLocked();
readGrantedUriPermissionsLocked();
}
@@ -14245,7 +14344,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
- mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
+ mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
traceLog.traceEnd(); // ActivityManagerStartApps
traceLog.traceEnd(); // PhaseActivityManagerReady
}
@@ -14597,6 +14696,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
sb.append("\n");
}
+ if (process.info.isInstantApp()) {
+ sb.append("Instant-App: true\n");
+ }
}
}
@@ -14943,6 +15045,13 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
+ }
+
+ /**
+ * Wrapper function to print out debug data filtered by specified arguments.
+ */
+ private void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
boolean dumpAll = false;
@@ -14951,6 +15060,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean dumpCheckinFormat = false;
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
+ boolean useProto = false;
String dumpPackage = null;
int opti = 0;
@@ -14984,12 +15094,26 @@ public class ActivityManagerService extends IActivityManager.Stub
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
+ } else if ("--proto".equals(opt)) {
+ useProto = true;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
}
long origId = Binder.clearCallingIdentity();
+
+ if (useProto) {
+ //TODO: Options when dumping proto
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ synchronized (this) {
+ writeActivitiesToProtoLocked(proto);
+ }
+ proto.flush();
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+
boolean more = false;
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
@@ -15333,6 +15457,10 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.restoreCallingIdentity(origId);
}
+ private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
+ mStackSupervisor.writeToProto(proto, ACTIVITIES);
+ }
+
private void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -19047,7 +19175,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
- if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
+ if (userId != UserHandle.USER_ALL && !mUserController.isUserRunning(userId, 0)) {
if ((callingUid != SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
@@ -19466,7 +19594,7 @@ public class ActivityManagerService extends IActivityManager.Stub
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
- users = mUserController.getStartedUserArrayLocked();
+ users = mUserController.getStartedUserArray();
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
@@ -20092,12 +20220,20 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public int getFocusedStackId() throws RemoteException {
- ActivityStack focusedStack = getFocusedStack();
- if (focusedStack != null) {
- return focusedStack.getStackId();
+ public StackInfo getFocusedStackInfo() throws RemoteException {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+ }
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- return -1;
}
public Configuration getConfiguration() {
@@ -20123,15 +20259,20 @@ public class ActivityManagerService extends IActivityManager.Stub
* activity and clearing the task at the same time.
*/
@Override
+ // TODO: API should just be about changing windowing modes...
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()");
- if (StackId.isHomeOrRecentsStack(fromStackId)) {
- throw new IllegalArgumentException("You can't move tasks from the home/recents stack.");
- }
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
try {
- mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+ final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+ if (stack != null){
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException(
+ "You can't move tasks from non-standard stacks.");
+ }
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -20229,7 +20370,7 @@ public class ActivityManagerService extends IActivityManager.Stub
void updateUserConfigurationLocked() {
final Configuration configuration = new Configuration(getGlobalConfiguration());
- final int currentUserId = mUserController.getCurrentUserIdLocked();
+ final int currentUserId = mUserController.getCurrentUserId();
Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
currentUserId, Settings.System.canWrite(mContext));
updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
@@ -20340,7 +20481,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
mUsageStatsService.reportConfigurationChange(mTempConfig,
- mUserController.getCurrentUserIdLocked());
+ mUserController.getCurrentUserId());
// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
mShowDialogs = shouldShowDialogs(mTempConfig);
@@ -22271,7 +22412,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String authority) {
if (app == null) return;
if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mUserController.getStartedUserStateLocked(app.userId);
+ UserState userState = mUserController.getStartedUserState(app.userId);
if (userState == null) return;
final long now = SystemClock.elapsedRealtime();
Long lastReported = userState.mProviderLastReportedFg.get(authority);
@@ -23566,54 +23707,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean switchUser(final int targetUserId) {
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
- int currentUserId;
- UserInfo targetUserInfo;
- synchronized (this) {
- currentUserId = mUserController.getCurrentUserIdLocked();
- targetUserInfo = mUserController.getUserInfo(targetUserId);
- if (targetUserId == currentUserId) {
- Slog.i(TAG, "user #" + targetUserId + " is already the current user");
- return true;
- }
- if (targetUserInfo == null) {
- Slog.w(TAG, "No user info for user #" + targetUserId);
- return false;
- }
- if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mContext)) {
- Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
- + " when device is in demo mode");
- return false;
- }
- if (!targetUserInfo.supportsSwitchTo()) {
- Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
- return false;
- }
- if (targetUserInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
- return false;
- }
- mUserController.setTargetUserIdLocked(targetUserId);
- }
- if (mUserController.mUserSwitchUiEnabled) {
- UserInfo currentUserInfo = mUserController.getUserInfo(currentUserId);
- Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
- mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
- mUiHandler.sendMessage(mHandler.obtainMessage(
- START_USER_SWITCH_UI_MSG, userNames));
- } else {
- mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(
- START_USER_SWITCH_FG_MSG, targetUserId, 0));
- }
- return true;
- }
-
- void scheduleStartProfilesLocked() {
- if (!mHandler.hasMessages(START_PROFILES_MSG)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
- DateUtils.SECOND_IN_MILLIS);
- }
+ return mUserController.switchUser(targetUserId);
}
@Override
@@ -23627,10 +23721,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
String getStartedUserState(int userId) {
- synchronized (this) {
- final UserState userState = mUserController.getStartedUserStateLocked(userId);
- return UserState.stateToString(userState.state);
- }
+ final UserState userState = mUserController.getStartedUserState(userId);
+ return UserState.stateToString(userState.state);
}
@Override
@@ -23645,9 +23737,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- synchronized (this) {
- return mUserController.isUserRunningLocked(userId, flags);
- }
+ return mUserController.isUserRunning(userId, flags);
}
@Override
@@ -23661,9 +23751,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- synchronized (this) {
- return mUserController.getStartedUserArrayLocked();
- }
+ return mUserController.getStartedUserArray();
}
@Override
@@ -23684,9 +23772,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
public boolean isUserStopped(int userId) {
- synchronized (this) {
- return mUserController.getStartedUserStateLocked(userId) == null;
- }
+ return mUserController.getStartedUserState(userId) == null;
}
ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
@@ -24025,7 +24111,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void notifyKeyguardTrustedChanged() {
synchronized (ActivityManagerService.this) {
- if (mKeyguardController.isKeyguardShowing()) {
+ if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
}
@@ -24274,7 +24360,7 @@ public class ActivityManagerService extends IActivityManager.Stub
permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
synchronized (this) {
if (mLastResumedActivity == null) {
- return mUserController.getCurrentUserIdLocked();
+ return mUserController.getCurrentUserId();
}
return mLastResumedActivity.userId;
}
@@ -24481,7 +24567,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, 0 /*flags*/, app.userId);
+ .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
app.thread.scheduleApplicationInfoChanged(ai);
}
diff --git a/com/android/server/am/ActivityManagerShellCommand.java b/com/android/server/am/ActivityManagerShellCommand.java
index 6901d2de..4c934232 100644
--- a/com/android/server/am/ActivityManagerShellCommand.java
+++ b/com/android/server/am/ActivityManagerShellCommand.java
@@ -75,6 +75,9 @@ 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;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
@@ -115,7 +118,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
private boolean mStreaming; // Streaming the profiling output to a file.
private String mAgent; // Agent to attach on startup.
private int mDisplayId;
- private int mStackId;
+ private int mWindowingMode;
+ private int mActivityType;
private int mTaskId;
private boolean mIsTaskOverlay;
@@ -271,7 +275,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
mStreaming = false;
mUserId = defUser;
mDisplayId = INVALID_DISPLAY;
- mStackId = INVALID_STACK_ID;
+ mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ mActivityType = ACTIVITY_TYPE_UNDEFINED;
mTaskId = INVALID_TASK_ID;
mIsTaskOverlay = false;
@@ -308,8 +313,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
mReceiverPermission = getNextArgRequired();
} else if (opt.equals("--display")) {
mDisplayId = Integer.parseInt(getNextArgRequired());
- } else if (opt.equals("--stack")) {
- mStackId = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--windowingMode")) {
+ mWindowingMode = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--activityType")) {
+ mActivityType = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--task")) {
mTaskId = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--task-overlay")) {
@@ -396,9 +403,17 @@ final class ActivityManagerShellCommand extends ShellCommand {
options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mDisplayId);
}
- if (mStackId != INVALID_STACK_ID) {
- options = ActivityOptions.makeBasic();
- options.setLaunchStackId(mStackId);
+ if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLaunchWindowingMode(mWindowingMode);
+ }
+ if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLaunchActivityType(mActivityType);
}
if (mTaskId != INVALID_TASK_ID) {
options = ActivityOptions.makeBasic();
@@ -2099,9 +2114,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
int runStackInfo(PrintWriter pw) throws RemoteException {
- String stackIdStr = getNextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- ActivityManager.StackInfo info = mInterface.getStackInfo(stackId);
+ int windowingMode = Integer.parseInt(getNextArgRequired());
+ int activityType = Integer.parseInt(getNextArgRequired());
+ ActivityManager.StackInfo info = mInterface.getStackInfo(windowingMode, activityType);
pw.println(info);
return 0;
}
@@ -2135,7 +2150,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
final String delayStr = getNextArg();
final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- ActivityManager.StackInfo info = mInterface.getStackInfo(DOCKED_STACK_ID);
+ 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;
@@ -2238,10 +2254,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runTaskResizeable(pw);
} else if (op.equals("resize")) {
return runTaskResize(pw);
- } else if (op.equals("drag-task-test")) {
- return runTaskDragTaskTest(pw);
- } else if (op.equals("size-task-test")) {
- return runTaskSizeTaskTest(pw);
} else if (op.equals("focus")) {
return runTaskFocus(pw);
} else {
@@ -2294,58 +2306,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
}
- int runTaskDragTaskTest(PrintWriter pw) throws RemoteException {
- final int taskId = Integer.parseInt(getNextArgRequired());
- final int stepSize = Integer.parseInt(getNextArgRequired());
- final String delayStr = getNextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final ActivityManager.StackInfo stackInfo;
- Rect taskBounds;
- stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId());
- taskBounds = mInterface.getTaskBounds(taskId);
- final Rect stackBounds = stackInfo.bounds;
- int travelRight = stackBounds.width() - taskBounds.width();
- int travelLeft = -travelRight;
- int travelDown = stackBounds.height() - taskBounds.height();
- int travelUp = -travelDown;
- int passes = 0;
-
- // We do 2 passes to get back to the original location of the task.
- while (passes < 2) {
- // Move right
- pw.println("Moving right...");
- pw.flush();
- travelRight = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelRight, MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel right by " + travelRight);
-
- // Move down
- pw.println("Moving down...");
- pw.flush();
- travelDown = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelDown, MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel down by " + travelDown);
-
- // Move left
- pw.println("Moving left...");
- pw.flush();
- travelLeft = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelLeft, !MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel left by " + travelLeft);
-
- // Move up
- pw.println("Moving up...");
- pw.flush();
- travelUp = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelUp, !MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel up by " + travelUp);
-
- taskBounds = mInterface.getTaskBounds(taskId);
- passes++;
- }
- return 0;
- }
-
int moveTask(int taskId, Rect taskRect, Rect stackRect, int stepSize,
int maxToTravel, boolean movingForward, boolean horizontal, int delay_ms)
throws RemoteException {
@@ -2408,133 +2368,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
return stepSize;
}
- int runTaskSizeTaskTest(PrintWriter pw) throws RemoteException {
- final int taskId = Integer.parseInt(getNextArgRequired());
- final int stepSize = Integer.parseInt(getNextArgRequired());
- final String delayStr = getNextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final ActivityManager.StackInfo stackInfo;
- final Rect initialTaskBounds;
- stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId());
- initialTaskBounds = mInterface.getTaskBounds(taskId);
- final Rect stackBounds = stackInfo.bounds;
- stackBounds.inset(STACK_BOUNDS_INSET, STACK_BOUNDS_INSET);
- final Rect currentTaskBounds = new Rect(initialTaskBounds);
-
- // Size by top-left
- pw.println("Growing top-left");
- pw.flush();
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- pw.println("Shrinking top-left");
- pw.flush();
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by top-right
- pw.println("Growing top-right");
- pw.flush();
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- pw.println("Shrinking top-right");
- pw.flush();
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.right < currentTaskBounds.right);
-
- // Size by bottom-left
- pw.println("Growing bottom-left");
- pw.flush();
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- pw.println("Shrinking bottom-left");
- pw.flush();
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by bottom-right
- pw.println("Growing bottom-right");
- pw.flush();
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- pw.println("Shrinking bottom-right");
- pw.flush();
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.right < currentTaskBounds.right);
- return 0;
- }
-
int runTaskFocus(PrintWriter pw) throws RemoteException {
final int taskId = Integer.parseInt(getNextArgRequired());
pw.println("Setting focus to task " + taskId);
@@ -2661,6 +2494,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" -p: limit output to given package.");
pw.println(" --checkin: output checkin format, resetting data.");
pw.println(" --C: output checkin format, not resetting data.");
+ pw.println(" --proto: output dump in protocol buffer format.");
} else {
pw.println("Activity manager (activity) commands:");
pw.println(" help");
@@ -2685,7 +2519,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" --track-allocation: enable tracking of object allocations");
pw.println(" --user <USER_ID> | current: Specify which user to run as; if not");
pw.println(" specified then run as the current user.");
- pw.println(" --stack <STACK_ID>: Specify into which stack should the activity be put.");
+ pw.println(" --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into.");
+ pw.println(" --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as.");
pw.println(" start-service [--user <USER_ID> | current] <INTENT>");
pw.println(" Start a Service. Options are:");
pw.println(" --user <USER_ID> | current: Specify which user to run as; if not");
@@ -2864,8 +2699,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Place <TASK_ID> in <STACK_ID> at <POSITION>");
pw.println(" list");
pw.println(" List all of the activity stacks and their sizes.");
- pw.println(" info <STACK_ID>");
- pw.println(" Display the information about activity stack <STACK_ID>.");
+ pw.println(" info <WINDOWING_MODE> <ACTIVITY_TYPE>");
+ pw.println(" Display the information about activity stack in <WINDOWING_MODE> and <ACTIVITY_TYPE>.");
pw.println(" remove <STACK_ID>");
pw.println(" Remove stack <STACK_ID>.");
pw.println(" task [COMMAND] [...]: sub-commands for operating on activity tasks.");
@@ -2883,14 +2718,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Makes sure <TASK_ID> is in a stack with the specified bounds.");
pw.println(" Forces the task to be resizeable and creates a stack if no existing stack");
pw.println(" has the specified bounds.");
- pw.println(" drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]");
- pw.println(" Test command for dragging/moving <TASK_ID> by");
- pw.println(" <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]");
- pw.println(" between each step.");
- pw.println(" size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]");
- pw.println(" Test command for sizing <TASK_ID> by <STEP_SIZE>");
- pw.println(" increments within the screen applying the optional [DELAY_MS] between");
- pw.println(" each step.");
pw.println(" update-appinfo <USER_ID> <PACKAGE_NAME> [<PACKAGE_NAME>...]");
pw.println(" Update the ApplicationInfo objects of the listed packages for <USER_ID>");
pw.println(" without restarting any processes.");
diff --git a/com/android/server/am/ActivityMetricsLogger.java b/com/android/server/am/ActivityMetricsLogger.java
index 0c8321d5..fdcb8c69 100644
--- a/com/android/server/am/ActivityMetricsLogger.java
+++ b/com/android/server/am/ActivityMetricsLogger.java
@@ -2,12 +2,9 @@ package com.android.server.am;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
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;
@@ -32,13 +29,10 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
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.ActivityStack.STACK_INVISIBLE;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.SystemClock;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
diff --git a/com/android/server/am/ActivityRecord.java b/com/android/server/am/ActivityRecord.java
index 0ccb45f7..7b0b942a 100644
--- a/com/android/server/am/ActivityRecord.java
+++ b/com/android/server/am/ActivityRecord.java
@@ -17,8 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-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.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
@@ -36,7 +34,6 @@ import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
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.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.content.Intent.ACTION_MAIN;
@@ -89,10 +86,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SCREENSHOTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_THUMBNAILS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -107,7 +102,6 @@ import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.LAUNCH_TICK;
import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
@@ -116,6 +110,16 @@ import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
import static com.android.server.am.TaskPersister.DEBUG;
import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.proto.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.proto.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.proto.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.proto.ActivityRecordProto.STATE;
+import static com.android.server.am.proto.ActivityRecordProto.VISIBLE;
+import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.proto.IdentifierProto.TITLE;
+import static com.android.server.wm.proto.IdentifierProto.USER_ID;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -153,6 +157,7 @@ import android.util.Log;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
@@ -1038,7 +1043,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
} else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
activityType = ACTIVITY_TYPE_RECENTS;
- } else if (options != null && options.getLaunchStackId() == ASSISTANT_STACK_ID
+ } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
activityType = ACTIVITY_TYPE_ASSISTANT;
}
@@ -1062,6 +1067,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
}
+ ActivityDisplay getDisplay() {
+ final ActivityStack stack = getStack();
+ return stack != null ? stack.getDisplay() : null;
+ }
+
boolean changeWindowTranslucency(boolean toOpaque) {
if (fullscreen == toOpaque) {
return false;
@@ -1127,10 +1137,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
* @return whether this activity supports split-screen multi-window and can be put in the docked
* stack.
*/
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
- return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ return super.supportsSplitScreenWindowingMode()
+ && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1157,8 +1169,15 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
* can be put a secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
+ final TaskRecord task = getTask();
+
+ // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
+ // This allows for a non resizable activity to be launched into a resizeable task.
+ final boolean resizeable =
+ task != null ? task.isResizeable() : supportsResizeableMultiWindow();
+
return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- supportsResizeableMultiWindow(), launchedFromPid, launchedFromUid, info);
+ resizeable, launchedFromPid, launchedFromUid, info);
}
/**
@@ -1184,7 +1203,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
boolean isKeyguardLocked = service.isKeyguardLocked();
boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
- boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+ final ActivityDisplay display = getDisplay();
+ boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
// is in an incorrect state
boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
@@ -1530,8 +1550,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) {
// 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.
- isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
- || mStackSupervisor.isFocusedStack(getStack());
+ final ActivityDisplay display = getDisplay();
+ boolean hasSplitScreenStack = display != null && display.hasSplitScreenStack();
+ isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack());
}
return isVisible;
@@ -1934,7 +1955,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
|| (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mUserController.isUserRunningLocked(userId, 0 /* flags */));
+ && service.mUserController.isUserRunning(userId, 0 /* flags */));
}
/**
@@ -2298,7 +2319,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// be visible based on the stack, task, and lockscreen state and use that here instead. The
// method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked().
// Skip updating configuration for activity is a stack that shouldn't be visible.
- if (stack.shouldBeVisible(null /* starting */) == STACK_INVISIBLE) {
+ if (!stack.shouldBeVisible(null /* starting */)) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check invisible stack: " + this);
return true;
@@ -2770,4 +2791,25 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
stringName = sb.toString();
return toString();
}
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, userId);
+ proto.write(TITLE, intent.getComponent().flattenToShortString());
+ proto.end(token);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER);
+ writeIdentifierToProto(proto, IDENTIFIER);
+ proto.write(STATE, state.toString());
+ proto.write(VISIBLE, visible);
+ proto.write(FRONT_OF_TASK, frontOfTask);
+ if (app != null) {
+ proto.write(PROC_ID, app.pid);
+ }
+ proto.end(token);
+ }
}
diff --git a/com/android/server/am/ActivityStack.java b/com/android/server/am/ActivityStack.java
index a6a702fb..1940ca2b 100644
--- a/com/android/server/am/ActivityStack.java
+++ b/com/android/server/am/ActivityStack.java
@@ -16,19 +16,19 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
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.HOME_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.getActivityTypeForStackId;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
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_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.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
@@ -37,6 +37,8 @@ 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 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;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
@@ -74,10 +76,16 @@ import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.proto.ActivityStackProto.BOUNDS;
+import static com.android.server.am.proto.ActivityStackProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.ActivityStackProto.DISPLAY_ID;
+import static com.android.server.am.proto.ActivityStackProto.FULLSCREEN;
+import static com.android.server.am.proto.ActivityStackProto.ID;
+import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY;
+import static com.android.server.am.proto.ActivityStackProto.TASKS;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
@@ -86,6 +94,7 @@ import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
+
import static java.lang.Integer.MAX_VALUE;
import android.app.Activity;
@@ -101,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;
@@ -122,6 +130,7 @@ import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
@@ -231,11 +240,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
DESTROYED
}
- // Stack is not considered visible.
- static final int STACK_INVISIBLE = 0;
- // Stack is considered visible
- static final int STACK_VISIBLE = 1;
-
@VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
// Task is being completely removed from all stacks in the system.
@@ -258,7 +262,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityManagerService mService;
private final WindowManagerService mWindowManager;
T mWindowContainerController;
- private final RecentTasks mRecentTasks;
/**
* The back history of all previous (and possibly still
@@ -341,9 +344,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
int mCurrentUser;
final int mStackId;
- /** The other stacks, in order, on the attached display. Updated at attach/detach time. */
- // TODO: This list doesn't belong here...
- ArrayList<ActivityStack> mStacks;
/** The attached Display's unique identifier, or -1 if detached */
int mDisplayId;
@@ -452,49 +452,35 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return count;
}
- ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
- ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
+ ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+ int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
mWindowManager = mService.mWindowManager;
mStackId = stackId;
- mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
- mRecentTasks = recentTasks;
+ mCurrentUser = mService.mUserController.getCurrentUserId();
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
mTmpRect2.setEmpty();
- updateOverrideConfiguration();
+ setWindowingMode(windowingMode);
+ setActivityType(activityType);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
- mStackSupervisor.mStacks.put(mStackId, this);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}
T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
- return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
T getWindowContainerController() {
return mWindowContainerController;
}
- // TODO: Not needed once we are no longer using stack ids as the override config. can be passed
- // in.
- private void updateOverrideConfiguration() {
- final int windowingMode = getWindowingModeForStackId(
- mStackId, mStackSupervisor.getStack(DOCKED_STACK_ID) != null);
- if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- setWindowingMode(windowingMode);
- }
- final int activityType = getActivityTypeForStackId(mStackId);
- if (activityType != ACTIVITY_TYPE_UNDEFINED) {
- setActivityType(activityType);
- }
- }
-
/** Adds the stack to specified display and calls WindowManager to do the same. */
- void reparent(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
+ void reparent(ActivityDisplay activityDisplay, boolean onTop) {
removeFromDisplay();
mTmpRect2.setEmpty();
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -512,10 +498,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* @param activityDisplay New display to which this stack was attached.
* @param bounds Updated bounds.
*/
- private void postAddToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay,
- Rect bounds, boolean onTop) {
+ private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
mDisplayId = activityDisplay.mDisplayId;
- mStacks = activityDisplay.mStacks;
mBounds = bounds != null ? new Rect(bounds) : null;
mFullscreen = mBounds == null;
if (mTaskPositioner != null) {
@@ -524,7 +508,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
onParentChanged();
- activityDisplay.attachStack(this, findStackInsertIndex(onTop));
+ activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
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.
@@ -538,42 +522,36 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* either destroyed completely or re-parented.
*/
private void removeFromDisplay() {
- final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+ if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // If we removed a docked stack we want to resize it so it resizes all other stacks
+ // in the system to fullscreen.
+ mStackSupervisor.resizeDockedStackLocked(
+ null, null, null, null, null, PRESERVE_WINDOWS);
+ }
+ final ActivityDisplay display = getDisplay();
if (display != null) {
- display.detachStack(this);
+ display.removeChild(this);
}
mDisplayId = INVALID_DISPLAY;
- mStacks = null;
if (mTaskPositioner != null) {
mTaskPositioner.reset();
}
- if (mStackId == DOCKED_STACK_ID) {
- // If we removed a docked stack we want to resize it so it resizes all other stacks
- // in the system to fullscreen.
- mStackSupervisor.resizeDockedStackLocked(
- null, null, null, null, null, PRESERVE_WINDOWS);
- }
}
/** Removes the stack completely. Also calls WindowManager to do the same on its side. */
void remove() {
removeFromDisplay();
- mStackSupervisor.mStacks.remove(mStackId);
mWindowContainerController.removeContainer();
mWindowContainerController = null;
onParentChanged();
}
- ActivityStackSupervisor.ActivityDisplay getDisplay() {
+ ActivityDisplay getDisplay() {
return mStackSupervisor.getActivityDisplay(mDisplayId);
}
- void getDisplaySize(Point out) {
- getDisplay().mDisplay.getSize(out);
- }
-
/**
- * @see ActivityStack.getStackDockedModeBounds(Rect, Rect, Rect, boolean)
+ * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean)
*/
void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
Rect outTempTaskBounds, boolean ignoreVisibility) {
@@ -837,7 +815,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
final boolean isHomeOrRecentsStack() {
- return StackId.isHomeOrRecentsStack(mStackId);
+ return isActivityTypeHome() || isActivityTypeRecents();
}
final boolean isDockedStack() {
@@ -849,7 +827,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
final boolean isOnHomeDisplay() {
- return isAttached() && mDisplayId == DEFAULT_DISPLAY;
+ return mDisplayId == DEFAULT_DISPLAY;
}
void moveToFront(String reason) {
@@ -865,8 +843,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return;
}
- mStacks.remove(this);
- mStacks.add(findStackInsertIndex(ON_TOP), this);
+ getDisplay().positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
insertTaskAtTop(task, null);
@@ -880,45 +857,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- /**
- * @param task If non-null, the task will be moved to the back of the stack.
- * */
- private void moveToBack(TaskRecord task) {
- if (!isAttached()) {
- return;
- }
-
- mStacks.remove(this);
- mStacks.add(0, this);
-
- if (task != null) {
- mTaskHistory.remove(task);
- mTaskHistory.add(0, task);
- updateTaskMovement(task, false);
- mWindowContainerController.positionChildAtBottom(task.getWindowContainerController());
- }
- }
-
- /**
- * @return the index to insert a new stack into, taking the always-on-top stacks into account.
- */
- private int findStackInsertIndex(boolean onTop) {
- if (onTop) {
- int addIndex = mStacks.size();
- if (addIndex > 0) {
- final ActivityStack topStack = mStacks.get(addIndex - 1);
- if (topStack.getWindowConfiguration().isAlwaysOnTop()
- && topStack != this) {
- // If the top stack is always on top, we move this stack just below it.
- addIndex--;
- }
- }
- return addIndex;
- } else {
- return 0;
- }
- }
-
boolean isFocusable() {
if (getWindowConfiguration().canReceiveKeys()) {
return true;
@@ -930,7 +868,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
final boolean isAttached() {
- return mStacks != null;
+ return getParent() != null;
}
/**
@@ -1105,14 +1043,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
"Launch completed; removing icicle of " + r.icicle);
}
- void addRecentActivityLocked(ActivityRecord r) {
- if (r != null) {
- final TaskRecord task = r.getTask();
- mRecentTasks.addLocked(task);
- task.touchActiveTime();
- }
- }
-
private void startLaunchTraces(String packageName) {
if (mFullyDrawnStartTime != 0) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
@@ -1527,7 +1457,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
+ || getDisplay().hasPinnedStack()) {
mService.mTaskChangeNotificationController.notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
@@ -1559,54 +1489,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- // Find the first visible activity above the passed activity and if it is translucent return it
- // otherwise return null;
- ActivityRecord findNextTranslucentActivity(ActivityRecord r) {
- TaskRecord task = r.getTask();
- if (task == null) {
- return null;
- }
-
- final ActivityStack stack = task.getStack();
- if (stack == null) {
- return null;
- }
-
- int stackNdx = mStacks.indexOf(stack);
-
- ArrayList<TaskRecord> tasks = stack.mTaskHistory;
- int taskNdx = tasks.indexOf(task);
-
- ArrayList<ActivityRecord> activities = task.mActivities;
- int activityNdx = activities.indexOf(r) + 1;
-
- final int numStacks = mStacks.size();
- while (stackNdx < numStacks) {
- final ActivityStack historyStack = mStacks.get(stackNdx);
- tasks = historyStack.mTaskHistory;
- final int numTasks = tasks.size();
- while (taskNdx < numTasks) {
- final TaskRecord currentTask = tasks.get(taskNdx);
- activities = currentTask.mActivities;
- final int numActivities = activities.size();
- while (activityNdx < numActivities) {
- final ActivityRecord activity = activities.get(activityNdx);
- if (!activity.finishing) {
- return historyStack.mFullscreen
- && currentTask.mFullscreen && activity.fullscreen ? null : activity;
- }
- ++activityNdx;
- }
- activityNdx = 0;
- ++taskNdx;
- }
- taskNdx = 0;
- ++stackNdx;
- }
-
- return null;
- }
-
/** Returns true if the stack contains a fullscreen task. */
private boolean hasFullscreenTask() {
for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
@@ -1650,9 +1532,11 @@ 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()
- && !StackId.isHomeOrRecentsStack(stackBehindId)
- && !isActivityTypeAssistant()) {
+ && !stackBehindHomeOrRecent && !isActivityTypeAssistant()) {
// Stack isn't translucent if it's top activity should have the home stack
// behind it and the stack currently behind it isn't the home or recents stack
// or the assistant stack.
@@ -1670,119 +1554,146 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
/**
- * Returns what the stack visibility should be: {@link #STACK_INVISIBLE} or
- * {@link #STACK_VISIBLE}.
+ * Returns true if the stack should be visible.
*
* @param starting The currently starting activity or null if there is none.
*/
- int shouldBeVisible(ActivityRecord starting) {
+ boolean shouldBeVisible(ActivityRecord starting) {
if (!isAttached() || mForceHidden) {
- return STACK_INVISIBLE;
+ return false;
}
if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) {
- return STACK_VISIBLE;
+ return true;
}
- final int stackIndex = mStacks.indexOf(this);
+ final ActivityDisplay display = getDisplay();
+ final ArrayList<ActivityStack> displayStacks = display.mStacks;
+ final int stackIndex = displayStacks.indexOf(this);
- if (stackIndex == mStacks.size() - 1) {
+ if (stackIndex == displayStacks.size() - 1) {
Slog.wtf(TAG,
"Stack=" + this + " isn't front stack but is at the top of the stack list");
- return STACK_INVISIBLE;
+ return false;
}
// Check position and visibility of this stack relative to the front stack on its display.
final ActivityStack topStack = getTopStackOnDisplay();
final int topStackId = topStack.mStackId;
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
- if (mStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isActivityTypeAssistant()) {
- return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
- : STACK_INVISIBLE;
+ return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
}
- return STACK_VISIBLE;
+ return true;
}
// Set home stack to invisible when it is below but not immediately below the docked stack
// 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 (mStackId == HOME_STACK_ID) {
- int dockedStackIndex = mStacks.indexOf(mStackSupervisor.getStack(DOCKED_STACK_ID));
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ 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 STACK_INVISIBLE;
+ return false;
}
}
// Find the first stack behind front stack that actually got something visible.
- int stackBehindTopIndex = mStacks.indexOf(topStack) - 1;
+ int stackBehindTopIndex = displayStacks.indexOf(topStack) - 1;
while (stackBehindTopIndex >= 0 &&
- mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
+ displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
- final int stackBehindTopId = (stackBehindTopIndex >= 0)
- ? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+ final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
+ ? 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 (topStackId == DOCKED_STACK_ID || alwaysOnTop) {
+ if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
if (stackIndex == stackBehindTopIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
- return STACK_VISIBLE;
+ return true;
} else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
// Otherwise, this stack can also be visible if it is directly behind a docked stack
// or translucent assistant stack behind an always-on-top top-most stack
- if (stackBehindTopId == DOCKED_STACK_ID) {
- return STACK_VISIBLE;
- } else if (stackBehindTopId == ASSISTANT_STACK_ID) {
- return mStacks.get(stackBehindTopIndex).isStackTranslucent(starting, mStackId)
- ? STACK_VISIBLE : STACK_INVISIBLE;
+ if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return true;
+ } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
+ return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
+ starting, mStackId);
}
}
}
- if (StackId.isBackdropToTranslucentActivity(topStackId)
+ if (topStack.isBackdropToTranslucentActivity()
&& 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
if (stackIndex == stackBehindTopIndex) {
- return STACK_VISIBLE;
+ return true;
}
if (stackBehindTopIndex >= 0) {
- if ((stackBehindTopId == DOCKED_STACK_ID
- || stackBehindTopId == PINNED_STACK_ID)
+ if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED)
&& stackIndex == (stackBehindTopIndex - 1)) {
// The stack behind the docked or pinned stack is also visible so we can have a
// complete backdrop to the translucent activity when the docked stack is up.
- return STACK_VISIBLE;
+ return true;
}
}
}
- if (StackId.isStaticStack(mStackId)) {
+ if (StackId.isStaticStack(mStackId)
+ || isHomeOrRecentsStack() || isActivityTypeAssistant()) {
// Visibility of any static stack should have been determined by the conditions above.
- return STACK_INVISIBLE;
+ return false;
}
- for (int i = stackIndex + 1; i < mStacks.size(); i++) {
- final ActivityStack stack = mStacks.get(i);
+ for (int i = stackIndex + 1; i < displayStacks.size(); i++) {
+ final ActivityStack stack = displayStacks.get(i);
if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
continue;
}
- if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
+ if (!stack.isDynamicStacksVisibleBehindAllowed()) {
// These stacks can't have any dynamic stacks visible behind them.
- return STACK_INVISIBLE;
+ return false;
}
if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
- return STACK_INVISIBLE;
+ return false;
}
}
- return STACK_VISIBLE;
+ return true;
+ }
+
+ private boolean isBackdropToTranslucentActivity() {
+ if (isActivityTypeAssistant()) {
+ return true;
+ }
+ final int windowingMode = getWindowingMode();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ private boolean isDynamicStacksVisibleBehindAllowed() {
+ return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED;
}
final int rankTaskLayers(int baseLayer) {
@@ -1819,12 +1730,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
- final int stackVisibility = shouldBeVisible(starting);
- final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
- boolean behindFullscreenActivity = stackInvisible;
+ final boolean stackShouldBeVisible = shouldBeVisible(starting);
+ boolean behindFullscreenActivity = !stackShouldBeVisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
- boolean behindTranslucentActivity = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1848,11 +1757,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final boolean reallyVisible = checkKeyguardVisibility(r,
visibleIgnoringKeyguard, isTop);
if (visibleIgnoringKeyguard) {
- behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
+ behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, task, r);
- if (behindFullscreenActivity && !r.fullscreen) {
- behindTranslucentActivity = true;
- }
}
if (reallyVisible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
@@ -1888,10 +1794,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
configChanges |= r.configChangeFlags;
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
- + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
- + stackInvisible + " behindFullscreenActivity="
- + behindFullscreenActivity + " mLaunchTaskBehind="
- + r.mLaunchTaskBehind);
+ + " finishing=" + r.finishing + " state=" + r.state
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity
+ + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
makeInvisible(r);
}
}
@@ -1899,10 +1805,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// 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.
- behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
- } else if (mStackId == HOME_STACK_ID) {
+ behindFullscreenActivity = !stackShouldBeVisible;
+ } else if (isActivityTypeHome()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
- + " stackInvisible=" + stackInvisible
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// No other task in the home stack should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
@@ -1962,7 +1868,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
- final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing();
+ final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
+ mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
@@ -2002,7 +1909,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
*/
private boolean canShowWithInsecureKeyguard() {
- final ActivityStackSupervisor.ActivityDisplay activityDisplay = getDisplay();
+ final ActivityDisplay activityDisplay = getDisplay();
if (activityDisplay == null) {
throw new IllegalStateException("Stack is not attached to any display, stackId="
+ mStackId);
@@ -2182,7 +2089,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// activities as we need to display their starting window until they are done initializing.
boolean behindFullscreenActivity = false;
- if (shouldBeVisible(null) == STACK_INVISIBLE) {
+ if (!shouldBeVisible(null)) {
// The stack is not visible, so no activity in it should be displaying a starting
// window. Mark all activities below top and behind fullscreen.
aboveTop = false;
@@ -2258,9 +2165,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mResumedActivity = r;
r.state = ActivityState.RESUMED;
mService.setResumedActivityUncheckLocked(r, reason);
- final TaskRecord task = r.getTask();
- task.touchActiveTime();
- mRecentTasks.addLocked(task);
+ mStackSupervisor.addRecentActivity(r);
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
@@ -2543,128 +2448,139 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
|| (lastStack.mLastPausedActivity != null
&& !lastStack.mLastPausedActivity.fullscreen));
- // This activity is now becoming visible.
- if (!next.visible || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
+ // The contained logic must be synchronized, since we are both changing the visibility
+ // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
+ // ultimately cause the client code to schedule a layout. Since layouts retrieve the
+ // current {@link Configuration}, we must ensure that the below code updates it before
+ // the layout can occur.
+ synchronized(mWindowManager.getWindowManagerLock()) {
+ // This activity is now becoming visible.
+ if (!next.visible || next.stopped || lastActivityTranslucent) {
+ next.setVisibility(true);
+ }
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
- ActivityRecord lastResumedActivity =
- lastStack == null ? null :lastStack.mResumedActivity;
- ActivityState lastState = next.state;
+ ActivityRecord lastResumedActivity =
+ lastStack == null ? null :lastStack.mResumedActivity;
+ ActivityState lastState = next.state;
- mService.updateCpuStats();
+ mService.updateCpuStats();
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + " (in existing)");
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ + " (in existing)");
- setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
+ setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
- mService.updateLruProcessLocked(next.app, true, null);
- updateLRUListLocked(next);
- mService.updateOomAdjLocked();
+ mService.updateLruProcessLocked(next.app, true, null);
+ updateLRUListLocked(next);
+ mService.updateOomAdjLocked();
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
- if (mStackSupervisor.isFocusedStack(this)) {
-
- // We have special rotation behavior when Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed to
- // get the correct rotation behavior.
- // TODO: Remove this once visibilities are set correctly immediately when starting
- // an activity.
- if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */);
- }
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
- false /* deferResume */, mDisplayId);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivityLocked();
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mStackSupervisor.scheduleResumeTopActivities();
- }
- if (!next.visible || next.stopped) {
- next.setVisibility(true);
- }
- next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
- try {
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- next.app.thread.scheduleSendResult(next.appToken, a);
+ if (mStackSupervisor.isFocusedStack(this)) {
+
+ // We have special rotation behavior when Keyguard is locked. Make sure all
+ // activity visibilities are set correctly as well as the transition is updated
+ // if needed to get the correct rotation behavior.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
}
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null,
+ mDisplayId);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
+ false /* deferResume */, mDisplayId);
}
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(
- next.newIntents, next.appToken, false /* andPause */);
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked();
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mStackSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.visible || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
-
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
- System.identityHashCode(next), next.getTask().taskId,
- next.shortComponentName);
+ try {
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+ "Delivering results to " + next + ": " + a);
+ next.app.thread.scheduleSendResult(next.appToken, a);
+ }
+ }
- next.sleeping = false;
- mService.showUnsupportedZoomDialogIfNeededLocked(next);
- mService.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(mService.mTopProcessState);
- next.clearOptionsLocked();
- next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward(), resumeAnimOptions);
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(
+ next.newIntents, next.appToken, false /* andPause */);
+ }
- if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
- + lastState + ": " + next);
- next.state = lastState;
- if (lastStack != null) {
- lastStack.mResumedActivity = lastResumedActivity;
- }
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
- mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwitch */);
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
+
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
+ System.identityHashCode(next), next.getTask().taskId,
+ next.shortComponentName);
+
+ next.sleeping = false;
+ mService.showUnsupportedZoomDialogIfNeededLocked(next);
+ mService.showAskCompatModeDialogLocked(next);
+ next.app.pendingUiClean = true;
+ next.app.forceProcessStateUpTo(mService.mTopProcessState);
+ next.clearOptionsLocked();
+ next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
+ mService.isNextTransitionForward(), resumeAnimOptions);
+
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ + next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
+ + lastState + ": " + next);
+ next.state = lastState;
+ if (lastStack != null) {
+ lastStack.mResumedActivity = lastResumedActivity;
+ }
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
+ mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
+ next.showStartingWindow(null /* prev */, false /* newTask */,
+ false /* taskSwitch */);
+ }
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
}
// From this point on, if something goes wrong there is no way
@@ -2989,9 +2905,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Ensure that we do not trigger entering PiP an activity on the pinned stack
return false;
}
- final int targetStackId = toFrontTask != null ? toFrontTask.getStackId()
- : toFrontActivity.getStackId();
- if (targetStackId == ASSISTANT_STACK_ID) {
+ final ActivityStack targetStack = toFrontTask != null
+ ? toFrontTask.getStack() : toFrontActivity.getStack();
+ if (targetStack != null && targetStack.isActivityTypeAssistant()) {
// Ensure the task/activity being brought forward is not the assistant
return false;
}
@@ -4548,7 +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()) {
- addRecentActivityLocked(top);
+ mStackSupervisor.addRecentActivity(top);
ActivityOptions.abort(options);
return;
}
@@ -4601,7 +4517,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
Slog.i(TAG, "moveTaskToBack: " + tr);
// If the task is locked, then show the lock task toast
- if (!mService.mLockTaskController.checkLockedTask(tr)) {
+ if (mService.mLockTaskController.checkLockedTask(tr)) {
return false;
}
@@ -4982,8 +4898,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
ci.numActivities = numActivities;
ci.numRunning = numRunning;
- ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen();
+ ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
ci.resizeMode = task.mResizeMode;
+ ci.configuration.setTo(task.getConfiguration());
list.add(ci);
}
}
@@ -5152,8 +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.
- mRecentTasks.remove(task);
- task.removedFromRecents();
+ mStackSupervisor.removeTaskFromRecents(task);
}
task.removeWindowContainer();
@@ -5170,11 +5086,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
- if (mStacks != null) {
- mStacks.remove(this);
- mStacks.add(0, this);
+ if (isAttached()) {
+ getDisplay().positionChildAtBottom(this);
}
- if (!isHomeOrRecentsStack()) {
+ if (!isActivityTypeHome()) {
remove();
}
}
@@ -5194,8 +5109,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
- final boolean isLockscreenShown =
- mService.mStackSupervisor.mKeyguardController.isKeyguardShowing();
+ final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
+ .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
&& !isLockscreenShown) {
task.updateOverrideConfiguration(mBounds);
@@ -5349,11 +5264,30 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
boolean shouldSleepActivities() {
- final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+ final ActivityDisplay display = getDisplay();
return display != null ? display.isSleeping() : mService.isSleepingLocked();
}
boolean shouldSleepOrShutDownActivities() {
return shouldSleepActivities() || mService.isShuttingDownLocked();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER);
+ proto.write(ID, mStackId);
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ task.writeToProto(proto, TASKS);
+ }
+ if (mResumedActivity != null) {
+ mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ proto.write(DISPLAY_ID, mDisplayId);
+ if (mBounds != null) {
+ mBounds.writeToProto(proto, BOUNDS);
+ }
+ proto.write(FULLSCREEN, mFullscreen);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/am/ActivityStackSupervisor.java b/com/android/server/am/ActivityStackSupervisor.java
index fe28956d..da2827a6 100644
--- a/com/android/server/am/ActivityStackSupervisor.java
+++ b/com/android/server/am/ActivityStackSupervisor.java
@@ -23,30 +23,29 @@ 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.FIRST_STATIC_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.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_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;
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;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
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;
@@ -81,19 +80,24 @@ import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import static com.android.server.am.ActivityStack.STACK_VISIBLE;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
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.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;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
@@ -106,6 +110,7 @@ import android.app.AppOpsManager;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -147,6 +152,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
@@ -166,7 +172,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -375,15 +380,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* They are used by components that may hide and block interaction with underlying
* activities.
*/
- final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+ final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
- // TODO: Add listener for removal of references.
- /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- SparseArray<ActivityStack> mStacks = new SparseArray<>();
-
// TODO: There should be an ActivityDisplayController coordinating am/wm interaction.
/** Mapping from displayId to display current state */
private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
@@ -602,16 +603,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
final int displayId = displays[displayNdx].getDisplayId();
- ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
- if (activityDisplay.mDisplay == null) {
- throw new IllegalStateException("Default Display does not exist");
- }
+ ActivityDisplay activityDisplay = new ActivityDisplay(this, displayId);
mActivityDisplays.put(displayId, activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
- mHomeStack = mFocusedStack = mLastFocusedStack =
- getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
@@ -667,7 +665,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = getStack(RECENTS_STACK_ID);
+ final ActivityStack recentsStack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
}
@@ -708,24 +707,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
- INVALID_STACK_ID);
+ return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+ }
+
+ TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+ return anyTaskForIdLocked(id, matchMode, null);
}
/**
* Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
* @param id Id of the task we would like returned.
* @param matchMode The mode to match the given task id in.
- * @param stackId The stack to restore the task to (default launch stack will be used if
- * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}). Only
- * valid if the matchMode is
- * {@link #MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE}.
+ * @param aOptions The activity options to use for restoration. Can be null.
*/
- TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, int stackId) {
+ TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+ @Nullable ActivityOptions aOptions) {
// If there is a stack id set, ensure that we are attempting to actually restore a task
- if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE &&
- stackId != INVALID_STACK_ID) {
- throw new IllegalArgumentException("Should not specify stackId for non-restore lookup");
+ // TODO: Don't really know if this is needed...
+ if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+ throw new IllegalArgumentException("Should not specify activity options for non-restore"
+ + " lookup");
}
int numDisplays = mActivityDisplays.size();
@@ -763,7 +764,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
// Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
- if (!restoreRecentTaskLocked(task, stackId)) {
+ if (!restoreRecentTaskLocked(task, aOptions)) {
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
"Couldn't restore task id=" + id + " found in recents");
return null;
@@ -858,8 +859,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// 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.taskIdTakenForUserLocked(candidateTaskId, userId)
- || anyTaskForIdLocked(candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- INVALID_STACK_ID) != null) {
+ || anyTaskForIdLocked(
+ candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
// Something wrong!
@@ -1152,8 +1153,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) {
// Gather all of the running tasks for each stack into runningTaskLists.
- ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists =
- new ArrayList<ArrayList<RunningTaskInfo>>();
+ ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>();
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -1235,7 +1235,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
synchronized (mService) {
return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ | ActivityManagerService.STOCK_PM_FLAGS, userId, true);
}
}
@@ -1639,6 +1639,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return true;
}
+ // Check if caller is already present on display
+ final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid);
+
final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid();
if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID
&& displayOwnerUid != aInfo.applicationInfo.uid) {
@@ -1651,7 +1654,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
// Check if the caller is allowed to embed activities from other apps.
if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
- == PERMISSION_DENIED) {
+ == PERMISSION_DENIED && !uidPresentOnDisplay) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ " disallow activity embedding without permission.");
return false;
@@ -1672,8 +1675,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return true;
}
- // Check if caller is present on display
- if (activityDisplay.isUidPresent(callingUid)) {
+ if (uidPresentOnDisplay) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ " allow launch for caller present on the display");
return true;
@@ -1953,7 +1955,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
*/
void updateUserStackLocked(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId() : HOME_STACK_ID);
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId);
}
}
@@ -2083,38 +2085,35 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// we'll just indicate that this task returns to the home task.
task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
}
- ActivityStack currentStack = task.getStack();
+ final ActivityStack currentStack = task.getStack();
if (currentStack == null) {
Slog.e(TAG, "findTaskToMoveToFrontLocked: can't move task="
+ task + " to front. Stack is null");
return;
}
- if (task.isResizeable() && options != null) {
- int stackId = options.getLaunchStackId();
- if (canUseActivityOptionsLaunchBounds(options, stackId)) {
- final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
- task.updateOverrideConfiguration(bounds);
- if (stackId == INVALID_STACK_ID) {
- stackId = task.getLaunchStackId();
- }
- if (stackId != currentStack.mStackId) {
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
- DEFER_RESUME, "findTaskToMoveToFrontLocked");
- stackId = currentStack.mStackId;
- // moveTaskToStackUncheckedLocked() should already placed the task on top,
- // still need moveTaskToFrontLocked() below for any transition settings.
- }
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- resizeStackLocked(stackId, bounds,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
- } else {
- // WM resizeTask must be done after the task is moved to the correct stack,
- // because Task's setBounds() also updates dim layer's bounds, but that has
- // dependency on the stack.
- task.resizeWindowContainer();
- }
+ if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
+ final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
+ task.updateOverrideConfiguration(bounds);
+
+ ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+
+ if (stack != currentStack) {
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
+ "findTaskToMoveToFrontLocked");
+ stack = currentStack;
+ // moveTaskToStackUncheckedLocked() should already placed the task on top,
+ // still need moveTaskToFrontLocked() below for any transition settings.
+ }
+ if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
+ resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
+ } else {
+ // WM resizeTask must be done after the task is moved to the correct stack,
+ // because Task's setBounds() also updates dim layer's bounds, but that has
+ // dependency on the stack.
+ task.resizeWindowContainer();
}
}
@@ -2125,39 +2124,246 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + currentStack);
- handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
+ handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY,
currentStack.mStackId, forceNonResizeable);
}
- boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
+ boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
// We use the launch bounds in the activity options is the device supports freeform
// window management or is launching into the pinned stack.
- if (options.getLaunchBounds() == null) {
+ if (options == null || options.getLaunchBounds() == null) {
return false;
}
- return (mService.mSupportsPictureInPicture && launchStackId == PINNED_STACK_ID)
+ return (mService.mSupportsPictureInPicture
+ && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
|| mService.mSupportsFreeformWindowManagement;
}
protected <T extends ActivityStack> T getStack(int stackId) {
- return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ /** @see ActivityDisplay#getStack(int, int) */
+ private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.valueAt(i).getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
+ }
+ if (!supportsMultiWindow) {
+ return false;
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType);
+ }
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
+ }
+
+ private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task, int activityType) {
+
+ // First preference if the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ // If windowing mode is unset, then next preference is the candidate task, then the
+ // activity record.
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ if (task != null) {
+ windowingMode = task.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
+ windowingMode = r.getWindowingMode();
+ }
+ }
+
+ // Make sure the windowing mode we are trying to use makes sense for what is supported.
+ boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mService.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
+ }
+
+ if (isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType)) {
+ return windowingMode;
+ }
+ return WINDOWING_MODE_FULLSCREEN;
+ }
+
+ int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task) {
+ // Preference is given to the activity type for the activity then the task since the type
+ // once set shouldn't change.
+ int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+ activityType = task.getActivityType();
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ return activityType;
+ }
+ return options != null ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+ return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY);
}
- protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
- boolean createOnTop) {
- final ActivityStack stack = mStacks.get(stackId);
+ /**
+ * Returns the right stack to use for launching factoring in all the input parameters.
+ *
+ * @param r The activity we are trying to launch. Can be null.
+ * @param options The activity options used to the launch. Can be null.
+ * @param candidateTask The possible task the activity might be launched in. Can be null.
+ *
+ * @return The stack to use for the launch or INVALID_STACK_ID.
+ */
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+ int candidateDisplayId) {
+ int taskId = INVALID_TASK_ID;
+ int displayId = INVALID_DISPLAY;
+ //Rect bounds = null;
+
+ // We give preference to the launch preference in activity options.
+ if (options != null) {
+ taskId = options.getLaunchTaskId();
+ displayId = options.getLaunchDisplayId();
+ // TODO: Need to work this into the equation...
+ //bounds = options.getLaunchBounds();
+ }
+
+ // First preference for stack goes to the task Id set in the activity options. Use the stack
+ // associated with that if possible.
+ if (taskId != INVALID_TASK_ID) {
+ // Temporarily set the task id to invalid in case in re-entry.
+ options.setLaunchTaskId(INVALID_TASK_ID);
+ final TaskRecord task = anyTaskForIdLocked(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options);
+ options.setLaunchTaskId(taskId);
+ if (task != null) {
+ return task.getStack();
+ }
+ }
+
+ final int activityType = resolveActivityType(r, options, candidateTask);
+ int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ T stack = null;
+
+ // Next preference for stack goes to the display Id set in the activity options or the
+ // candidate display.
+ if (displayId == INVALID_DISPLAY) {
+ displayId = candidateDisplayId;
+ }
+ if (displayId != INVALID_DISPLAY) {
+ if (r != null) {
+ // TODO: This should also take in the windowing mode and activity type into account.
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
+ if (display != null) {
+ for (int i = display.mStacks.size() - 1; i >= 0; --i) {
+ stack = (T) display.mStacks.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
+ // TODO: We should create the stack we want on the display at this point.
+ }
+ }
+
+ // Give preference to the stack and display of the input task and activity if they match the
+ // mode we want to launch into.
+ stack = null;
+ ActivityDisplay display = null;
+ if (candidateTask != null) {
+ stack = candidateTask.getStack();
+ }
+ if (stack == null && r != null) {
+ stack = r.getStack();
+ }
if (stack != null) {
- return (T) stack;
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ display = stack.getDisplay();
}
- if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
- return null;
+
+ if (display == null
+ // TODO: Can be removed once we figure-out how non-standard types should launch
+ // outside the default display.
+ || (activityType != ACTIVITY_TYPE_STANDARD
+ && activityType != ACTIVITY_TYPE_UNDEFINED)) {
+ display = getDefaultDisplay();
}
- if (stackId == DOCKED_STACK_ID) {
- // Make sure recents stack exist when creating a dock stack as it normally need to be on
- // the other side of the docked stack and we make visibility decisions based on that.
- getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, createOnTop);
+
+ stack = display.getOrCreateStack(windowingMode, activityType, onTop);
+ if (stack != null) {
+ return stack;
+ }
+
+ // Whatever...return some default for now.
+ if (candidateTask != null && candidateTask.mBounds != null
+ && mService.mSupportsFreeformWindowManagement) {
+ windowingMode = WINDOWING_MODE_FREEFORM;
+ } else {
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
}
- return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+ return display.getOrCreateStack(windowingMode, activityType, onTop);
}
/**
@@ -2174,26 +2380,50 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
"Display with displayId=" + displayId + " not found.");
}
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ return null;
+ }
+
// Return the topmost valid stack on the display.
for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = activityDisplay.mStacks.get(i);
- if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, displayId, r)) {
+ if (isValidLaunchStack(stack, displayId, r)) {
return stack;
}
}
// If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != Display.DEFAULT_DISPLAY) {
- final int newDynamicStackId = getNextStackId();
- if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, displayId, r)) {
- return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
- }
+ if (displayId != DEFAULT_DISPLAY) {
+ return activityDisplay.createStack(
+ r.getWindowingMode(), r.getActivityType(), true /*onTop*/);
}
Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
return null;
}
+ // TODO: Can probably be consolidated into getLaunchStack()...
+ private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
+ switch (stack.getActivityType()) {
+ case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+ case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+ case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ }
+ switch (stack.getWindowingMode()) {
+ case WINDOWING_MODE_FULLSCREEN: return true;
+ case WINDOWING_MODE_FREEFORM: return r.supportsFreeform();
+ case WINDOWING_MODE_PINNED: return r.supportsPictureInPicture();
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return r.supportsSplitScreenWindowingMode();
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
+ }
+
+ 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) {
@@ -2225,7 +2455,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
for (int j = stacks.size() - 1; j >= 0; --j) {
final ActivityStack stack = stacks.get(j);
if (stack != currentFocus && stack.isFocusable()
- && stack.shouldBeVisible(null) != STACK_INVISIBLE) {
+ && stack.shouldBeVisible(null)) {
return stack;
}
}
@@ -2294,7 +2524,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
- final boolean splitScreenActive = getStack(DOCKED_STACK_ID) != null;
+ 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
@@ -2305,7 +2535,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
- if (stack.supportSplitScreenWindowingMode()) {
+ if (stack.supportsSplitScreenWindowingMode()) {
if (bounds == null && stack.inSplitScreenWindowingMode()) {
// null bounds = fullscreen windowing mode...at least for now.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2326,26 +2556,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- private void deferUpdateBounds(int stackId) {
- final ActivityStack stack = getStack(stackId);
+ private void deferUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
stack.deferUpdateBounds();
}
}
- private void continueUpdateBounds(int stackId) {
- final ActivityStack stack = getStack(stackId);
+ private void continueUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
stack.continueUpdateBounds();
}
}
void notifyAppTransitionDone() {
- continueUpdateBounds(RECENTS_STACK_ID);
+ continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
- final TaskRecord task =
- anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID);
+ final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
if (task != null) {
task.setTaskDockedResizing(false);
}
@@ -2353,29 +2582,31 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mResizingTasksDuringAnimation.clear();
}
- private void moveTasksToFullscreenStackInSurfaceTransaction(int fromStackId,
- boolean onTop) {
-
- final ActivityStack stack = getStack(fromStackId);
- if (stack == null) {
- return;
- }
+ /**
+ * 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. Specially when
+ * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
+ */
+ private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
+ int toDisplayId, boolean onTop) {
mWindowManager.deferSurfaceLayout();
try {
- if (fromStackId == DOCKED_STACK_ID) {
+ final int windowingMode = fromStack.getWindowingMode();
+ final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
+ final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// 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 = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- final ActivityStack otherStack = getStack(i);
- if (otherStack == null) {
- continue;
- }
+ 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(i, null, null, null, PRESERVE_WINDOWS,
+ resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
@@ -2384,48 +2615,51 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// resize when we remove task from it below and it is detached from the
// display because it no longer contains any tasks.
mAllowDockedStackResize = false;
- } else if (fromStackId == PINNED_STACK_ID) {
- if (onTop) {
- // Log if we are expanding the PiP to fullscreen
- MetricsLogger.action(mService.mContext,
- ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
- }
+ } else if (inPinnedWindowingMode && onTop) {
+ // Log if we are expanding the PiP to fullscreen
+ MetricsLogger.action(mService.mContext,
+ ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
}
- ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
- final boolean isFullscreenStackVisible = fullscreenStack != null &&
- fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE;
+
// If we are moving from the pinned stack, then the animation takes care of updating
// the picture-in-picture mode.
- final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID);
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- final int size = tasks.size();
- if (onTop) {
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
- final boolean isTopTask = i == (size - 1);
- if (fromStackId == PINNED_STACK_ID) {
- // Update the return-to to reflect where the pinned stack task was moved
- // from so that we retain the stack that was previously visible if the
- // pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(isFullscreenStackVisible ?
- ACTIVITY_TYPE_STANDARD : ACTIVITY_TYPE_HOME);
+ final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
+ final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
+
+ if (!tasks.isEmpty()) {
+ final int size = tasks.size();
+ final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
+
+ if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
+ for (int i = 0; i < size; i++) {
+ final TaskRecord task = tasks.get(i);
+ final boolean isTopTask = i == (size - 1);
+ if (inPinnedWindowingMode) {
+ // Update the return-to to reflect where the pinned stack task was moved
+ // from so that we retain the stack that was previously visible if the
+ // pinned stack is recreated. See moveActivityToPinnedStackLocked().
+ task.setTaskToReturnTo(returnToType);
+ }
+ // Defer resume until all the tasks have been moved to the fullscreen stack
+ task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ isTopTask /* animate */, DEFER_RESUME,
+ schedulePictureInPictureModeChange,
+ "moveTasksToFullscreenStack - onTop");
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ final TaskRecord task = tasks.get(i);
+ // Position the tasks in the fullscreen stack in order at the bottom of the
+ // stack. Also defer resume until all the tasks have been moved to the
+ // fullscreen stack.
+ task.reparent(fullscreenStack, i /* position */,
+ REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
+ schedulePictureInPictureModeChange,
+ "moveTasksToFullscreenStack - NOT_onTop");
}
- // Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
- schedulePictureInPictureModeChange,
- "moveTasksToFullscreenStack - onTop");
- }
- } else {
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
- // Position the tasks in the fullscreen stack in order at the bottom of the
- // stack. Also defer resume until all the tasks have been moved to the
- // fullscreen stack.
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, i /* position */,
- REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
- schedulePictureInPictureModeChange,
- "moveTasksToFullscreenStack - NOT_onTop");
}
}
@@ -2437,9 +2671,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
- mWindowManager.inSurfaceTransaction(
- () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStackId, onTop));
+ void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
+ moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
+ }
+
+ void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
+ mWindowManager.inSurfaceTransaction(() ->
+ moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
}
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
@@ -2450,7 +2688,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
false /* deferResume */);
}
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -2459,7 +2697,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return;
}
- final ActivityStack stack = getStack(DOCKED_STACK_ID);
+ 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;
@@ -2479,7 +2718,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
+ moveTasksToFullscreenStackLocked(stack, ON_TOP);
// stack shouldn't contain anymore activities, so nothing to resume.
r = null;
@@ -2488,13 +2727,14 @@ 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 ArrayList<ActivityStack> stacks = getStacksOnDefaultDisplay();
final Rect otherTaskRect = new Rect();
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i == DOCKED_STACK_ID) {
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = stacks.get(i);
+ if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
- final ActivityStack current = getStack(i);
- if (current == null || !current.supportSplitScreenWindowingMode()) {
+ if (!current.supportsSplitScreenWindowingMode()) {
continue;
}
// Need to set windowing mode here before we try to get the dock bounds.
@@ -2504,7 +2744,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
- resizeStackLocked(i, !tempRect.isEmpty() ? tempRect : null,
+ resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2521,7 +2761,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- final PinnedActivityStack stack = getStack(PINNED_STACK_ID);
+ // TODO(multi-display): Pinned stack display should be passed in.
+ final PinnedActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -2559,32 +2801,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- return null;
- }
- return createStack(stackId, activityDisplay, onTop);
-
- }
-
- ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
- switch (stackId) {
- case PINNED_STACK_ID:
- return new PinnedActivityStack(display, stackId, this, mRecentTasks, onTop);
- default:
- return new ActivityStack(display, stackId, this, mRecentTasks, onTop);
- }
- }
-
- void removeStackInSurfaceTransaction(int stackId) {
+ private void removeStackInSurfaceTransaction(int stackId) {
final ActivityStack stack = getStack(stackId);
if (stack == null) {
return;
}
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- if (stack.getStackId() == PINNED_STACK_ID) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
/**
* Workaround: Force-stop all the activities in the pinned stack before we reparent them
* to the fullscreen stack. This is to guarantee that when we are removing a stack,
@@ -2602,7 +2826,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
true /* processPausingActivites */, null /* configuration */);
// Move all the tasks to the bottom of the fullscreen stack
- moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+ moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
} else {
for (int i = tasks.size() - 1; i >= 0; i--) {
removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
@@ -2617,8 +2841,23 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* instead moved back onto the fullscreen stack.
*/
void removeStackLocked(int stackId) {
- mWindowManager.inSurfaceTransaction(
- () -> removeStackInSurfaceTransaction(stackId));
+ mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stackId));
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.valueAt(i).removeStacksInWindowingModes(windowingModes);
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.valueAt(i).removeStacksWithActivityTypes(activityTypes);
+ }
}
/**
@@ -2640,8 +2879,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
boolean pauseImmediately) {
- final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- INVALID_STACK_ID);
+ final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
tr.removeTaskActivitiesLocked(pauseImmediately);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -2654,10 +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);
- tr.removedFromRecents();
+ removeTaskFromRecents(tr);
}
ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
@@ -2740,27 +2991,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
/**
* Restores a recent task to a stack
* @param task The recent task to be restored.
- * @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}
- * or is not a static stack).
+ * @param aOptions The activity options to use for restoration.
* @return true if the task has been restored successfully.
*/
- boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
- if (!StackId.isStaticStack(stackId)) {
- // If stack is not static (or stack id is invalid) - use the default one.
- // This means that tasks that were on external displays will be restored on the
- // primary display.
- stackId = task.getLaunchStackId();
- } else if (stackId == DOCKED_STACK_ID && !task.supportsSplitScreen()) {
- // Preferred stack is the docked stack, but the task can't go in the docked stack.
- // Put it in the fullscreen stack.
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- }
-
+ boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions) {
+ final ActivityStack stack = getLaunchStack(null, aOptions, task, !ON_TOP);
final ActivityStack currentStack = task.getStack();
if (currentStack != null) {
// Task has already been restored once. See if we need to do anything more
- if (currentStack.mStackId == stackId) {
+ if (currentStack == stack) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
@@ -2769,19 +3008,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
}
- final ActivityStack stack =
- getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
-
- if (stack == null) {
- // What does this mean??? Not sure how we would get here...
- if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
- "Unable to find/create stack to restore recent task=" + task);
- return false;
- }
-
- stack.addTask(task, false /* toTop */, "restoreRecentTask");
+ stack.addTask(task, !ON_TOP, "restoreRecentTask");
// TODO: move call for creation here and other place into Stack.addTask()
- task.createWindowContainer(false /* toTop */, true /* showForAllUsers */);
+ task.createWindowContainer(!ON_TOP, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -2803,7 +3032,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
+ displayId);
}
- final ActivityStack stack = mStacks.get(stackId);
+ final ActivityStack stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
+ stackId);
@@ -2828,8 +3057,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
- ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) {
+ ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
final ActivityStack prevStack = task.getStack();
+ final int stackId = stack.mStackId;
+ final boolean inMultiWindowMode = stack.inMultiWindowMode();
// Check that we aren't reparenting to the same stack that the task is already in
if (prevStack != null && prevStack.mStackId == stackId) {
@@ -2840,22 +3071,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Ensure that we aren't trying to move into a multi-window stack without multi-window
// support
- if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
+ if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
throw new IllegalArgumentException("Device doesn't support multi-window, can not"
- + " reparent task=" + task + " to stackId=" + stackId);
+ + " reparent task=" + task + " to stack=" + stack);
}
// Ensure that we're not moving a task to a dynamic stack if device doesn't support
// multi-display.
- // TODO(multi-display): Support non-dynamic stacks on secondary displays.
- if (StackId.isDynamicStack(stackId) && !mService.mSupportsMultiDisplay) {
+ if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
throw new IllegalArgumentException("Device doesn't support multi-display, can not"
+ " reparent task=" + task + " to stackId=" + stackId);
}
// Ensure that we aren't trying to move into a freeform stack without freeform
// support
- if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+ " task=" + task);
}
@@ -2864,25 +3095,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// used for split-screen mode and will cause things like the docked divider to show up. We
// instead leave the task in its current stack or move it to the fullscreen stack if it
// isn't currently in a stack.
- if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
- stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
+ if (inMultiWindowMode && !task.isResizeable()) {
Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
+ " Moving to stackId=" + stackId + " instead.");
+ // Temporarily disable resizeablility of the task as we don't want it to be resized if,
+ // for example, a docked stack is created which will lead to the stack we are moving
+ // from being resized and and its resizeable tasks being resized.
+ try {
+ task.mTemporarilyUnresizable = true;
+ stack = stack.getDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
+ } finally {
+ task.mTemporarilyUnresizable = false;
+ }
}
-
- // Temporarily disable resizeablility of the task as we don't want it to be resized if, for
- // example, a docked stack is created which will lead to the stack we are moving from being
- // resized and and its resizeable tasks being resized.
- try {
- task.mTemporarilyUnresizable = true;
- return getStack(stackId, CREATE_IF_NEEDED, toTop);
- } finally {
- task.mTemporarilyUnresizable = false;
- }
+ return stack;
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
- final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ final ActivityStack stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException(
"moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
@@ -2912,12 +3143,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mWindowManager.deferSurfaceLayout();
+ final ActivityDisplay display = r.getStack().getDisplay();
+ PinnedActivityStack stack = display.getPinnedStack();
+
// This will clear the pinned stack by moving an existing task to the full screen stack,
// ensuring only one task is present.
- moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+ if (stack != null) {
+ moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+ }
// Need to make sure the pinned stack exist so we can resize it below...
- final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
try {
final TaskRecord task = r.getTask();
@@ -2941,8 +3177,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
moveHomeStackToFront(reason);
}
// Defer resume until below, and do not schedule PiP changes until we animate below
- task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
- DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+ false /* schedulePictureInPictureModeChange */, reason);
} else {
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
@@ -2957,7 +3193,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
// Defer resume until below, and do not schedule PiP changes until we animate below
- newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
}
@@ -2983,8 +3219,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName,
- r.getTask().taskId);
+ mService.mTaskChangeNotificationController.notifyActivityPinned(r);
}
/** Move activity with its stack to front and make the stack focused. */
@@ -3044,6 +3279,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// tasks should always have lower priority than any affinity-matching tasks
// in the fullscreen stacks
affinityMatch = mTmpFindTaskResult.r;
+ } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
+ Slog.d(TAG_TASKS, "Skipping match on different display "
+ + mTmpFindTaskResult.r.getDisplayId() + " " + displayId);
}
}
}
@@ -3408,14 +3646,17 @@ 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.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID);
+ 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.
removeStackLocked(PINNED_STACK_ID);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID);
+ final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
mCurrentUser = userId;
mStartingUsers.add(uss);
@@ -3448,7 +3689,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- return mService.mUserController.isCurrentProfileLocked(userId);
+ return mService.mUserController.isCurrentProfile(userId);
}
/**
@@ -3555,15 +3796,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
- pw.print(prefix); pw.println("mStacks=" + mStacks);
- // TODO: move this to LockTaskController
- final SparseArray<String[]> packages = mService.mLockTaskPackages;
- if (packages.size() > 0) {
- pw.print(prefix); pw.println("mLockTaskPackages (userId:packages)=");
- for (int i = 0; i < packages.size(); ++i) {
- pw.print(prefix); pw.print(prefix); pw.print(packages.keyAt(i));
- pw.print(":"); pw.println(Arrays.toString(packages.valueAt(i)));
- }
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ pw.println(prefix + "displayId=" + display.mDisplayId + " mStacks=" + display.mStacks);
}
if (!mWaitingForActivityVisible.isEmpty()) {
pw.print(prefix); pw.println("mWaitingForActivityVisible=");
@@ -3576,6 +3811,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mService.mLockTaskController.dump(pw, prefix);
}
+ 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);
+ activityDisplay.writeToProto(proto, DISPLAYS);
+ }
+ mKeyguardController.writeToProto(proto, KEYGUARD_CONTROLLER);
+ if (mFocusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
+ ActivityRecord focusedActivity = getResumedActivityLocked();
+ if (focusedActivity != null) {
+ focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ } else {
+ proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+ }
+ proto.end(token);
+ }
+
/**
* Dump all connected displays' configurations.
* @param prefix Prefix to apply to each line of the dump.
@@ -3605,8 +3860,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
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) == STACK_VISIBLE) {
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
activities.addAll(stack.getDumpActivitiesLocked(name));
}
}
@@ -3832,15 +4086,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return getActivityDisplayOrCreateLocked(displayId) != null;
}
+ // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
ActivityDisplay getActivityDisplay(int displayId) {
return mActivityDisplays.get(displayId);
}
+ // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+ ActivityDisplay getDefaultDisplay() {
+ return mActivityDisplays.get(DEFAULT_DISPLAY);
+ }
+
/**
* Get an existing instance of {@link ActivityDisplay} or create new if there is a
* corresponding record in display manager.
*/
- private ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
+ // TODO: Look into consolidating with getActivityDisplay()
+ ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
return activityDisplay;
@@ -3855,17 +4116,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return null;
}
// The display hasn't been added to ActivityManager yet, create a new record now.
- activityDisplay = new ActivityDisplay(displayId);
- if (activityDisplay.mDisplay == null) {
- Slog.w(TAG, "Display " + displayId + " gone before initialization complete");
- return null;
- }
- mActivityDisplays.put(displayId, activityDisplay);
+ activityDisplay = new ActivityDisplay(this, displayId);
+ attachDisplay(activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
mWindowManager.onDisplayAdded(displayId);
return activityDisplay;
}
+ @VisibleForTesting
+ void attachDisplay(ActivityDisplay display) {
+ mActivityDisplays.put(display.mDisplayId, display);
+ }
+
private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
mDefaultMinSizeOfResizeableTask =
mService.mContext.getResources().getDimensionPixelSize(
@@ -3893,7 +4155,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// 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.getStackId(), true /* onTop */);
+ moveTasksToFullscreenStackLocked(stack, true /* onTop */);
}
}
@@ -3955,7 +4217,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (display.mAllSleepTokens.isEmpty()) {
return;
}
- for (SleepTokenImpl token : display.mAllSleepTokens) {
+ for (SleepToken token : display.mAllSleepTokens) {
mSleepTokens.remove(token);
}
display.mAllSleepTokens.clear();
@@ -3963,7 +4225,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mService.updateSleepIfNeededLocked();
}
- private StackInfo getStackInfoLocked(ActivityStack stack) {
+ private StackInfo getStackInfo(ActivityStack stack) {
final int displayId = stack.mDisplayId;
final ActivityDisplay display = mActivityDisplays.get(displayId);
StackInfo info = new StackInfo();
@@ -3971,11 +4233,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
info.displayId = displayId;
info.stackId = stack.mStackId;
info.userId = stack.mCurrentUser;
- info.visible = stack.shouldBeVisible(null) == STACK_VISIBLE;
+ info.visible = stack.shouldBeVisible(null);
// A stack might be not attached to a display.
- info.position = display != null
- ? display.mStacks.indexOf(stack)
- : 0;
+ info.position = display != null ? display.mStacks.indexOf(stack) : 0;
+ info.configuration.setTo(stack.getConfiguration());
ArrayList<TaskRecord> tasks = stack.getAllTasks();
final int numTasks = tasks.size();
@@ -4004,40 +4265,44 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return info;
}
- StackInfo getStackInfoLocked(int stackId) {
+ StackInfo getStackInfo(int stackId) {
ActivityStack stack = getStack(stackId);
if (stack != null) {
- return getStackInfoLocked(stack);
+ return getStackInfo(stack);
}
return null;
}
+ StackInfo getStackInfo(int windowingMode, int activityType) {
+ final ActivityStack stack = getStack(windowingMode, activityType);
+ return (stack != null) ? getStackInfo(stack) : null;
+ }
+
ArrayList<StackInfo> getAllStackInfosLocked() {
ArrayList<StackInfo> list = new ArrayList<>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
- list.add(getStackInfoLocked(stacks.get(ndx)));
+ list.add(getStackInfo(stacks.get(ndx)));
}
}
return list;
}
- void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
+ void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
int preferredDisplayId, int actualStackId) {
- handleNonResizableTaskIfNeeded(task, preferredStackId, preferredDisplayId, actualStackId,
- false /* forceNonResizable */);
+ handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
+ actualStackId, false /* forceNonResizable */);
}
- void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
+ void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
int preferredDisplayId, int actualStackId, boolean forceNonResizable) {
final boolean isSecondaryDisplayPreferred =
- (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
- || StackId.isDynamicStack(preferredStackId);
+ (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final ActivityStack actualStack = getStack(actualStackId);
final boolean inSplitScreenMode = actualStack != null
&& actualStack.inSplitScreenWindowingMode();
- if (((!inSplitScreenMode && preferredStackId != DOCKED_STACK_ID)
+ if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
&& !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) {
return;
}
@@ -4065,7 +4330,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
final ActivityRecord topActivity = task.getTopActivity();
- if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) {
+ if (launchOnSecondaryDisplayFailed
+ || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
@@ -4079,7 +4345,12 @@ 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.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
+
+ final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenStack();
+ if (dockedStack != null) {
+ moveTasksToFullscreenStackLocked(dockedStack,
+ actualStackId == dockedStack.getStackId());
+ }
} else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
@@ -4284,122 +4555,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- // TODO: Move to its own file.
- /** Exactly one of these classes per Display in the system. Capable of holding zero or more
- * attached {@link ActivityStack}s */
- class ActivityDisplay extends ConfigurationContainer {
- /** Actual Display this object tracks. */
- int mDisplayId;
- Display mDisplay;
-
- /** 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 */
- final ArrayList<ActivityStack> mStacks = new ArrayList<>();
-
- /** Array of all UIDs that are present on the display. */
- private IntArray mDisplayAccessUIDs = new IntArray();
-
- /** All tokens used to put activities on this stack to sleep (including mOffToken) */
- final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>();
- /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
- SleepToken mOffToken;
-
- private boolean mSleeping;
-
- @VisibleForTesting
- ActivityDisplay() {
- mActivityDisplays.put(mDisplayId, this);
- }
-
- // After instantiation, check that mDisplay is not null before using this. The alternative
- // is for this to throw an exception if mDisplayManager.getDisplay() returns null.
- ActivityDisplay(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- return;
- }
- init(display);
- }
-
- void init(Display display) {
- mDisplay = display;
- mDisplayId = display.getDisplayId();
- }
-
- void attachStack(ActivityStack stack, int position) {
- if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
- + " to displayId=" + mDisplayId + " position=" + position);
- mStacks.add(position, stack);
- mService.updateSleepIfNeededLocked();
- }
-
- void detachStack(ActivityStack stack) {
- if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
- + " from displayId=" + mDisplayId);
- mStacks.remove(stack);
- mService.updateSleepIfNeededLocked();
- }
-
- @Override
- public String toString() {
- return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
- }
-
- @Override
- protected int getChildCount() {
- return mStacks.size();
- }
-
- @Override
- protected ConfigurationContainer getChildAt(int index) {
- return mStacks.get(index);
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return ActivityStackSupervisor.this;
- }
-
- boolean isPrivate() {
- return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
- }
-
- boolean isUidPresent(int uid) {
- for (ActivityStack stack : mStacks) {
- if (stack.isUidPresent(uid)) {
- return true;
- }
- }
- return false;
- }
-
- /** Update and get all UIDs that are present on the display and have access to it. */
- private IntArray getPresentUIDs() {
- mDisplayAccessUIDs.clear();
- for (ActivityStack stack : mStacks) {
- stack.getPresentUIDs(mDisplayAccessUIDs);
- }
- return mDisplayAccessUIDs;
- }
-
- boolean shouldDestroyContentOnRemove() {
- return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
- }
-
- boolean shouldSleep() {
- return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mService.mRunningVoice == null);
- }
-
- boolean isSleeping() {
- return mSleeping;
- }
-
- void setIsSleeping(boolean asleep) {
- mSleeping = asleep;
- }
- }
-
ActivityStack findStackBehind(ActivityStack stack) {
// TODO(multi-display): We are only looking for stacks on the default display.
final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
@@ -4432,32 +4587,36 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final String callingPackage;
final Intent intent;
final int userId;
+ int activityType = ACTIVITY_TYPE_UNDEFINED;
+ int windowingMode = WINDOWING_MODE_UNDEFINED;
final ActivityOptions activityOptions = (bOptions != null)
? new ActivityOptions(bOptions) : null;
- final int launchStackId = (activityOptions != null)
- ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
- if (StackId.isHomeOrRecentsStack(launchStackId)) {
+ if (activityOptions != null) {
+ activityType = activityOptions.getLaunchActivityType();
+ windowingMode = activityOptions.getLaunchWindowingMode();
+ }
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ taskId + " can't be launch in the home/recents stack.");
}
mWindowManager.deferSurfaceLayout();
try {
- if (launchStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mWindowManager.setDockedStackCreateState(
activityOptions.getDockCreateMode(), null /* initialBounds */);
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
- deferUpdateBounds(RECENTS_STACK_ID);
+ deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
- launchStackId);
+ activityOptions);
if (task == null) {
- continueUpdateBounds(RECENTS_STACK_ID);
+ continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecentsInner: Task " + taskId + " not found.");
@@ -4465,15 +4624,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Since we don't have an actual source record here, we assume that the currently
// focused activity was the source.
- final ActivityStack focusedStack = getFocusedStack();
- final ActivityRecord sourceRecord =
- focusedStack != null ? focusedStack.topActivity() : null;
-
- if (launchStackId != INVALID_STACK_ID) {
- if (task.getStackId() != launchStackId) {
- task.reparent(launchStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
- DEFER_RESUME, "startActivityFromRecents");
- }
+ final ActivityStack stack = getLaunchStack(null, activityOptions, task, ON_TOP);
+
+ if (stack != null && task.getStack() != stack) {
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+ "startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4492,15 +4647,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// If we are launching the task in the docked stack, put it into resizing mode so
// the window renders full-screen with the background filling the void. Also only
// call this at the end to make sure that tasks exists on the window manager side.
- if (launchStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
setResizingDuringAnimation(task);
}
mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(),
- ActivityManager.START_TASK_TO_FRONT,
- sourceRecord != null
- ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID,
- sourceRecord, task.getStack());
+ ActivityManager.START_TASK_TO_FRONT, task.getStack());
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
@@ -4510,7 +4662,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
userId = task.userId;
int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
null, null, 0, 0, bOptions, userId, task, "startActivityFromRecents");
- if (launchStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
setResizingDuringAnimation(task);
}
return result;
@@ -4532,8 +4684,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
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 */)
- == ActivityStack.STACK_VISIBLE) {
+ if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.topActivity();
if (top != null) {
if (stack == mFocusedStack) {
diff --git a/com/android/server/am/ActivityStarter.java b/com/android/server/am/ActivityStarter.java
index 16abcfb6..d444c663 100644
--- a/com/android/server/am/ActivityStarter.java
+++ b/com/android/server/am/ActivityStarter.java
@@ -27,18 +27,19 @@ 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.ASSISTANT_STACK_ID;
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.HOME_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.RECENTS_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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -76,8 +77,6 @@ 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.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -167,7 +166,8 @@ class ActivityStarter {
private boolean mDoResume;
private int mStartFlags;
private ActivityRecord mSourceRecord;
- private int mSourceDisplayId;
+ // The display to launch the activity onto, barring any strong reason to do otherwise.
+ private int mPreferredDisplayId;
private TaskRecord mInTask;
private boolean mAddingToTask;
@@ -186,6 +186,12 @@ class ActivityStarter {
private boolean mAvoidMoveToFront;
private boolean mPowerHintSent;
+ // We must track when we deliver the new intent since multiple code paths invoke
+ // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
+ // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is
+ // delivered at most once.
+ private boolean mIntentDelivered;
+
private IVoiceInteractionSession mVoiceSession;
private IVoiceInteractor mVoiceInteractor;
@@ -222,7 +228,7 @@ class ActivityStarter {
mDoResume = false;
mStartFlags = 0;
mSourceRecord = null;
- mSourceDisplayId = INVALID_DISPLAY;
+ mPreferredDisplayId = INVALID_DISPLAY;
mInTask = null;
mAddingToTask = false;
@@ -243,6 +249,8 @@ class ActivityStarter {
mVoiceInteractor = null;
mUsingVr2dDisplay = false;
+
+ mIntentDelivered = false;
}
ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
@@ -589,9 +597,7 @@ class ActivityStarter {
auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
}
- void postStartActivityProcessing(
- ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
- ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
@@ -613,7 +619,8 @@ class ActivityStarter {
}
if (startedActivityStackId == DOCKED_STACK_ID) {
- final ActivityStack homeStack = mSupervisor.getStack(HOME_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
@@ -1001,8 +1008,7 @@ class ActivityStarter {
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord,
- mTargetStack);
+ postStartActivityProcessing(r, result, mTargetStack);
return result;
}
@@ -1024,10 +1030,12 @@ class ActivityStarter {
ActivityRecord reusedActivity = getReusableIntentActivity();
- final int preferredLaunchStackId =
- (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
- final int preferredLaunchDisplayId =
- (mOptions != null) ? mOptions.getLaunchDisplayId() : DEFAULT_DISPLAY;
+ int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
+ int preferredLaunchDisplayId = DEFAULT_DISPLAY;
+ if (mOptions != null) {
+ preferredWindowingMode = mOptions.getLaunchWindowingMode();
+ preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
+ }
if (reusedActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
@@ -1077,9 +1085,7 @@ class ActivityStarter {
// so make sure the task now has the identity of the new intent.
top.getTask().setIntent(mStartActivity);
}
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
}
}
@@ -1141,7 +1147,6 @@ class ActivityStarter {
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
@@ -1153,12 +1158,12 @@ class ActivityStarter {
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
- top.deliverNewIntentLocked(
- mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+
+ deliverNewIntent(top);
// 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(), preferredLaunchStackId,
+ mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
preferredLaunchDisplayId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
@@ -1173,8 +1178,7 @@ class ActivityStarter {
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
- result = setTaskFromReuseOrCreateNewTask(
- taskToAffiliate, preferredLaunchStackId, topStack);
+ result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
} else if (mSourceRecord != null) {
result = setTaskFromSourceRecord();
} else if (mInTask != null) {
@@ -1237,11 +1241,11 @@ class ActivityStarter {
mOptions);
}
} else {
- mTargetStack.addRecentActivityLocked(mStartActivity);
+ mSupervisor.addRecentActivity(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
- mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,
+ mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
preferredLaunchDisplayId, mTargetStack.mStackId);
return START_SUCCESS;
@@ -1260,7 +1264,7 @@ class ActivityStarter {
mVoiceSession = voiceSession;
mVoiceInteractor = voiceInteractor;
- mSourceDisplayId = getSourceDisplayId(mSourceRecord, mStartActivity);
+ mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);
mLaunchBounds = getOverrideBounds(r, options, inTask);
@@ -1515,7 +1519,7 @@ class ActivityStarter {
!mLaunchSingleTask);
} else {
// Otherwise find the best task to put the activity in.
- intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
+ intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
return intentActivity;
@@ -1523,10 +1527,12 @@ class ActivityStarter {
/**
* Returns the ID of the display to use for a new activity. If the device is in VR mode,
- * then return the Vr mode's virtual display ID. If not, if the source activity has
- * a explicit display ID set, use that to launch the activity.
+ * then return the Vr mode's virtual display ID. If not, if the activity was started with
+ * a launchDisplayId, use that. Otherwise, if the source activity has a explicit display ID
+ * set, use that to launch the activity.
*/
- private int getSourceDisplayId(ActivityRecord sourceRecord, ActivityRecord startingActivity) {
+ private int getPreferedDisplayId(
+ ActivityRecord sourceRecord, ActivityRecord startingActivity, ActivityOptions options) {
// Check if the Activity is a VR activity. If so, the activity should be launched in
// main display.
if (startingActivity != null && startingActivity.requestedVrComponent != null) {
@@ -1543,6 +1549,13 @@ class ActivityStarter {
return displayId;
}
+ // If the caller requested a display, prefer that display.
+ final int launchDisplayId =
+ (options != null) ? options.getLaunchDisplayId() : INVALID_DISPLAY;
+ if (launchDisplayId != INVALID_DISPLAY) {
+ return launchDisplayId;
+ }
+
displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
// If the activity has a displayId set explicitly, launch it on the same displayId.
if (displayId != INVALID_DISPLAY) {
@@ -1601,12 +1614,11 @@ class ActivityStarter {
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true;
- } else if (launchStack.mStackId == DOCKED_STACK_ID
- || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (launchStack.inSplitScreenWindowingMode()) {
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
- intentTask.reparent(launchStack.mStackId, ON_TOP,
+ intentTask.reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"launchToSide");
} else {
@@ -1623,17 +1635,17 @@ class ActivityStarter {
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
- } else if (launchStack.getStackId() == StackId.HOME_STACK_ID
- && mTargetStack.getStackId() != StackId.HOME_STACK_ID) {
+ } else if (launchStack.isActivityTypeHome()
+ && !mTargetStack.isActivityTypeHome()) {
// It is possible for the home activity to be in another stack initially.
// For example, the activity may have been initially started with an intent
// which placed it in the fullscreen stack. To ensure the proper handling of
// the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
@@ -1654,8 +1666,8 @@ class ActivityStarter {
mTargetStack.moveToFront("intentActivityFound");
}
- mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID,
- DEFAULT_DISPLAY, mTargetStack.mStackId);
+ mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
+ 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) {
@@ -1675,8 +1687,7 @@ class ActivityStarter {
// Task will be launched over the home stack, so return home.
task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
return;
- } else if (focusedStack != null && focusedStack != task.getStack() &&
- focusedStack.isActivityTypeAssistant()) {
+ } else if (focusedStack != task.getStack() && focusedStack.isActivityTypeAssistant()) {
// Task was launched over the assistant stack, so return there
task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
return;
@@ -1739,13 +1750,10 @@ class ActivityStarter {
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
- intentActivity.getTask());
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
}
- intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(intentActivity);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
// In this case we are launching the root activity of the task, but with a
// different intent. We should start a new instance on top.
@@ -1779,7 +1787,7 @@ class ActivityStarter {
}
private int setTaskFromReuseOrCreateNewTask(
- TaskRecord taskToAffiliate, int preferredLaunchStackId, ActivityStack topStack) {
+ TaskRecord taskToAffiliate, ActivityStack topStack) {
mTargetStack = computeStackFocus(
mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
@@ -1793,15 +1801,8 @@ class ActivityStarter {
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- if (mLaunchBounds != null) {
- final int stackId = mTargetStack.mStackId;
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(
- stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- } else {
- mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds);
- }
- }
+ updateBounds(mStartActivity.getTask(), mLaunchBounds);
+
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTask());
} else {
@@ -1821,8 +1822,10 @@ class ActivityStarter {
// If stack id is specified in activity options, usually it means that activity is
// launched not from currently focused stack (e.g. from SysUI or from shell) - in
// that case we check the target stack.
+ // TODO: Not sure I understand the value or use of the commented out code and the
+ // comment above. See if this causes any issues and why...
updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags,
- preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack);
+ /*preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : */topStack);
}
if (mDoResume) {
mTargetStack.moveToFront("reuseOrNewTask");
@@ -1830,6 +1833,17 @@ class ActivityStarter {
return START_SUCCESS;
}
+ private void deliverNewIntent(ActivityRecord activity) {
+ if (mIntentDelivered) {
+ return;
+ }
+
+ ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+ activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+ mStartActivity.launchedFromPackage);
+ mIntentDelivered = true;
+ }
+
private int setTaskFromSourceRecord() {
if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
@@ -1867,8 +1881,8 @@ class ActivityStarter {
if (mTargetStack == null) {
mTargetStack = sourceStack;
} else if (mTargetStack != sourceStack) {
- sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
- !ANIMATE, DEFER_RESUME, "launchToSide");
+ sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, "launchToSide");
}
final TaskRecord topTask = mTargetStack.topTask();
@@ -1886,7 +1900,7 @@ class ActivityStarter {
mKeepCurTransition = true;
if (top != null) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
// For paranoia, make sure we have correctly resumed the top activity.
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
@@ -1905,7 +1919,7 @@ class ActivityStarter {
task.moveActivityToFrontLocked(top);
top.updateOptionsLocked(mOptions);
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
@@ -1941,14 +1955,12 @@ class ActivityStarter {
|| mLaunchSingleTop || mLaunchSingleTask) {
mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "inTaskToFront");
- ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
return START_DELIVERED_TO_TOP;
}
}
@@ -1963,17 +1975,15 @@ class ActivityStarter {
}
if (mLaunchBounds != null) {
- mInTask.updateOverrideConfiguration(mLaunchBounds);
- int stackId = mInTask.getLaunchStackId();
- if (stackId != mInTask.getStackId()) {
- mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+ // TODO: Shouldn't we already know what stack to use by the time we get here?
+ ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+ if (stack != mInTask.getStack()) {
+ mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
- stackId = mInTask.getStackId();
mTargetStack = mInTask.getStack();
}
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- }
+
+ updateBounds(mInTask, mLaunchBounds);
}
mTargetStack.moveTaskToFrontLocked(
@@ -1986,6 +1996,19 @@ class ActivityStarter {
return START_SUCCESS;
}
+ void updateBounds(TaskRecord task, Rect bounds) {
+ if (bounds == null) {
+ return;
+ }
+
+ final int stackId = task.getStackId();
+ if (StackId.resizeStackWithLaunchBounds(stackId)) {
+ mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+ } else {
+ task.updateOverrideConfiguration(bounds);
+ }
+ }
+
private void setTaskToCurrentTopOrCreateNewTask() {
mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
mOptions);
@@ -2079,20 +2102,21 @@ class ActivityStarter {
return mSupervisor.mFocusedStack;
}
- if (mSourceDisplayId != DEFAULT_DISPLAY) {
+ if (mPreferredDisplayId != DEFAULT_DISPLAY) {
// Try to put the activity in a stack on a secondary display.
- stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
+ stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r);
if (stack == null) {
// If source display is not suitable - look for topmost valid stack in the system.
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
- + ", looking on all displays.");
- stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
+ "computeStackFocus: Can't launch on mPreferredDisplayId="
+ + mPreferredDisplayId + ", looking on all displays.");
+ stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
}
}
if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
- final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
+ final ArrayList<ActivityStack> homeDisplayStacks =
+ mSupervisor.getStacksOnDefaultDisplay();
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
if (isDynamicStack(stack.mStackId)) {
@@ -2102,10 +2126,7 @@ class ActivityStarter {
}
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
- final int stackId = task != null ? task.getLaunchStackId() :
- bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
- FULLSCREEN_WORKSPACE_STACK_ID;
- stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+ stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2113,37 +2134,40 @@ class ActivityStarter {
}
/** Check if provided activity record can launch in currently focused stack. */
+ // TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
final ActivityStack focusedStack = mSupervisor.mFocusedStack;
- final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
final boolean canUseFocusedStack;
- switch (focusedStackId) {
- case FULLSCREEN_WORKSPACE_STACK_ID:
- // The fullscreen stack can contain any task regardless of if the task is resizeable
- // or not. So, we let the task go in the fullscreen task if it is the focus stack.
- canUseFocusedStack = true;
- break;
- case ASSISTANT_STACK_ID:
- canUseFocusedStack = r.isActivityTypeAssistant();
- break;
- case DOCKED_STACK_ID:
- // Any activity which supports split screen can go in the docked stack.
- canUseFocusedStack = r.supportsSplitScreen();
- break;
- case FREEFORM_WORKSPACE_STACK_ID:
- // Any activity which supports freeform can go in the freeform stack.
- canUseFocusedStack = r.supportsFreeform();
- break;
- default:
- // Dynamic stacks behave similarly to the fullscreen stack and can contain any
- // resizeable task.
- canUseFocusedStack = isDynamicStack(focusedStackId)
- && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
+ final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
+ if (focusedStack.isActivityTypeAssistant()) {
+ canUseFocusedStack = r.isActivityTypeAssistant();
+ } else {
+ switch (focusedStack.getWindowingMode()) {
+ case WINDOWING_MODE_FULLSCREEN:
+ // The fullscreen stack can contain any task regardless of if the task is
+ // resizeable or not. So, we let the task go in the fullscreen task if it is the
+ // focus stack.
+ canUseFocusedStack = true;
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+ // Any activity which supports split screen can go in the docked stack.
+ canUseFocusedStack = r.supportsSplitScreenWindowingMode();
+ break;
+ case WINDOWING_MODE_FREEFORM:
+ // Any activity which supports freeform can go in the freeform stack.
+ canUseFocusedStack = r.supportsFreeform();
+ break;
+ default:
+ // Dynamic stacks behave similarly to the fullscreen stack and can contain any
+ // resizeable task.
+ canUseFocusedStack = isDynamicStack(focusedStackId)
+ && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
+ }
}
-
return canUseFocusedStack && !newTask
- // We strongly prefer to launch activities on the same display as their source.
- && (mSourceDisplayId == focusedStack.mDisplayId);
+ // Using the focus stack isn't important enough to override the preferred display.
+ && (mPreferredDisplayId == focusedStack.mDisplayId);
}
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
@@ -2153,54 +2177,16 @@ class ActivityStarter {
return mReuseTask.getStack();
}
- // If the activity is of a specific type, return the associated stack, creating it if
- // necessary
- if (r.isActivityTypeHome()) {
- return mSupervisor.mHomeStack;
- }
- if (r.isActivityTypeRecents()) {
- return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- if (r.isActivityTypeAssistant()) {
- return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
-
- final int launchDisplayId =
- (aOptions != null) ? aOptions.getLaunchDisplayId() : INVALID_DISPLAY;
-
- final int launchStackId =
- (aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID;
-
- if (launchStackId != INVALID_STACK_ID && launchDisplayId != INVALID_DISPLAY) {
- throw new IllegalArgumentException(
- "Stack and display id can't be set at the same time.");
- }
+ final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY;
+ final ActivityStack launchStack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
+ vrDisplayId);
- if (isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
- return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
- }
- if (launchStackId == DOCKED_STACK_ID) {
- // The preferred launch stack is the docked stack, but it isn't a valid launch stack
- // for this activity, so we put the activity in the fullscreen stack.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- if (launchDisplayId != INVALID_DISPLAY) {
- // Stack id has higher priority than display id.
- return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
- }
-
- // If we are using Vr2d display, find the virtual display stack.
- if (mUsingVr2dDisplay) {
- ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
- if (DEBUG_STACK) {
- Slog.v(TAG, "Launch stack for app: " + r.toString() +
- ", on virtual display stack:" + as.toString());
- }
- return as;
+ if (launchStack != null) {
+ return launchStack;
}
if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
- || mSourceDisplayId != DEFAULT_DISPLAY) {
+ || mPreferredDisplayId != DEFAULT_DISPLAY) {
return null;
}
// Otherwise handle adjacent launch.
@@ -2222,15 +2208,16 @@ class ActivityStarter {
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.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
- ON_TOP);
+ final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+ return parentStack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
} else {
// 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.getStack(DOCKED_STACK_ID);
- if (dockedStack != null
- && dockedStack.shouldBeVisible(r) == STACK_INVISIBLE) {
+ 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;
} else {
@@ -2240,39 +2227,11 @@ class ActivityStarter {
}
}
- boolean isValidLaunchStackId(int stackId, int displayId, ActivityRecord r) {
- switch (stackId) {
- case INVALID_STACK_ID:
- case HOME_STACK_ID:
- return false;
- case FULLSCREEN_WORKSPACE_STACK_ID:
- return true;
- case FREEFORM_WORKSPACE_STACK_ID:
- return r.supportsFreeform();
- case DOCKED_STACK_ID:
- return r.supportsSplitScreen();
- case PINNED_STACK_ID:
- return r.supportsPictureInPicture();
- case RECENTS_STACK_ID:
- return r.isActivityTypeRecents();
- case ASSISTANT_STACK_ID:
- return r.isActivityTypeAssistant();
- default:
- if (StackId.isDynamicStack(stackId)) {
- return r.canBeLaunchedOnDisplay(displayId);
- }
- Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
- return false;
- }
- }
-
- Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
+ private Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
Rect newBounds = null;
- if (options != null && (r.isResizeable() || (inTask != null && inTask.isResizeable()))) {
- if (mSupervisor.canUseActivityOptionsLaunchBounds(
- options, options.getLaunchStackId())) {
- newBounds = TaskRecord.validateBounds(options.getLaunchBounds());
- }
+ if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
+ && (r.isResizeable() || (inTask != null && inTask.isResizeable()))) {
+ newBounds = TaskRecord.validateBounds(options.getLaunchBounds());
}
return newBounds;
}
@@ -2281,15 +2240,6 @@ class ActivityStarter {
mWindowManager = wm;
}
- void removePendingActivityLaunchesLocked(ActivityStack stack) {
- for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
- PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
- if (pal.stack == stack) {
- mPendingActivityLaunches.remove(palNdx);
- }
- }
- }
-
static boolean isDocumentLaunchesIntoExisting(int flags) {
return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
diff --git a/com/android/server/am/AppErrors.java b/com/android/server/am/AppErrors.java
index 440b3d3b..fe380970 100644
--- a/com/android/server/am/AppErrors.java
+++ b/com/android/server/am/AppErrors.java
@@ -507,7 +507,7 @@ class AppErrors {
// launching the report UI under a different user.
app.errorReportReceiver = null;
- for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (app.userId == userId) {
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
@@ -728,7 +728,7 @@ class AppErrors {
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
- for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
isBackground &= (proc.userId != userId);
}
if (isBackground && !showBackground) {
diff --git a/com/android/server/am/BatteryStatsService.java b/com/android/server/am/BatteryStatsService.java
index 3105e37f..7c9cd00e 100644
--- a/com/android/server/am/BatteryStatsService.java
+++ b/com/android/server/am/BatteryStatsService.java
@@ -220,7 +220,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// Shutdown the thread we made.
mWorker.shutdown();
}
-
+
public static IBatteryStats getService() {
if (sService != null) {
return sService;
@@ -300,10 +300,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// TODO: remove this once we figure out properly where and how
// PROCESS_EVENT = 1112
- // EVENT SUBTYPE: START = 1
- // KEY_NAME: 1
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
// KEY_UID: 2
- StatsLog.writeArray(1112, 1, 1, name, 2, uid);
+ StatsLog.writeArray(1112, 1, 1, 1002, name, 2, uid);
}
}
@@ -313,10 +313,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// TODO: remove this once we figure out properly where and how
// PROCESS_EVENT = 1112
- // EVENT SUBTYPE: CRASH = 2
- // KEY_NAME: 1
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
// KEY_UID: 2
- StatsLog.writeArray(1112, 2, 1, name, 2, uid);
+ StatsLog.writeArray(1112, 1, 2, 1002, name, 2, uid);
}
}
@@ -550,10 +550,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
synchronized (mStats) {
mStats.noteScreenStateLocked(state);
// TODO: remove this once we figure out properly where and how
- // SCREEN_EVENT = 1003
- // State key: 1
+ // SCREEN_EVENT = 2
+ // KEY_STATE: 1
// State value: state. We can change this to our own def later.
- StatsLog.writeArray(1003, 1, state);
+ StatsLog.writeArray(2, 1, state);
}
if (DBG) Slog.d(TAG, "end noteScreenState");
}
@@ -564,14 +564,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.noteScreenBrightnessLocked(brightness);
}
}
-
+
public void noteUserActivity(int uid, int event) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteUserActivityLocked(uid, event);
}
}
-
+
public void noteWakeUp(String reason, int reasonUid) {
enforceCallingPermission();
synchronized (mStats) {
@@ -611,21 +611,21 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.notePhoneOnLocked();
}
}
-
+
public void notePhoneOff() {
enforceCallingPermission();
synchronized (mStats) {
mStats.notePhoneOffLocked();
}
}
-
+
public void notePhoneSignalStrength(SignalStrength signalStrength) {
enforceCallingPermission();
synchronized (mStats) {
mStats.notePhoneSignalStrengthLocked(signalStrength);
}
}
-
+
public void notePhoneDataConnectionState(int dataType, boolean hasData) {
enforceCallingPermission();
synchronized (mStats) {
@@ -647,7 +647,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.noteWifiOnLocked();
}
}
-
+
public void noteWifiOff() {
enforceCallingPermission();
synchronized (mStats) {
@@ -806,7 +806,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.noteFullWifiLockAcquiredLocked(uid);
}
}
-
+
public void noteFullWifiLockReleased(int uid) {
enforceCallingPermission();
synchronized (mStats) {
@@ -1043,7 +1043,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
});
});
}
-
+
public long getAwakeTimeBattery() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BATTERY_STATS, null);
@@ -1186,6 +1186,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
int flags = 0;
boolean useCheckinFormat = false;
+ boolean toProto = false;
boolean isRealCheckin = false;
boolean noOutput = false;
boolean writeData = false;
@@ -1212,6 +1213,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else if ("-c".equals(arg)) {
useCheckinFormat = true;
flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
+ } else if ("--proto".equals(arg)) {
+ toProto = true;
} else if ("--charged".equals(arg)) {
flags |= BatteryStats.DUMP_CHARGED_ONLY;
} else if ("--daily".equals(arg)) {
@@ -1304,7 +1307,45 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
- if (useCheckinFormat) {
+ if (toProto) {
+ List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+ PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
+ if (isRealCheckin) {
+ // For a real checkin, first we want to prefer to use the last complete checkin
+ // file if there is one.
+ synchronized (mStats.mCheckinFile) {
+ if (mStats.mCheckinFile.exists()) {
+ try {
+ byte[] raw = mStats.mCheckinFile.readFully();
+ if (raw != null) {
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+ null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+ checkinStats.readSummaryFromParcel(in);
+ in.recycle();
+ checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
+ historyStart);
+ mStats.mCheckinFile.delete();
+ return;
+ }
+ } catch (IOException | ParcelFormatException e) {
+ Slog.w(TAG, "Failure reading checkin file "
+ + mStats.mCheckinFile.getBaseFile(), e);
+ }
+ }
+ }
+ }
+ if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
+ synchronized (mStats) {
+ mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+ if (writeData) {
+ mStats.writeAsyncLocked();
+ }
+ }
+ if (DBG) Slog.d(TAG, "end dumpProtoLocked");
+ } else if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
if (isRealCheckin) {
diff --git a/com/android/server/am/KeyguardController.java b/com/android/server/am/KeyguardController.java
index cea80c8d..5c48f90d 100644
--- a/com/android/server/am/KeyguardController.java
+++ b/com/android/server/am/KeyguardController.java
@@ -19,12 +19,15 @@ 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;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
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.PRESERVE_WINDOWS;
+import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED;
+import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
@@ -38,6 +41,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.wm.WindowManagerService;
@@ -66,6 +70,7 @@ class KeyguardController {
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
private SleepToken mSleepToken;
+ private int mSecondaryDisplayShowing = INVALID_DISPLAY;
KeyguardController(ActivityManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -78,10 +83,12 @@ class KeyguardController {
}
/**
- * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise
+ * @return true if Keyguard is showing, not going away, and not being occluded on the given
+ * display, false otherwise
*/
- boolean isKeyguardShowing() {
- return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded;
+ boolean isKeyguardShowing(int displayId) {
+ return mKeyguardShowing && !mKeyguardGoingAway &&
+ (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
}
/**
@@ -94,15 +101,19 @@ class KeyguardController {
/**
* Update the Keyguard showing state.
*/
- void setKeyguardShown(boolean showing) {
- if (showing == mKeyguardShowing) {
+ void setKeyguardShown(boolean showing, int secondaryDisplayShowing) {
+ boolean showingChanged = showing != mKeyguardShowing;
+ if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
return;
}
mKeyguardShowing = showing;
- dismissDockedStackIfNeeded();
- if (showing) {
- setKeyguardGoingAway(false);
- mDismissalRequested = false;
+ mSecondaryDisplayShowing = secondaryDisplayShowing;
+ if (showingChanged) {
+ dismissDockedStackIfNeeded();
+ if (showing) {
+ setKeyguardGoingAway(false);
+ mDismissalRequested = false;
+ }
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
updateKeyguardSleepToken();
@@ -331,15 +342,19 @@ 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.
- mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
- mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
+ if (stack == null) {
+ return;
+ }
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
+ mStackSupervisor.mFocusedStack == stack);
}
}
private void updateKeyguardSleepToken() {
- if (mSleepToken == null && isKeyguardShowing()) {
+ if (mSleepToken == null && isKeyguardShowing(DEFAULT_DISPLAY)) {
mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
- } else if (mSleepToken != null && !isKeyguardShowing()) {
+ } else if (mSleepToken != null && !isKeyguardShowing(DEFAULT_DISPLAY)) {
mSleepToken.release();
mSleepToken = null;
}
@@ -354,4 +369,11 @@ class KeyguardController {
pw.println(prefix + " mDismissalRequested=" + mDismissalRequested);
pw.println(prefix + " mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
+ proto.write(KEYGUARD_OCCLUDED, mOccluded);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/am/LockTaskController.java b/com/android/server/am/LockTaskController.java
index d8706bcc..72b5de88 100644
--- a/com/android/server/am/LockTaskController.java
+++ b/com/android/server/am/LockTaskController.java
@@ -25,12 +25,14 @@ import static android.app.StatusBarManager.DISABLE_HOME;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.app.StatusBarManager.DISABLE_RECENT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
import static android.provider.Settings.Secure.LOCK_TO_APP_EXIT_LOCKED;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -55,7 +57,9 @@ import android.os.RemoteException;
import android.os.ServiceManager;
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;
@@ -65,6 +69,7 @@ import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Helper class that deals with all things related to task locking. This includes the screen pinning
@@ -123,6 +128,11 @@ public class LockTaskController {
private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
/**
+ * Packages that are allowed to be launched into the lock task mode for each user.
+ */
+ private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+
+ /**
* Store the current lock task mode. Possible values:
* {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}
@@ -159,8 +169,8 @@ public class LockTaskController {
}
/**
- * @return whether the given task can be moved to the back of the stack. Locked tasks cannot be
- * moved to the back of the stack.
+ * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the
+ * back of the stack.
*/
boolean checkLockedTask(TaskRecord task) {
if (mLockTaskModeTasks.contains(task)) {
@@ -422,8 +432,8 @@ public class LockTaskController {
mSupervisor.resumeFocusedStackTopActivityLocked();
mWindowManager.executeAppTransition();
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
- mSupervisor.handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
- task.getStackId(), true /* forceNonResizable */);
+ mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
+ DEFAULT_DISPLAY, task.getStackId(), true /* forceNonResizable */);
}
}
@@ -452,29 +462,35 @@ public class LockTaskController {
}
/**
- * Called when the list of packages whitelisted for lock task mode is changed. Any currently
- * locked tasks that got removed from the whitelist will be finished.
+ * Update packages that are allowed to be launched in lock task mode.
+ * @param userId Which user this whitelist is associated with
+ * @param packages The whitelist of packages allowed in lock task mode
+ * @see #mLockTaskPackages
*/
- // TODO: Missing unit tests
- void onLockTaskPackagesUpdated() {
- boolean didSomething = false;
+ void updateLockTaskPackages(int userId, String[] packages) {
+ mLockTaskPackages.put(userId, packages);
+
+ boolean taskChanged = false;
for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
- final boolean wasWhitelisted =
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
+ final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+ || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
lockedTask.setLockTaskAuth();
- final boolean isWhitelisted =
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
- if (wasWhitelisted && !isWhitelisted) {
- // Lost whitelisting authorization. End it now.
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
- lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
- removeLockedTask(lockedTask);
- lockedTask.performClearTaskLocked();
- didSomething = true;
+ final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+ || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+
+ if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
+ || lockedTask.userId != userId
+ || !wasWhitelisted || isWhitelisted) {
+ continue;
}
+
+ // Terminate locked tasks that have recently lost whitelist authorization.
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
+ lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
+ removeLockedTask(lockedTask);
+ lockedTask.performClearTaskLocked();
+ taskChanged = true;
}
for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
@@ -484,22 +500,40 @@ public class LockTaskController {
stack.onLockTaskPackagesUpdatedLocked();
}
}
+
final ActivityRecord r = mSupervisor.topRunningActivityLocked();
- final TaskRecord task = r != null ? r.getTask() : null;
- if (mLockTaskModeTasks.isEmpty() && task != null
+ final TaskRecord task = (r != null) ? r.getTask() : null;
+ if (mLockTaskModeTasks.isEmpty() && task!= null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
// This task must have just been authorized.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
"onLockTaskPackagesUpdated: starting new locktask task=" + task);
- setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated",
- false);
- didSomething = true;
+ setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
+ taskChanged = true;
}
- if (didSomething) {
+
+ if (taskChanged) {
mSupervisor.resumeFocusedStackTopActivityLocked();
}
}
+ boolean isPackageWhitelisted(int userId, String pkg) {
+ if (pkg == null) {
+ return false;
+ }
+ String[] whitelist;
+ whitelist = mLockTaskPackages.get(userId);
+ if (whitelist == null) {
+ return false;
+ }
+ for (String whitelistedPkg : whitelist) {
+ if (pkg.equals(whitelistedPkg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* @return the topmost locked task
*/
@@ -556,8 +590,18 @@ public class LockTaskController {
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString());
- pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks);
+ pw.println(prefix + "LockTaskController");
+ prefix = prefix + " ";
+ pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString());
+ pw.println(prefix + "mLockTaskModeTasks=");
+ for (int i = 0; i < mLockTaskModeTasks.size(); ++i) {
+ pw.println(prefix + " #" + i + " " + mLockTaskModeTasks.get(i));
+ }
+ pw.println(prefix + "mLockTaskPackages (userId:packages)=");
+ for (int i = 0; i < mLockTaskPackages.size(); ++i) {
+ pw.println(prefix + " u" + mLockTaskPackages.keyAt(i)
+ + ":" + Arrays.toString(mLockTaskPackages.valueAt(i)));
+ }
}
private String lockTaskModeToString() {
diff --git a/com/android/server/am/PendingIntentRecord.java b/com/android/server/am/PendingIntentRecord.java
index ee593866..7930f534 100644
--- a/com/android/server/am/PendingIntentRecord.java
+++ b/com/android/server/am/PendingIntentRecord.java
@@ -308,7 +308,7 @@ final class PendingIntentRecord extends IIntentSender.Stub {
boolean sendFinish = finishedReceiver != null;
int userId = key.userId;
if (userId == UserHandle.USER_CURRENT) {
- userId = owner.mUserController.getCurrentOrTargetUserIdLocked();
+ userId = owner.mUserController.getCurrentOrTargetUserId();
}
int res = 0;
switch (key.type) {
diff --git a/com/android/server/am/PinnedActivityStack.java b/com/android/server/am/PinnedActivityStack.java
index a601ee1c..27269791 100644
--- a/com/android/server/am/PinnedActivityStack.java
+++ b/com/android/server/am/PinnedActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
import android.app.RemoteAction;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -32,15 +35,16 @@ import java.util.List;
class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
implements PinnedStackWindowListener {
- PinnedActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
- ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
- super(display, stackId, supervisor, recentTasks, onTop);
+ PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+ boolean onTop) {
+ super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
}
@Override
PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
Rect outBounds) {
- return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/com/android/server/am/ProcessStatsService.java b/com/android/server/am/ProcessStatsService.java
index 39aed7cf..effb86c1 100644
--- a/com/android/server/am/ProcessStatsService.java
+++ b/com/android/server/am/ProcessStatsService.java
@@ -23,11 +23,14 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.DumpUtils;
@@ -622,13 +625,17 @@ public final class ProcessStatsService extends IProcessStats.Stub {
long ident = Binder.clearCallingIdentity();
try {
- dumpInner(fd, pw, args);
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInner(pw, args);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
+ private void dumpInner(PrintWriter pw, String[] args) {
final long now = SystemClock.uptimeMillis();
boolean isCheckin = false;
@@ -1038,4 +1045,44 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
}
}
+
+ private void dumpAggregatedStats(ProtoOutputStream proto, int aggregateHours, long now) {
+ ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
+ - (ProcessStats.COMMIT_PERIOD/2));
+ if (pfd == null) {
+ return;
+ }
+ ProcessStats stats = new ProcessStats(false);
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ stats.read(stream);
+ if (stats.mReadError != null) {
+ return;
+ }
+ stats.toProto(proto, now);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ // dump current procstats
+ long nowToken = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
+ long now;
+ synchronized (mAm) {
+ now = SystemClock.uptimeMillis();
+ mProcessStats.toProto(proto, now);
+ }
+ proto.end(nowToken);
+
+ // aggregated over last 3 hours procstats
+ long tokenOf3Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS);
+ dumpAggregatedStats(proto, 3, now);
+ proto.end(tokenOf3Hrs);
+
+ // aggregated over last 24 hours procstats
+ long tokenOf24Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS);
+ dumpAggregatedStats(proto, 24, now);
+ proto.end(tokenOf24Hrs);
+
+ proto.flush();
+ }
}
diff --git a/com/android/server/am/ServiceRecord.java b/com/android/server/am/ServiceRecord.java
index 027dc086..ac85e6b1 100644
--- a/com/android/server/am/ServiceRecord.java
+++ b/com/android/server/am/ServiceRecord.java
@@ -517,11 +517,14 @@ final class ServiceRecord extends Binder {
} catch (PackageManager.NameNotFoundException e) {
}
}
- if (localForegroundNoti.getSmallIcon() == null) {
+ if (localForegroundNoti.getSmallIcon() == null
+ || nm.getNotificationChannel(localPackageName, appUid,
+ localForegroundNoti.getChannelId()) == 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/TaskChangeNotificationController.java b/com/android/server/am/TaskChangeNotificationController.java
index 82971696..5a7e7ced 100644
--- a/com/android/server/am/TaskChangeNotificationController.java
+++ b/com/android/server/am/TaskChangeNotificationController.java
@@ -95,7 +95,8 @@ class TaskChangeNotificationController {
};
private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
- l.onActivityPinned((String) m.obj, m.arg1);
+ final ActivityRecord r = (ActivityRecord) m.obj;
+ l.onActivityPinned(r.packageName, r.userId, r.getTask().taskId, r.getStackId());
};
private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
@@ -278,10 +279,9 @@ class TaskChangeNotificationController {
}
/** Notifies all listeners when an Activity is pinned. */
- void notifyActivityPinned(String packageName, int taskId) {
+ void notifyActivityPinned(ActivityRecord r) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
- final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- taskId, 0, packageName);
+ final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, r);
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
}
diff --git a/com/android/server/am/TaskPersister.java b/com/android/server/am/TaskPersister.java
index 74c4826f..61994b55 100644
--- a/com/android/server/am/TaskPersister.java
+++ b/com/android/server/am/TaskPersister.java
@@ -54,9 +54,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
public class TaskPersister {
@@ -472,8 +469,7 @@ public class TaskPersister {
final int taskId = task.taskId;
if (mStackSupervisor.anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- INVALID_STACK_ID) != null) {
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
} else if (userId != task.userId) {
diff --git a/com/android/server/am/TaskRecord.java b/com/android/server/am/TaskRecord.java
index 0e651845..0d8df796 100644
--- a/com/android/server/am/TaskRecord.java
+++ b/com/android/server/am/TaskRecord.java
@@ -18,19 +18,19 @@ 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.ASSISTANT_STACK_ID;
-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.HOME_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.RECENTS_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;
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.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;
@@ -65,6 +65,20 @@ import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.proto.TaskRecordProto.ACTIVITIES;
+import static com.android.server.am.proto.TaskRecordProto.BOUNDS;
+import static com.android.server.am.proto.TaskRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.proto.TaskRecordProto.ID;
+import static com.android.server.am.proto.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
+import static com.android.server.am.proto.TaskRecordProto.MIN_HEIGHT;
+import static com.android.server.am.proto.TaskRecordProto.MIN_WIDTH;
+import static com.android.server.am.proto.TaskRecordProto.ORIG_ACTIVITY;
+import static com.android.server.am.proto.TaskRecordProto.REAL_ACTIVITY;
+import static com.android.server.am.proto.TaskRecordProto.RESIZE_MODE;
+import static com.android.server.am.proto.TaskRecordProto.RETURN_TO_TYPE;
+import static com.android.server.am.proto.TaskRecordProto.STACK_ID;
+import static com.android.server.am.proto.TaskRecordProto.ACTIVITY_TYPE;
import static java.lang.Integer.MAX_VALUE;
@@ -78,7 +92,6 @@ import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityManager;
-import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -95,6 +108,7 @@ import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -508,8 +522,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
updateOverrideConfiguration(bounds);
if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
// re-restore the task so it can have the proper stack association.
- mService.mStackSupervisor.restoreRecentTaskLocked(this,
- FREEFORM_WORKSPACE_STACK_ID);
+ mService.mStackSupervisor.restoreRecentTaskLocked(this, null);
}
return true;
}
@@ -559,36 +572,36 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
/**
* Convenience method to reparent a task to the top or bottom position of the stack.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
- deferResume, true /* schedulePictureInPictureModeChange */, reason);
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
+ true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Convenience method to reparent a task to the top or bottom position of the stack, with
* an option to skip scheduling the picture-in-picture mode change.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ boolean schedulePictureInPictureModeChange, String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
deferResume, schedulePictureInPictureModeChange, reason);
}
- /**
- * Convenience method to reparent a task to a specific position of the stack.
- */
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, position, moveStackMode, animate, deferResume,
+ /** Convenience method to reparent a task to a specific position of the stack. */
+ boolean reparent(ActivityStack preferredStack, int position,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, position, moveStackMode, animate, deferResume,
true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Reparents the task into a preferred stack, creating it if necessary.
*
- * @param preferredStackId the stack id of the target stack to move this task
+ * @param preferredStack the target stack to move this task
* @param position the position to place this task in the new stack
* @param animate whether or not we should wait for the new window created as a part of the
* reparenting to be drawn and animated in
@@ -602,13 +615,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* @param reason the caller of this reparenting
* @return whether the task was reparented
*/
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
+ // 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 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) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack();
- final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
+ final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
position == MAX_VALUE);
if (toStack == sourceStack) {
return false;
@@ -691,19 +707,22 @@ 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 (stackId == FULLSCREEN_WORKSPACE_STACK_ID
&& !Objects.equals(mBounds, toStack.mBounds)) {
kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
deferResume);
- } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
toStack.layoutTaskInStack(this, null);
bounds = mBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
- } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- if (stackId == DOCKED_STACK_ID && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
+ } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
+ if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
// Move recents to front so it is not behind home stack when going into docked
// mode
mService.mStackSupervisor.moveRecentsStackToFront(reason);
@@ -730,10 +749,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
// TODO: Handle incorrect request to move before the actual move, not after.
- supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, DEFAULT_DISPLAY, stackId);
+ final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
+ supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
+ DEFAULT_DISPLAY, stackId);
- boolean successful = (preferredStackId == stackId);
- if (successful && stackId == DOCKED_STACK_ID) {
+ boolean successful = (preferredStack == toStack);
+ if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps(false /* fromHome */);
}
@@ -857,8 +878,13 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
mResizeMode = info.resizeMode;
mSupportsPictureInPicture = info.supportsPictureInPicture();
- mLockTaskMode = info.lockTaskLaunchMode;
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();
}
@@ -921,8 +947,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
}
- ActivityStack getStack() {
- return mStack;
+ <T extends ActivityStack> T getStack() {
+ return (T) mStack;
}
/**
@@ -1396,16 +1422,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
void setLockTaskAuth() {
- 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;
- }
+ final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
switch (mLockTaskMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
- mLockTaskAuth = isLockTaskWhitelistedLocked() ?
- LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+ mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+ ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_NEVER:
@@ -1417,31 +1438,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
break;
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
- mLockTaskAuth = isLockTaskWhitelistedLocked() ?
- LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+ mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+ ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
break;
}
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
" mLockTaskAuth=" + lockTaskAuthToString());
}
- private boolean isLockTaskWhitelistedLocked() {
- String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
- if (pkg == null) {
- return false;
- }
- String[] packages = mService.mLockTaskPackages.get(userId);
- if (packages == null) {
- return false;
- }
- for (int i = packages.length - 1; i >= 0; --i) {
- if (pkg.equals(packages[i])) {
- return true;
- }
- }
- return false;
- }
-
boolean isOverHomeStack() {
return mTaskToReturnTo == ACTIVITY_TYPE_HOME;
}
@@ -1459,10 +1463,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return isResizeable(true /* checkSupportsPip */);
}
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// A task can not be docked even if it is considered resizeable because it only supports
// picture-in-picture mode but has a non-resizeable resizeMode
- return mService.mSupportsSplitScreenMultiWindow
+ return super.supportsSplitScreenWindowingMode()
+ && mService.mSupportsSplitScreenMultiWindow
&& (mService.mForceResizableActivities
|| (isResizeable(false /* checkSupportsPip */)
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
@@ -2097,39 +2103,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
}
- /**
- * Returns the correct stack to use based on task type and currently set bounds,
- * regardless of the focused stack and current stack association of the task.
- * The task will be moved (and stack focus changed) later if necessary.
- */
- int getLaunchStackId() {
- if (isActivityTypeRecents()) {
- return RECENTS_STACK_ID;
- }
- if (isActivityTypeHome()) {
- return HOME_STACK_ID;
- }
- if (isActivityTypeAssistant()) {
- return ASSISTANT_STACK_ID;
- }
- if (mBounds != null) {
- return FREEFORM_WORKSPACE_STACK_ID;
- }
- return FULLSCREEN_WORKSPACE_STACK_ID;
- }
-
/** Returns the bounds that should be used to launch this task. */
private Rect getLaunchBounds() {
if (mStack == null) {
return null;
}
- final int stackId = mStack.mStackId;
- if (stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID
- || stackId == ASSISTANT_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || (stackId == DOCKED_STACK_ID && !isResizeable())) {
+ final int windowingMode = getWindowingMode();
+ if (!isActivityTypeStandardOrUndefined()
+ || windowingMode == WINDOWING_MODE_FULLSCREEN
+ || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
return isResizeable() ? mStack.mBounds : null;
} else if (!getWindowConfiguration().persistTaskBounds()) {
return mStack.mBounds;
@@ -2275,4 +2258,34 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
stringName = sb.toString();
return toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER);
+ proto.write(ID, taskId);
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ ActivityRecord activity = mActivities.get(i);
+ activity.writeToProto(proto, ACTIVITIES);
+ }
+ proto.write(STACK_ID, mStack.mStackId);
+ if (mLastNonFullscreenBounds != null) {
+ mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS);
+ }
+ if (realActivity != null) {
+ proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
+ }
+ if (origActivity != null) {
+ proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
+ }
+ proto.write(ACTIVITY_TYPE, getActivityType());
+ proto.write(RETURN_TO_TYPE, mTaskToReturnTo);
+ proto.write(RESIZE_MODE, mResizeMode);
+ proto.write(FULLSCREEN, mFullscreen);
+ if (mBounds != null) {
+ mBounds.writeToProto(proto, BOUNDS);
+ }
+ proto.write(MIN_WIDTH, mMinWidth);
+ proto.write(MIN_HEIGHT, mMinHeight);
+ proto.end(token);
+ }
}
diff --git a/com/android/server/am/UserController.java b/com/android/server/am/UserController.java
index db6bb7d8..5a295942 100644
--- a/com/android/server/am/UserController.java
+++ b/com/android/server/am/UserController.java
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -30,14 +31,6 @@ import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
-import static com.android.server.am.ActivityManagerService.SYSTEM_USER_UNLOCK_MSG;
-import static com.android.server.am.ActivityManagerService.USER_SWITCH_CALLBACKS_TIMEOUT_MSG;
-import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
import static com.android.server.am.UserState.STATE_BOOTING;
import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
@@ -68,6 +61,7 @@ import android.os.IBinder;
import android.os.IProgressListener;
import android.os.IRemoteCallback;
import android.os.IUserManager;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -78,6 +72,7 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
@@ -93,6 +88,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
+import com.android.server.SystemServiceManager;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
@@ -107,25 +103,64 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
+ *
+ * <p>This class use {@link #mLock} to synchronize access to internal state. Methods that require
+ * {@link #mLock} to be held should have "LU" suffix in the name.
+ *
+ * <p><strong>Important:</strong> Synchronized code, i.e. one executed inside a synchronized(mLock)
+ * block or inside LU method, should only access internal state of this class or make calls to
+ * other LU methods. Non-LU method calls or calls to external classes are discouraged as they
+ * may cause lock inversion.
*/
-class UserController {
+class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
- // Maximum number of users we allow to be running at a time.
+ /**
+ * Maximum number of users we allow to be running at a time, including the system user and
+ * its profiles.
+ * Note changing this to 2 is not recommended, since that would mean, if the user uses
+ * work profile and then switch to a secondary user, then the work profile user would be killed,
+ * which should work fine, but aggressively killing the work profile user that has just been
+ * running could cause data loss. (Even without work profile, witching from secondary user A
+ * to secondary user B would cause similar issues on user B.)
+ *
+ * TODO: Consider adding or replacing with "MAX_RUNNING_*SECONDARY*_USERS", which is the max
+ * number of running *secondary, switchable* users.
+ */
static final int MAX_RUNNING_USERS = 3;
// Amount of time we wait for observers to handle a user switch before
// giving up on them and unfreezing the screen.
static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000;
+ // ActivityManager thread message constants
+ static final int REPORT_USER_SWITCH_MSG = 10;
+ static final int CONTINUE_USER_SWITCH_MSG = 20;
+ static final int USER_SWITCH_TIMEOUT_MSG = 30;
+ static final int START_PROFILES_MSG = 40;
+ static final int SYSTEM_USER_START_MSG = 50;
+ static final int SYSTEM_USER_CURRENT_MSG = 60;
+ static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
+ static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
+ static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
+ static final int SYSTEM_USER_UNLOCK_MSG = 100;
+ static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
+ static final int START_USER_SWITCH_FG_MSG = 120;
+
+ // UI thread message constants
+ static final int START_USER_SWITCH_UI_MSG = 1000;
+
// If a callback wasn't called within USER_SWITCH_CALLBACKS_TIMEOUT_MS after
// USER_SWITCH_TIMEOUT_MS, an error is reported. Usually it indicates a problem in the observer
// when it never calls back.
private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000;
- private final Object mLock;
+ // Lock for internal state.
+ private final Object mLock = new Object();
+
private final Injector mInjector;
private final Handler mHandler;
+ private final Handler mUiHandler;
// Holds the current foreground user's id. Use mLock when updating
@GuardedBy("mLock")
@@ -161,7 +196,8 @@ class UserController {
/**
* Mapping from each known user ID to the profile group ID it is associated with.
*/
- private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseIntArray mUserProfileGroupIds = new SparseIntArray();
/**
* Registered observers of the user switching mechanics.
@@ -192,26 +228,25 @@ class UserController {
@VisibleForTesting
UserController(Injector injector) {
mInjector = injector;
- mLock = injector.getLock();
- mHandler = injector.getHandler();
+ mHandler = mInjector.getHandler(this);
+ mUiHandler = mInjector.getUiHandler(this);
// User 0 is the first and only user that runs at boot.
final UserState uss = new UserState(UserHandle.SYSTEM);
mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
- updateStartedUserArrayLocked();
+ updateStartedUserArrayLU();
}
void finishUserSwitch(UserState uss) {
+ finishUserBoot(uss);
+ startProfiles();
synchronized (mLock) {
- finishUserBoot(uss);
-
- startProfilesLocked();
- stopRunningUsersLocked(MAX_RUNNING_USERS);
+ stopRunningUsersLU(MAX_RUNNING_USERS);
}
}
- void stopRunningUsersLocked(int maxRunningUsers) {
+ void stopRunningUsersLU(int maxRunningUsers) {
int num = mUserLru.size();
int i = 0;
while (num > maxRunningUsers && i < mUserLru.size()) {
@@ -240,7 +275,7 @@ class UserController {
continue;
}
// This is a user to be stopped.
- if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) {
+ if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) {
num--;
}
num--;
@@ -258,55 +293,57 @@ class UserController {
Slog.d(TAG, "Finishing user boot " + userId);
synchronized (mLock) {
// Bail if we ended up with a stale user
- if (mStartedUsers.get(userId) != uss) return;
+ if (mStartedUsers.get(userId) != uss) {
+ return;
+ }
+ }
- // We always walk through all the user lifecycle states to send
- // consistent developer events. We step into RUNNING_LOCKED here,
- // but we might immediately step into RUNNING below if the user
- // storage is already unlocked.
- if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- // Do not report secondary users, runtime restarts or first boot/upgrade
- if (userId == UserHandle.USER_SYSTEM
- && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(),
- "framework_locked_boot_completed", uptimeSeconds);
- final int MAX_UPTIME_SECONDS = 120;
- if (uptimeSeconds > MAX_UPTIME_SECONDS) {
- Slog.wtf("SystemServerTiming",
- "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
- }
+ // We always walk through all the user lifecycle states to send
+ // consistent developer events. We step into RUNNING_LOCKED here,
+ // but we might immediately step into RUNNING below if the user
+ // storage is already unlocked.
+ if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ // Do not report secondary users, runtime restarts or first boot/upgrade
+ if (userId == UserHandle.USER_SYSTEM
+ && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
+ int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger.histogram(mInjector.getContext(),
+ "framework_locked_boot_completed", uptimeSeconds);
+ final int MAX_UPTIME_SECONDS = 120;
+ if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+ Slog.wtf("SystemServerTiming",
+ "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
}
+ }
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
- userId, 0));
- Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(intent, null, resultTo, 0, null, null,
- new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
- }
-
- // We need to delay unlocking managed profiles until the parent user
- // is also unlocked.
- if (mInjector.getUserManager().isManagedProfile(userId)) {
- final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
- if (parent != null
- && isUserRunningLocked(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
- Slog.d(TAG, "User " + userId + " (parent " + parent.id
- + "): attempting unlock because parent is unlocked");
- maybeUnlockUser(userId);
- } else {
- String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
- Slog.d(TAG, "User " + userId + " (parent " + parentId
- + "): delaying unlock because parent is locked");
- }
- } else {
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+ userId, 0));
+ Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+
+ // We need to delay unlocking managed profiles until the parent user
+ // is also unlocked.
+ if (mInjector.getUserManager().isManagedProfile(userId)) {
+ final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
+ if (parent != null
+ && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
+ Slog.d(TAG, "User " + userId + " (parent " + parent.id
+ + "): attempting unlock because parent is unlocked");
maybeUnlockUser(userId);
+ } else {
+ String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
+ Slog.d(TAG, "User " + userId + " (parent " + parentId
+ + "): delaying unlock because parent is locked");
}
+ } else {
+ maybeUnlockUser(userId);
}
}
@@ -316,34 +353,30 @@ class UserController {
*/
private void finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
- boolean proceedWithUnlock = false;
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- proceedWithUnlock = true;
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ return;
}
}
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ uss.mUnlockProgress.start();
- if (proceedWithUnlock) {
- uss.mUnlockProgress.start();
-
- // Prepare app storage before we go any further
- uss.mUnlockProgress.setProgress(5,
- mInjector.getContext().getString(R.string.android_start_title));
- mInjector.getUserManager().onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
+ // Prepare app storage before we go any further
+ uss.mUnlockProgress.setProgress(5,
+ mInjector.getContext().getString(R.string.android_start_title));
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
+ uss.mUnlockProgress.setProgress(20);
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
- }
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
}
/**
@@ -352,63 +385,63 @@ class UserController {
*/
void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- uss.mUnlockProgress.finish();
-
- // Dispatch unlocked to external apps
- final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
- unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- unlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- mInjector.broadcastIntentLocked(unlockedIntent, null, null, 0, null,
- null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- userId);
-
- if (getUserInfo(userId).isManagedProfile()) {
- UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
- if (parent != null) {
- final Intent profileUnlockedIntent = new Intent(
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
- profileUnlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
+ return;
+ }
+ }
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ uss.mUnlockProgress.finish();
+ // Dispatch unlocked to external apps
+ final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,
+ null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ userId);
+
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ profileUnlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
- mInjector.broadcastIntentLocked(profileUnlockedIntent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID,
- parent.id);
- }
- }
+ mInjector.broadcastIntent(profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
- // Send PRE_BOOT broadcasts if user fingerprint changed; we
- // purposefully block sending BOOT_COMPLETED until after all
- // PRE_BOOT receivers are finished to avoid ANR'ing apps
- final UserInfo info = getUserInfo(userId);
- if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
- // Suppress double notifications for managed profiles that
- // were unlocked automatically as part of their parent user
- // being unlocked.
- final boolean quiet;
- if (info.isManagedProfile()) {
- quiet = !uss.tokenProvided
- || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
- } else {
- quiet = false;
- }
- mInjector.sendPreBootBroadcast(userId, quiet,
- () -> finishUserUnlockedCompleted(uss));
- } else {
- finishUserUnlockedCompleted(uss);
- }
+ // Send PRE_BOOT broadcasts if user fingerprint changed; we
+ // purposefully block sending BOOT_COMPLETED until after all
+ // PRE_BOOT receivers are finished to avoid ANR'ing apps
+ final UserInfo info = getUserInfo(userId);
+ if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
+ // Suppress double notifications for managed profiles that
+ // were unlocked automatically as part of their parent user
+ // being unlocked.
+ final boolean quiet;
+ if (info.isManagedProfile()) {
+ quiet = !uss.tokenProvided
+ || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+ } else {
+ quiet = false;
}
+ mInjector.sendPreBootBroadcast(userId, quiet,
+ () -> finishUserUnlockedCompleted(uss));
+ } else {
+ finishUserUnlockedCompleted(uss);
}
}
@@ -417,60 +450,59 @@ class UserController {
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- final UserInfo userInfo = getUserInfo(userId);
- if (userInfo == null) {
- return;
- }
+ }
+ UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ return;
+ }
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- // Remember that we logged in
- mInjector.getUserManager().onUserLoggedIn(userId);
-
- if (!userInfo.isInitialized()) {
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.d(TAG, "Initializing user #" + userId);
- Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(intent, null,
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- // Note: performReceive is called with mService lock held
- mInjector.getUserManager().makeInitialized(userInfo.id);
- }
- }, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
- }
- }
+ // Remember that we logged in
+ mInjector.getUserManager().onUserLoggedIn(userId);
- Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
- // Do not report secondary users, runtime restarts or first boot/upgrade
- if (userId == UserHandle.USER_SYSTEM
- && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
- uptimeSeconds);
+ if (!userInfo.isInitialized()) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ Slog.d(TAG, "Initializing user #" + userId);
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ // Note: performReceive is called with mService lock held
+ mInjector.getUserManager().makeInitialized(userInfo.id);
+ }
+ }, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
}
- final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser)
- throws RemoteException {
- Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
- }
- }, 0, null, null,
- new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
+
+ Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
+ // Do not report secondary users, runtime restarts or first boot/upgrade
+ if (userId == UserHandle.USER_SYSTEM
+ && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
+ int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
+ uptimeSeconds);
+ }
+ final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(bootIntent, null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
+ }
+ }, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
int restartUser(final int userId, final boolean foreground) {
@@ -499,35 +531,35 @@ class UserController {
if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
throw new IllegalArgumentException("Can't stop system user " + userId);
}
- mInjector.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
- return stopUsersLocked(userId, force, callback);
+ return stopUsersLU(userId, force, callback);
}
}
/**
* Stops the user along with its related users. The method calls
- * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped.
+ * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
*/
- private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) {
+ private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) {
if (userId == UserHandle.USER_SYSTEM) {
return USER_OP_ERROR_IS_SYSTEM;
}
- if (isCurrentUserLocked(userId)) {
+ if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
- int[] usersToStop = getUsersToStopLocked(userId);
+ int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
for (int i = 0; i < usersToStop.length; i++) {
int relatedUserId = usersToStop[i];
- if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) {
+ if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
+ relatedUserId);
// We still need to stop the requested user if it's a force stop.
if (force) {
Slog.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
- stopSingleUserLocked(userId, callback);
+ stopSingleUserLU(userId, callback);
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -535,25 +567,22 @@ class UserController {
}
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
- stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null);
+ stopSingleUserLU(userIdToStop, userIdToStop == userId ? callback : null);
}
return USER_OP_SUCCESS;
}
- private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) {
+ private void stopSingleUserLU(final int userId, final IStopUserCallback callback) {
if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) {
// User is not started, nothing to do... but we do need to
// callback if requested.
if (callback != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- callback.userStopped(userId);
- } catch (RemoteException e) {
- }
+ mHandler.post(() -> {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
}
});
}
@@ -568,10 +597,10 @@ class UserController {
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
+ updateStartedUserArrayLU();
- long ident = Binder.clearCallingIdentity();
- try {
+ // Post to handler to obtain amLock
+ mHandler.post(() -> {
// We are going to broadcast ACTION_USER_STOPPING and then
// once that is done send a final ACTION_SHUTDOWN and then
// stop the user.
@@ -584,31 +613,24 @@ class UserController {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- finishUserStopping(userId, uss);
- }
- });
+ mHandler.post(() -> finishUserStopping(userId, uss));
}
};
+
// Clear broadcast queue for the user to avoid delivering stale broadcasts
- mInjector.clearBroadcastQueueForUserLocked(userId);
+ mInjector.clearBroadcastQueueForUser(userId);
// Kick things off.
- mInjector.broadcastIntentLocked(stoppingIntent,
+ mInjector.broadcastIntent(stoppingIntent,
null, stoppingReceiver, 0, null, null,
new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ });
}
}
void finishUserStopping(final int userId, final UserState uss) {
// On to the next.
final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
- shutdownIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
// This is the result receiver for the final shutdown broadcast.
final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
@Override
@@ -635,20 +657,19 @@ class UserController {
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
Integer.toString(userId), userId);
- mInjector.systemServiceManagerStopUser(userId);
+ mInjector.getSystemServiceManager().stopUser(userId);
- synchronized (mLock) {
- mInjector.broadcastIntentLocked(shutdownIntent,
- null, shutdownReceiver, 0, null, null, null,
- AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
- }
+ mInjector.broadcastIntent(shutdownIntent,
+ null, shutdownReceiver, 0, null, null, null,
+ AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
}
void finishUserStopped(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
boolean stopped;
ArrayList<IStopUserCallback> callbacks;
+ boolean forceStopUser = false;
synchronized (mLock) {
callbacks = new ArrayList<>(uss.mStopCallbacks);
if (mStartedUsers.get(userId) != uss) {
@@ -659,16 +680,18 @@ class UserController {
stopped = true;
// User can no longer run.
mStartedUsers.remove(userId);
- mInjector.getUserManagerInternal().removeUserState(userId);
mUserLru.remove(Integer.valueOf(userId));
- updateStartedUserArrayLocked();
-
- mInjector.activityManagerOnUserStopped(userId);
- // Clean up all state and processes associated with the user.
- // Kill all the processes for the user.
- forceStopUserLocked(userId, "finish user");
+ updateStartedUserArrayLU();
+ forceStopUser = true;
}
}
+ if (forceStopUser) {
+ mInjector.getUserManagerInternal().removeUserState(userId);
+ mInjector.activityManagerOnUserStopped(userId);
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUser(userId, "finish user");
+ }
for (int i = 0; i < callbacks.size(); i++) {
try {
@@ -680,9 +703,7 @@ class UserController {
if (stopped) {
mInjector.systemServiceManagerCleanupUser(userId);
- synchronized (mLock) {
- mInjector.getActivityStackSupervisor().removeUserLocked(userId);
- }
+ mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
if (getUserInfo(userId).isEphemeral()) {
mInjector.getUserManager().removeUser(userId);
@@ -700,39 +721,36 @@ class UserController {
* Determines the list of users that should be stopped together with the specified
* {@code userId}. The returned list includes {@code userId}.
*/
- private @NonNull int[] getUsersToStopLocked(int userId) {
+ private @NonNull int[] getUsersToStopLU(int userId) {
int startedUsersSize = mStartedUsers.size();
IntArray userIds = new IntArray();
userIds.add(userId);
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId,
+ int userGroupId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID);
+ for (int i = 0; i < startedUsersSize; i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ int startedUserId = uss.mHandle.getIdentifier();
+ // Skip unrelated users (profileGroupId mismatch)
+ int startedUserGroupId = mUserProfileGroupIds.get(startedUserId,
UserInfo.NO_PROFILE_GROUP_ID);
- for (int i = 0; i < startedUsersSize; i++) {
- UserState uss = mStartedUsers.valueAt(i);
- int startedUserId = uss.mHandle.getIdentifier();
- // Skip unrelated users (profileGroupId mismatch)
- int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
- && (userGroupId == startedUserGroupId);
- // userId has already been added
- boolean sameUserId = startedUserId == userId;
- if (!sameGroup || sameUserId) {
- continue;
- }
- userIds.add(startedUserId);
+ boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
+ && (userGroupId == startedUserGroupId);
+ // userId has already been added
+ boolean sameUserId = startedUserId == userId;
+ if (!sameGroup || sameUserId) {
+ continue;
}
+ userIds.add(startedUserId);
}
return userIds.toArray();
}
- private void forceStopUserLocked(int userId, String reason) {
- mInjector.activityManagerForceStopPackageLocked(userId, reason);
+ private void forceStopUser(int userId, String reason) {
+ mInjector.activityManagerForceStopPackage(userId, reason);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
}
@@ -741,6 +759,7 @@ class UserController {
* Stops the guest or ephemeral user if it has gone to the background.
*/
private void stopGuestOrEphemeralUserIfBackground() {
+ IntArray userIds = new IntArray();
synchronized (mLock) {
final int num = mUserLru.size();
for (int i = 0; i < num; i++) {
@@ -751,28 +770,42 @@ class UserController {
|| oldUss.state == UserState.STATE_SHUTDOWN) {
continue;
}
- UserInfo userInfo = getUserInfo(oldUserId);
- if (userInfo.isEphemeral()) {
- LocalServices.getService(UserManagerInternal.class)
- .onEphemeralUserStop(oldUserId);
- }
- if (userInfo.isGuest() || userInfo.isEphemeral()) {
- // This is a user to be stopped.
- stopUsersLocked(oldUserId, true, null);
- break;
+ userIds.add(oldUserId);
+ }
+ }
+ final int userIdsSize = userIds.size();
+ for (int i = 0; i < userIdsSize; i++) {
+ int oldUserId = userIds.get(i);
+ UserInfo userInfo = getUserInfo(oldUserId);
+ if (userInfo.isEphemeral()) {
+ LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId);
+ }
+ if (userInfo.isGuest() || userInfo.isEphemeral()) {
+ // This is a user to be stopped.
+ synchronized (mLock) {
+ stopUsersLU(oldUserId, true, null);
}
+ break;
}
}
}
- void startProfilesLocked() {
+ void scheduleStartProfiles() {
+ if (!mHandler.hasMessages(START_PROFILES_MSG)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
+ DateUtils.SECOND_IN_MILLIS);
+ }
+ }
+
+ void startProfiles() {
+ int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
+ currentUserId, false /* enabledOnly */);
List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
for (UserInfo user : profiles) {
if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
- && user.id != mCurrentUserId && !user.isQuietModeEnabled()) {
+ && user.id != currentUserId && !user.isQuietModeEnabled()) {
profilesToStart.add(user);
}
}
@@ -834,143 +867,156 @@ class UserController {
final long ident = Binder.clearCallingIdentity();
try {
- synchronized (mLock) {
- final int oldUserId = mCurrentUserId;
- if (oldUserId == userId) {
- return true;
- }
+ final int oldUserId = getCurrentUserId();
+ if (oldUserId == userId) {
+ return true;
+ }
- if (foreground) {
- // TODO: I don't think this does what the caller think it does. Seems to only
- // remove one locked task and won't work if multiple locked tasks are present.
- mInjector.getLockTaskController().clearLockTaskMode("startUser");
- }
+ if (foreground) {
+ // TODO: I don't think this does what the caller think it does. Seems to only
+ // remove one locked task and won't work if multiple locked tasks are present.
+ mInjector.clearLockTaskMode("startUser");
+ }
- final UserInfo userInfo = getUserInfo(userId);
- if (userInfo == null) {
- Slog.w(TAG, "No user info for user #" + userId);
- return false;
- }
- if (foreground && userInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
- return false;
- }
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ Slog.w(TAG, "No user info for user #" + userId);
+ return false;
+ }
+ if (foreground && userInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+ return false;
+ }
- if (foreground && mUserSwitchUiEnabled) {
- mInjector.getWindowManager().startFreezingScreen(
- R.anim.screen_user_exit, R.anim.screen_user_enter);
- }
+ if (foreground && mUserSwitchUiEnabled) {
+ mInjector.getWindowManager().startFreezingScreen(
+ R.anim.screen_user_exit, R.anim.screen_user_enter);
+ }
- boolean needStart = false;
+ boolean needStart = false;
+ boolean updateUmState = false;
+ UserState uss;
- // If the user we are switching to is not currently started, then
- // we need to start it now.
- if (mStartedUsers.get(userId) == null) {
- UserState userState = new UserState(UserHandle.of(userId));
- mStartedUsers.put(userId, userState);
- mInjector.getUserManagerInternal().setUserState(userId, userState.state);
- updateStartedUserArrayLocked();
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
+ synchronized (mLock) {
+ uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ uss = new UserState(UserHandle.of(userId));
+ mStartedUsers.put(userId, uss);
+ updateStartedUserArrayLU();
needStart = true;
+ updateUmState = true;
}
-
- final UserState uss = mStartedUsers.get(userId);
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
-
- if (foreground) {
+ }
+ if (updateUmState) {
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ }
+ if (foreground) {
+ synchronized (mLock) {
mCurrentUserId = userId;
- mInjector.updateUserConfigurationLocked();
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
- updateCurrentProfileIdsLocked();
- mInjector.getWindowManager().setCurrentUser(userId, mCurrentProfileIds);
- // Once the internal notion of the active user has switched, we lock the device
- // with the option to show the user switcher on the keyguard.
- if (mUserSwitchUiEnabled) {
- mInjector.getWindowManager().setSwitchingUser(true);
- mInjector.getWindowManager().lockNow(null);
- }
- } else {
- final Integer currentUserIdInt = mCurrentUserId;
- updateCurrentProfileIdsLocked();
- mInjector.getWindowManager().setCurrentProfileIds(mCurrentProfileIds);
+ }
+ mInjector.updateUserConfiguration();
+ updateCurrentProfileIds();
+ mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
+ // Once the internal notion of the active user has switched, we lock the device
+ // with the option to show the user switcher on the keyguard.
+ if (mUserSwitchUiEnabled) {
+ mInjector.getWindowManager().setSwitchingUser(true);
+ mInjector.getWindowManager().lockNow(null);
+ }
+ } else {
+ final Integer currentUserIdInt = mCurrentUserId;
+ updateCurrentProfileIds();
+ mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds());
+ synchronized (mLock) {
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
+ }
- // Make sure user is in the started state. If it is currently
- // stopping, we need to knock that off.
- if (uss.state == UserState.STATE_STOPPING) {
- // If we are stopping, we haven't sent ACTION_SHUTDOWN,
- // so we can just fairly silently bring the user back from
- // the almost-dead.
- uss.setState(uss.lastState);
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
- needStart = true;
- } else if (uss.state == UserState.STATE_SHUTDOWN) {
- // This means ACTION_SHUTDOWN has been sent, so we will
- // need to treat this as a new boot of the user.
- uss.setState(UserState.STATE_BOOTING);
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
- needStart = true;
+ // Make sure user is in the started state. If it is currently
+ // stopping, we need to knock that off.
+ if (uss.state == UserState.STATE_STOPPING) {
+ // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+ // so we can just fairly silently bring the user back from
+ // the almost-dead.
+ uss.setState(uss.lastState);
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ synchronized (mLock) {
+ updateStartedUserArrayLU();
}
+ needStart = true;
+ } else if (uss.state == UserState.STATE_SHUTDOWN) {
+ // This means ACTION_SHUTDOWN has been sent, so we will
+ // need to treat this as a new boot of the user.
+ uss.setState(UserState.STATE_BOOTING);
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ synchronized (mLock) {
+ updateStartedUserArrayLU();
+ }
+ needStart = true;
+ }
- if (uss.state == UserState.STATE_BOOTING) {
- // Give user manager a chance to propagate user restrictions
- // to other services and prepare app storage
- mInjector.getUserManager().onBeforeStartUser(userId);
+ if (uss.state == UserState.STATE_BOOTING) {
+ // Give user manager a chance to propagate user restrictions
+ // to other services and prepare app storage
+ mInjector.getUserManager().onBeforeStartUser(userId);
- // Booting up a new user, need to tell system services about it.
- // Note that this is on the same handler as scheduling of broadcasts,
- // which is important because it needs to go first.
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
- }
+ // Booting up a new user, need to tell system services about it.
+ // Note that this is on the same handler as scheduling of broadcasts,
+ // which is important because it needs to go first.
+ mHandler.sendMessage(
+ mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ }
- if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
- mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
- oldUserId, userId, uss));
- mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
- oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
- }
+ if (foreground) {
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
+ oldUserId));
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
+ }
- if (needStart) {
- // Send USER_STARTED broadcast
- Intent intent = new Intent(Intent.ACTION_USER_STARTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID, userId);
- }
+ if (needStart) {
+ // Send USER_STARTED broadcast
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mInjector.broadcastIntent(intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, userId);
+ }
- if (foreground) {
- moveUserToForegroundLocked(uss, oldUserId, userId);
- } else {
- finishUserBoot(uss);
- }
+ if (foreground) {
+ moveUserToForeground(uss, oldUserId, userId);
+ } else {
+ finishUserBoot(uss);
+ }
- if (needStart) {
- Intent intent = new Intent(Intent.ACTION_USER_STARTING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
- null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky,
- int sendingUser) throws RemoteException {
- }
- }, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- }
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mInjector.broadcastIntent(intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky,
+ int sendingUser) throws RemoteException {
+ }
+ }, 0, null, null,
+ new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1014,7 +1060,7 @@ class UserController {
* when the the credential-encrypted storage isn't tied to a user-provided
* PIN or pattern.
*/
- boolean maybeUnlockUser(final int userId) {
+ private boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
return unlockUserCleared(userId, null, null, null);
}
@@ -1027,65 +1073,106 @@ class UserController {
}
}
- boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
+ private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
UserState uss;
- synchronized (mLock) {
- // TODO Move this block outside of synchronized if it causes lock contention
- if (!StorageManager.isUserKeyUnlocked(userId)) {
- final UserInfo userInfo = getUserInfo(userId);
- final IStorageManager storageManager = getStorageManager();
- try {
- // We always want to unlock user storage, even user is not started yet
- storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
- } catch (RemoteException | RuntimeException e) {
- Slog.w(TAG, "Failed to unlock: " + e.getMessage());
- }
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
+ final UserInfo userInfo = getUserInfo(userId);
+ final IStorageManager storageManager = getStorageManager();
+ try {
+ // We always want to unlock user storage, even user is not started yet
+ storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.w(TAG, "Failed to unlock: " + e.getMessage());
}
- // Bail if user isn't actually running, otherwise register the given
- // listener to watch for unlock progress
+ }
+ synchronized (mLock) {
+ // Register the given listener to watch for unlock progress
uss = mStartedUsers.get(userId);
- if (uss == null) {
- notifyFinished(userId, listener);
- return false;
- } else {
+ if (uss != null) {
uss.mUnlockProgress.addListener(listener);
uss.tokenProvided = (token != null);
}
}
+ // Bail if user isn't actually running
+ if (uss == null) {
+ notifyFinished(userId, listener);
+ return false;
+ }
finishUserUnlocking(uss);
- final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>();
- synchronized (mLock) {
+ // We just unlocked a user, so let's now attempt to unlock any
+ // managed profiles under that user.
- // We just unlocked a user, so let's now attempt to unlock any
- // managed profiles under that user.
- for (int i = 0; i < mStartedUsers.size(); i++) {
- final int testUserId = mStartedUsers.keyAt(i);
- final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
- if (parent != null && parent.id == userId && testUserId != userId) {
- Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
- + "): attempting unlock because parent was just unlocked");
- childProfilesToUnlock.add(testUserId);
- }
+ // First, get list of userIds. Requires mLock, so we cannot make external calls, e.g. to UMS
+ int[] userIds;
+ synchronized (mLock) {
+ userIds = new int[mStartedUsers.size()];
+ for (int i = 0; i < userIds.length; i++) {
+ userIds[i] = mStartedUsers.keyAt(i);
}
}
-
- final int size = childProfilesToUnlock.size();
- for (int i = 0; i < size; i++) {
- maybeUnlockUser(childProfilesToUnlock.valueAt(i));
+ for (int testUserId : userIds) {
+ final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
+ if (parent != null && parent.id == userId && testUserId != userId) {
+ Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+ + "): attempting unlock because parent was just unlocked");
+ maybeUnlockUser(testUserId);
+ }
}
return true;
}
- void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
+ boolean switchUser(final int targetUserId) {
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
+ int currentUserId = getCurrentUserId();
+ UserInfo targetUserInfo = getUserInfo(targetUserId);
+ if (targetUserId == currentUserId) {
+ Slog.i(TAG, "user #" + targetUserId + " is already the current user");
+ return true;
+ }
+ if (targetUserInfo == null) {
+ Slog.w(TAG, "No user info for user #" + targetUserId);
+ return false;
+ }
+ if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) {
+ Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
+ + " when device is in demo mode");
+ return false;
+ }
+ if (!targetUserInfo.supportsSwitchTo()) {
+ Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
+ return false;
+ }
+ if (targetUserInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
+ return false;
+ }
+ synchronized (mLock) {
+ mTargetUserId = targetUserId;
+ }
+ if (mUserSwitchUiEnabled) {
+ UserInfo currentUserInfo = getUserInfo(currentUserId);
+ Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
+ mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
+ mUiHandler.sendMessage(mHandler.obtainMessage(
+ START_USER_SWITCH_UI_MSG, userNames));
+ } else {
+ mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ START_USER_SWITCH_FG_MSG, targetUserId, 0));
+ }
+ return true;
+ }
+
+ private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second);
}
- void dispatchForegroundProfileChanged(int userId) {
+ private void dispatchForegroundProfileChanged(int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
@@ -1110,7 +1197,7 @@ class UserController {
mUserSwitchObservers.finishBroadcast();
}
- void dispatchLockedBootComplete(int userId) {
+ private void dispatchLockedBootComplete(int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
@@ -1136,23 +1223,23 @@ class UserController {
synchronized (mLock) {
if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
+ " and related users");
- stopUsersLocked(oldUserId, false, null);
+ stopUsersLU(oldUserId, false, null);
}
}
- void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mLock) {
Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG);
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
// Report observers that never called back (USER_SWITCH_CALLBACKS_TIMEOUT)
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG,
oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS);
}
}
- void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
+ private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
synchronized (mLock) {
if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
@@ -1195,7 +1282,7 @@ class UserController {
if (waitingCallbacksCount.decrementAndGet() == 0
&& (curWaitingUserSwitchCallbacks
== mCurWaitingUserSwitchCallbacks)) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
}
@@ -1206,25 +1293,23 @@ class UserController {
}
} else {
synchronized (mLock) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
mUserSwitchObservers.finishBroadcast();
}
- void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
+ void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG,
+ mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
}
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
if (mUserSwitchUiEnabled) {
- synchronized (mLock) {
- mInjector.getWindowManager().stopFreezingScreen();
- }
+ mInjector.getWindowManager().stopFreezingScreen();
}
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
@@ -1234,19 +1319,18 @@ class UserController {
stopBackgroundUsersIfEnforced(oldUserId);
}
- void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
- boolean homeInFront =
- mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss);
+ private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
+ boolean homeInFront = mInjector.stackSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
- mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground");
+ mInjector.startHomeActivity(newUserId, "moveUserToForeground");
} else {
- mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked();
+ mInjector.stackSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
- sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+ sendUserSwitchBroadcasts(oldUserId, newUserId);
}
- void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
+ void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
long ident = Binder.clearCallingIdentity();
try {
Intent intent;
@@ -1260,7 +1344,7 @@ class UserController {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, profileUserId);
}
@@ -1275,7 +1359,7 @@ class UserController {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, profileUserId);
}
@@ -1283,7 +1367,7 @@ class UserController {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.MANAGE_USERS},
AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
@@ -1308,7 +1392,7 @@ class UserController {
// the value the caller will receive and someone else changing it.
// We assume that USER_CURRENT_OR_SELF will use the current user; later
// we will switch to the calling user if access to the current user fails.
- int targetUserId = unsafeConvertIncomingUserLocked(userId);
+ int targetUserId = unsafeConvertIncomingUser(userId);
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
@@ -1376,9 +1460,9 @@ class UserController {
return targetUserId;
}
- int unsafeConvertIncomingUserLocked(int userId) {
+ int unsafeConvertIncomingUser(int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
- ? getCurrentUserIdLocked(): userId;
+ ? getCurrentUserId(): userId;
}
void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
@@ -1395,19 +1479,26 @@ class UserController {
mUserSwitchObservers.register(observer, name);
}
+ void sendForegroundProfileChanged(int userId) {
+ mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
+ mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, userId, 0).sendToTarget();
+ }
+
void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
mUserSwitchObservers.unregister(observer);
}
- UserState getStartedUserStateLocked(int userId) {
- return mStartedUsers.get(userId);
+ UserState getStartedUserState(int userId) {
+ synchronized (mLock) {
+ return mStartedUsers.get(userId);
+ }
}
boolean hasStartedUserState(int userId) {
return mStartedUsers.get(userId) != null;
}
- private void updateStartedUserArrayLocked() {
+ private void updateStartedUserArrayLU() {
int num = 0;
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
@@ -1428,15 +1519,20 @@ class UserController {
}
}
- void sendBootCompletedLocked(IIntentReceiver resultTo) {
- for (int i = 0; i < mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
+ void sendBootCompleted(IIntentReceiver resultTo) {
+ // Get a copy of mStartedUsers to use outside of lock
+ SparseArray<UserState> startedUsers;
+ synchronized (mLock) {
+ startedUsers = mStartedUsers.clone();
+ }
+ for (int i = 0; i < startedUsers.size(); i++) {
+ UserState uss = startedUsers.valueAt(i);
finishUserBoot(uss, resultTo);
}
}
void onSystemReady() {
- updateCurrentProfileIdsLocked();
+ updateCurrentProfileIds();
}
/**
@@ -1444,33 +1540,35 @@ class UserController {
* user switch happens or when a new related user is started in the
* background.
*/
- private void updateCurrentProfileIdsLocked() {
- final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(mCurrentUserId,
+ private void updateCurrentProfileIds() {
+ final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(),
false /* enabledOnly */);
int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
for (int i = 0; i < currentProfileIds.length; i++) {
currentProfileIds[i] = profiles.get(i).id;
}
- mCurrentProfileIds = currentProfileIds;
+ final List<UserInfo> users = mInjector.getUserManager().getUsers(false);
+ synchronized (mLock) {
+ mCurrentProfileIds = currentProfileIds;
- synchronized (mUserProfileGroupIdsSelfLocked) {
- mUserProfileGroupIdsSelfLocked.clear();
- final List<UserInfo> users = mInjector.getUserManager().getUsers(false);
+ mUserProfileGroupIds.clear();
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
- mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
+ mUserProfileGroupIds.put(user.id, user.profileGroupId);
}
}
}
}
- int[] getStartedUserArrayLocked() {
- return mStartedUserArray;
+ int[] getStartedUserArray() {
+ synchronized (mLock) {
+ return mStartedUserArray;
+ }
}
- boolean isUserRunningLocked(int userId, int flags) {
- UserState state = getStartedUserStateLocked(userId);
+ boolean isUserRunning(int userId, int flags) {
+ UserState state = getStartedUserState(userId);
if (state == null) {
return false;
}
@@ -1533,29 +1631,38 @@ class UserController {
return getUserInfo(mCurrentUserId);
}
synchronized (mLock) {
- return getCurrentUserLocked();
+ return getCurrentUserLU();
}
}
- UserInfo getCurrentUserLocked() {
+ UserInfo getCurrentUserLU() {
int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
return getUserInfo(userId);
}
- int getCurrentOrTargetUserIdLocked() {
+ int getCurrentOrTargetUserId() {
+ synchronized (mLock) {
+ return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ }
+ }
+
+ int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
- int getCurrentUserIdLocked() {
+
+ int getCurrentUserIdLU() {
return mCurrentUserId;
}
- private boolean isCurrentUserLocked(int userId) {
- return userId == getCurrentOrTargetUserIdLocked();
+ int getCurrentUserId() {
+ synchronized (mLock) {
+ return mCurrentUserId;
+ }
}
- int setTargetUserIdLocked(int targetUserId) {
- return mTargetUserId = targetUserId;
+ private boolean isCurrentUserLU(int userId) {
+ return userId == getCurrentOrTargetUserIdLU();
}
int[] getUsers() {
@@ -1575,6 +1682,15 @@ class UserController {
return mInjector.getUserManager().exists(userId);
}
+ void enforceShellRestriction(String restriction, int userHandle) {
+ if (Binder.getCallingUid() == SHELL_UID) {
+ if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) {
+ throw new SecurityException("Shell does not have permission to access user "
+ + userHandle);
+ }
+ }
+ }
+
boolean hasUserRestriction(String restriction, int userId) {
return mInjector.getUserManager().hasUserRestriction(restriction, userId);
}
@@ -1593,22 +1709,26 @@ class UserController {
if (callingUserId == targetUserId) {
return true;
}
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
+ synchronized (mLock) {
+ int callingProfile = mUserProfileGroupIds.get(callingUserId,
UserInfo.NO_PROFILE_GROUP_ID);
- int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
+ int targetProfile = mUserProfileGroupIds.get(targetUserId,
UserInfo.NO_PROFILE_GROUP_ID);
return callingProfile != UserInfo.NO_PROFILE_GROUP_ID
&& callingProfile == targetProfile;
}
}
- boolean isCurrentProfileLocked(int userId) {
- return ArrayUtils.contains(mCurrentProfileIds, userId);
+ boolean isCurrentProfile(int userId) {
+ synchronized (mLock) {
+ return ArrayUtils.contains(mCurrentProfileIds, userId);
+ }
}
- int[] getCurrentProfileIdsLocked() {
- return mCurrentProfileIds;
+ int[] getCurrentProfileIds() {
+ synchronized (mLock) {
+ return mCurrentProfileIds;
+ }
}
/**
@@ -1633,40 +1753,107 @@ class UserController {
}
void dump(PrintWriter pw, boolean dumpAll) {
- pw.println(" mStartedUsers:");
- for (int i = 0; i < mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
- pw.print(": "); uss.dump("", pw);
- }
- pw.print(" mStartedUserArray: [");
- for (int i = 0; i < mStartedUserArray.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(mStartedUserArray[i]);
- }
- pw.println("]");
- pw.print(" mUserLru: [");
- for (int i = 0; i < mUserLru.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(mUserLru.get(i));
- }
- pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
- }
- synchronized (mUserProfileGroupIdsSelfLocked) {
- if (mUserProfileGroupIdsSelfLocked.size() > 0) {
+ synchronized (mLock) {
+ pw.println(" mStartedUsers:");
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #");
+ pw.print(uss.mHandle.getIdentifier());
+ pw.print(": ");
+ uss.dump("", pw);
+ }
+ pw.print(" mStartedUserArray: [");
+ for (int i = 0; i < mStartedUserArray.length; i++) {
+ if (i > 0)
+ pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
+ pw.print(" mUserLru: [");
+ for (int i = 0; i < mUserLru.size(); i++) {
+ if (i > 0)
+ pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
+ if (dumpAll) {
+ pw.print(" mStartedUserArray: ");
+ pw.println(Arrays.toString(mStartedUserArray));
+ }
+ if (mUserProfileGroupIds.size() > 0) {
pw.println(" mUserProfileGroupIds:");
- for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
+ for (int i=0; i< mUserProfileGroupIds.size(); i++) {
pw.print(" User #");
- pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
+ pw.print(mUserProfileGroupIds.keyAt(i));
pw.print(" -> profile #");
- pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
+ pw.println(mUserProfileGroupIds.valueAt(i));
}
}
}
}
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case START_USER_SWITCH_FG_MSG:
+ startUserInForeground(msg.arg1);
+ break;
+ case REPORT_USER_SWITCH_MSG:
+ dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ break;
+ case CONTINUE_USER_SWITCH_MSG:
+ continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ break;
+ case USER_SWITCH_TIMEOUT_MSG:
+ timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ break;
+ case USER_SWITCH_CALLBACKS_TIMEOUT_MSG:
+ timeoutUserSwitchCallbacks(msg.arg1, msg.arg2);
+ break;
+ case START_PROFILES_MSG:
+ startProfiles();
+ break;
+ case SYSTEM_USER_START_MSG:
+ mInjector.batteryStatsServiceNoteEvent(
+ BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
+ Integer.toString(msg.arg1), msg.arg1);
+ mInjector.getSystemServiceManager().startUser(msg.arg1);
+ break;
+ case SYSTEM_USER_UNLOCK_MSG:
+ final int userId = msg.arg1;
+ mInjector.getSystemServiceManager().unlockUser(userId);
+ mInjector.loadUserRecents(userId);
+ if (userId == UserHandle.USER_SYSTEM) {
+ mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ }
+ mInjector.installEncryptionUnawareProviders(userId);
+ finishUserUnlocked((UserState) msg.obj);
+ break;
+ case SYSTEM_USER_CURRENT_MSG:
+ mInjector.batteryStatsServiceNoteEvent(
+ BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
+ Integer.toString(msg.arg2), msg.arg2);
+ mInjector.batteryStatsServiceNoteEvent(
+ BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
+ Integer.toString(msg.arg1), msg.arg1);
+
+ mInjector.getSystemServiceManager().switchUser(msg.arg1);
+ break;
+ case FOREGROUND_PROFILE_CHANGED_MSG:
+ dispatchForegroundProfileChanged(msg.arg1);
+ break;
+ case REPORT_USER_SWITCH_COMPLETE_MSG:
+ dispatchUserSwitchComplete(msg.arg1);
+ break;
+ case REPORT_LOCKED_BOOT_COMPLETE_MSG:
+ dispatchLockedBootComplete(msg.arg1);
+ break;
+ case START_USER_SWITCH_UI_MSG:
+ showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj);
+ break;
+ }
+ return false;
+ }
+
@VisibleForTesting
static class Injector {
private final ActivityManagerService mService;
@@ -1677,12 +1864,12 @@ class UserController {
mService = service;
}
- protected Object getLock() {
- return mService;
+ protected Handler getHandler(Handler.Callback callback) {
+ return new Handler(mService.mHandlerThread.getLooper(), callback);
}
- protected Handler getHandler() {
- return mService.mHandler;
+ protected Handler getUiHandler(Handler.Callback callback) {
+ return new Handler(mService.mUiHandler.getLooper(), callback);
}
protected Context getContext() {
@@ -1693,13 +1880,16 @@ class UserController {
return new LockPatternUtils(getContext());
}
- protected int broadcastIntentLocked(Intent intent, String resolvedType,
+ protected int broadcastIntent(Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
- return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
- resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
- ordered, sticky, callingPid, callingUid, userId);
+ // TODO b/64165549 Verify that mLock is not held before calling AMS methods
+ synchronized (mService) {
+ return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
+ resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
+ ordered, sticky, callingPid, callingUid, userId);
+ }
}
int checkCallingPermission(String permission) {
@@ -1710,7 +1900,9 @@ class UserController {
return mService.mWindowManager;
}
void activityManagerOnUserStopped(int userId) {
- mService.onUserStoppedLocked(userId);
+ synchronized (mService) {
+ mService.onUserStoppedLocked(userId);
+ }
}
void systemServiceManagerCleanupUser(int userId) {
@@ -1740,14 +1932,14 @@ class UserController {
mService.mBatteryStatsService.noteEvent(code, name, uid);
}
- void systemServiceManagerStopUser(int userId) {
- mService.mSystemServiceManager.stopUser(userId);
- }
-
boolean isRuntimeRestarted() {
return mService.mSystemServiceManager.isRuntimeRestarted();
}
+ SystemServiceManager getSystemServiceManager() {
+ return mService.mSystemServiceManager;
+ }
+
boolean isFirstBootOrUpgrade() {
IPackageManager pm = AppGlobals.getPackageManager();
try {
@@ -1766,9 +1958,11 @@ class UserController {
}.sendNext();
}
- void activityManagerForceStopPackageLocked(int userId, String reason) {
- mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
- userId, reason);
+ void activityManagerForceStopPackage(int userId, String reason) {
+ synchronized (mService) {
+ mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
+ userId, reason);
+ }
};
int checkComponentPermission(String permission, int pid, int uid, int owningUid,
@@ -1776,20 +1970,36 @@ class UserController {
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
- void startHomeActivityLocked(int userId, String reason) {
- mService.startHomeActivityLocked(userId, reason);
+ protected void startHomeActivity(int userId, String reason) {
+ synchronized (mService) {
+ mService.startHomeActivityLocked(userId, reason);
+ }
+ }
+
+ void updateUserConfiguration() {
+ synchronized (mService) {
+ mService.updateUserConfigurationLocked();
+ }
}
- void updateUserConfigurationLocked() {
- mService.updateUserConfigurationLocked();
+ void clearBroadcastQueueForUser(int userId) {
+ synchronized (mService) {
+ mService.clearBroadcastQueueForUserLocked(userId);
+ }
+ }
+
+ void loadUserRecents(int userId) {
+ synchronized (mService) {
+ mService.mRecentTasks.loadUserRecentsLocked(userId);
+ }
}
- void clearBroadcastQueueForUserLocked(int userId) {
- mService.clearBroadcastQueueForUserLocked(userId);
+ void startPersistentApps(int matchFlags) {
+ mService.startPersistentApps(matchFlags);
}
- void enforceShellRestriction(String restriction, int userId) {
- mService.enforceShellRestriction(restriction, userId);
+ void installEncryptionUnawareProviders(int userId) {
+ mService.installEncryptionUnawareProviders(userId);
}
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser) {
@@ -1798,12 +2008,28 @@ class UserController {
d.show();
}
- ActivityStackSupervisor getActivityStackSupervisor() {
- return mService.mStackSupervisor;
+ void stackSupervisorRemoveUser(int userId) {
+ synchronized (mService) {
+ mService.mStackSupervisor.removeUserLocked(userId);
+ }
+ }
+
+ protected boolean stackSupervisorSwitchUser(int userId, UserState uss) {
+ synchronized (mService) {
+ return mService.mStackSupervisor.switchUserLocked(userId, uss);
+ }
}
- LockTaskController getLockTaskController() {
- return mService.mLockTaskController;
+ protected void stackSupervisorResumeFocusedStackTopActivity() {
+ synchronized (mService) {
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ }
+
+ protected void clearLockTaskMode(String reason) {
+ synchronized (mService) {
+ mService.mLockTaskController.clearLockTaskMode(reason);
+ }
}
}
}
diff --git a/com/android/server/am/UserState.java b/com/android/server/am/UserState.java
index 2e27387a..d36d9cbe 100644
--- a/com/android/server/am/UserState.java
+++ b/com/android/server/am/UserState.java
@@ -59,8 +59,10 @@ public final class UserState {
/**
* The last time that a provider was reported to usage stats as being brought to important
* foreground procstate.
+ * <p><strong>Important: </strong>Only access this field when holding ActivityManagerService
+ * lock.
*/
- public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
+ final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
public UserState(UserHandle handle) {
mHandle = handle;
diff --git a/com/android/server/appwidget/AppWidgetServiceImpl.java b/com/android/server/appwidget/AppWidgetServiceImpl.java
index 80b54770..a6aaaa67 100644
--- a/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2427,14 +2427,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
out.attribute(null, "p", Integer.toHexString(widget.provider.tag));
}
if (widget.options != null) {
- out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
- out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
- out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
- out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+ int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
+ int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
+ int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
+ int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
+ out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0));
+ out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0));
+ out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0));
+ out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
}
diff --git a/com/android/server/audio/AudioEventLogger.java b/com/android/server/audio/AudioEventLogger.java
index c96138ff..9ebd75bd 100644
--- a/com/android/server/audio/AudioEventLogger.java
+++ b/com/android/server/audio/AudioEventLogger.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import android.util.Log;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -47,6 +49,22 @@ public class AudioEventLogger {
}
/**
+ * Causes the string message for the event to appear in the logcat.
+ * Here is an example of how to create a new event (a StringEvent), adding it to the logger
+ * (an instance of AudioEventLogger) while also making it show in the logcat:
+ * <pre>
+ * myLogger.log(
+ * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
+ * </pre>
+ * @param tag the tag for the android.util.Log.v
+ * @return the same instance of the event
+ */
+ public Event printLog(String tag) {
+ Log.i(tag, eventToString());
+ return this;
+ }
+
+ /**
* Convert event to String.
* This method is only called when the logger history is about to the dumped,
* so this method is where expensive String conversions should be made, not when the Event
diff --git a/com/android/server/audio/AudioService.java b/com/android/server/audio/AudioService.java
index 91b15912..5eb2a8d2 100644
--- a/com/android/server/audio/AudioService.java
+++ b/com/android/server/audio/AudioService.java
@@ -461,6 +461,8 @@ public class AudioService extends IAudioService.Stub
// Forced device usage for communications
private int mForcedUseForComm;
+ private int mForcedUseForCommExt; // External state returned by getters: always consistent
+ // with requests by setters
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
@@ -749,6 +751,9 @@ public class AudioService extends IAudioService.Stub
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ mPlaybackMonitor =
+ new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
mRecordMonitor = new RecordingActivityMonitor(mContext);
@@ -2544,13 +2549,15 @@ public class AudioService extends IAudioService.Stub
}
}
int status = AudioSystem.AUDIO_STATUS_OK;
+ int actualMode;
do {
+ actualMode = mode;
if (mode == AudioSystem.MODE_NORMAL) {
// get new mode from client at top the list if any
if (!mSetModeDeathHandlers.isEmpty()) {
hdlr = mSetModeDeathHandlers.get(0);
cb = hdlr.getBinder();
- mode = hdlr.getMode();
+ actualMode = hdlr.getMode();
if (DEBUG_MODE) {
Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
+ hdlr.mPid);
@@ -2574,12 +2581,11 @@ public class AudioService extends IAudioService.Stub
hdlr.setMode(mode);
}
- if (mode != mMode) {
- status = AudioSystem.setPhoneState(mode);
+ if (actualMode != mMode) {
+ status = AudioSystem.setPhoneState(actualMode);
if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + mode); }
- mMode = mode;
- mModeLogger.log(new PhoneStateEvent(caller, pid, mode));
+ if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
+ mMode = actualMode;
} else {
if (hdlr != null) {
mSetModeDeathHandlers.remove(hdlr);
@@ -2595,13 +2601,16 @@ public class AudioService extends IAudioService.Stub
} while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (mode != AudioSystem.MODE_NORMAL) {
+ if (actualMode != AudioSystem.MODE_NORMAL) {
if (mSetModeDeathHandlers.isEmpty()) {
Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
} else {
newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
}
+ // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+ mModeLogger.log(
+ new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
int device = getDeviceForStream(streamType);
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
@@ -2890,13 +2899,14 @@ public class AudioService extends IAudioService.Stub
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
+ mForcedUseForCommExt = mForcedUseForComm;
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
}
/** @see AudioManager#isSpeakerphoneOn() */
public boolean isSpeakerphoneOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
@@ -2904,6 +2914,13 @@ public class AudioService extends IAudioService.Stub
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+
+ // Only enable calls from system components
+ if (Binder.getCallingUid() >= FIRST_APPLICATION_UID) {
+ mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
+ return;
+ }
+
// for logging only
final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
@@ -2913,11 +2930,21 @@ public class AudioService extends IAudioService.Stub
public void setBluetoothScoOnInt(boolean on, String eventSource) {
if (on) {
+ // do not accept SCO ON if SCO audio is not connected
+ synchronized(mScoClients) {
+ if ((mBluetoothHeadset != null) &&
+ (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ return;
+ }
+ }
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
-
+ mForcedUseForCommExt = mForcedUseForComm;
+ AudioSystem.setParameters("BT_SCO="+ (on ? "on" : "off"));
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
@@ -2926,7 +2953,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#isBluetoothScoOn() */
public boolean isBluetoothScoOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO);
}
/** @see AudioManager#setBluetoothA2dpOn(boolean) */
@@ -4134,7 +4161,8 @@ public class AudioService extends IAudioService.Stub
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
}
synchronized (mConnectedDevices) {
- if ((newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
+ if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0)
@@ -6134,12 +6162,12 @@ public class AudioService extends IAudioService.Stub
private int mSafeMediaVolumeIndex;
// mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index
// corresponding to a gain of -30 dBFS in audio flinger mixer.
- // We remove -15 dBs from the theoretical -15dB to account for the EQ boost when bands are set
- // to max gain.
+ // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
+ // amplification when both effects are on with all band gains at maximum.
// This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
// the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
private int mSafeUsbMediaVolumeIndex;
- private static final float SAFE_VOLUME_GAIN_DBFS = -30.0f;
+ private static final float SAFE_VOLUME_GAIN_DBFS = -37.0f;
// mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
@@ -6952,7 +6980,7 @@ public class AudioService extends IAudioService.Stub
//======================
// Audio playback notification
//======================
- private final PlaybackActivityMonitor mPlaybackMonitor = new PlaybackActivityMonitor();
+ private final PlaybackActivityMonitor mPlaybackMonitor;
public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
final boolean isPrivileged =
diff --git a/com/android/server/audio/AudioServiceEvents.java b/com/android/server/audio/AudioServiceEvents.java
index 634c8c27..9d9e35bd 100644
--- a/com/android/server/audio/AudioServiceEvents.java
+++ b/com/android/server/audio/AudioServiceEvents.java
@@ -26,20 +26,27 @@ public class AudioServiceEvents {
final static class PhoneStateEvent extends AudioEventLogger.Event {
final String mPackage;
- final int mPid;
- final int mMode;
+ final int mOwnerPid;
+ final int mRequesterPid;
+ final int mRequestedMode;
+ final int mActualMode;
- PhoneStateEvent(String callingPackage, int pid, int mode) {
+ PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode,
+ int ownerPid, int actualMode) {
mPackage = callingPackage;
- mPid = pid;
- mMode = mode;
+ mRequesterPid = requesterPid;
+ mRequestedMode = requestedMode;
+ mOwnerPid = ownerPid;
+ mActualMode = actualMode;
}
@Override
public String eventToString() {
- return new StringBuilder("setMode(").append(AudioSystem.modeToString(mMode))
+ return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode))
.append(") from package=").append(mPackage)
- .append(" pid=").append(mPid).toString();
+ .append(" pid=").append(mRequesterPid)
+ .append(" selected mode=").append(AudioSystem.modeToString(mActualMode))
+ .append(" by pid=").append(mOwnerPid).toString();
}
}
diff --git a/com/android/server/audio/MediaFocusControl.java b/com/android/server/audio/MediaFocusControl.java
index 7d742ffd..c5f563c7 100644
--- a/com/android/server/audio/MediaFocusControl.java
+++ b/com/android/server/audio/MediaFocusControl.java
@@ -89,6 +89,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
pw.println("\nMediaFocusControl dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
+ pw.println("\n");
+ // log
+ mEventLogger.dump(pw);
}
//=================================================================
@@ -120,6 +123,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
private final static Object mAudioFocusLock = new Object();
/**
+ * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
+ */
+ private static final int MAX_STACK_SIZE = 100;
+
+ private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+ "focus commands as seen by MediaFocusControl");
+
+ /**
* Discard the current audio focus owner.
* Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
* focus), remove it from the stack, and clear the remote control display.
@@ -643,11 +654,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk) {
- Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId
- + " req=" + focusChangeHint
- + " flags=0x" + Integer.toHexString(flags));
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId + " callingPack=" + callingPackageName
+ + " req=" + focusChangeHint
+ + " flags=0x" + Integer.toHexString(flags)
+ + " sdk=" + sdk))
+ .printLog(TAG));
// we need a valid binder callback for clients
if (!cb.pingBinder()) {
Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -660,6 +674,11 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
synchronized(mAudioFocusLock) {
+ if (mFocusStack.size() > MAX_STACK_SIZE) {
+ Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
boolean enteringRingOrCall = !mRingOrCallActive
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
if (enteringRingOrCall) { mRingOrCallActive = true; }
@@ -770,10 +789,12 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
String callingPackageName) {
- // AudioAttributes are currently ignored, to be used for zones
- Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId);
+ // AudioAttributes are currently ignored, to be used for zones / a11y
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId))
+ .printLog(TAG));
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
diff --git a/com/android/server/audio/PlaybackActivityMonitor.java b/com/android/server/audio/PlaybackActivityMonitor.java
index d1a37af5..6506cf7f 100644
--- a/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,6 +17,8 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
@@ -90,7 +92,14 @@ public final class PlaybackActivityMonitor
private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
new HashMap<Integer, AudioPlaybackConfiguration>();
- PlaybackActivityMonitor() {
+ private final Context mContext;
+ private int mSavedAlarmVolume = -1;
+ private final int mMaxAlarmVolume;
+ private int mPrivilegedAlarmActiveCount = 0;
+
+ PlaybackActivityMonitor(Context context, int maxAlarmVolume) {
+ mContext = context;
+ mMaxAlarmVolume = maxAlarmVolume;
PlayMonitorClient.sListenerDeathMonitor = this;
AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
}
@@ -105,7 +114,7 @@ public final class PlaybackActivityMonitor
if (index >= 0) {
if (!disable) {
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- mEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
+ sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
}
mBannedUids.remove(index);
// nothing else to do, future playback requests from this uid are ok
@@ -116,7 +125,7 @@ public final class PlaybackActivityMonitor
checkBanPlayer(apc, uid);
}
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- mEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
+ sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
}
mBannedUids.add(new Integer(uid));
} // no else to handle, uid already not in list, so enabling again is no-op
@@ -151,7 +160,7 @@ public final class PlaybackActivityMonitor
new AudioPlaybackConfiguration(pic, newPiid,
Binder.getCallingUid(), Binder.getCallingPid());
apc.init();
- mEventLogger.log(new NewPlayerEvent(apc));
+ sEventLogger.log(new NewPlayerEvent(apc));
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
}
@@ -163,7 +172,7 @@ public final class PlaybackActivityMonitor
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
- mEventLogger.log(new AudioAttrEvent(piid, attr));
+ sEventLogger.log(new AudioAttrEvent(piid, attr));
change = apc.handleAudioAttributesEvent(attr);
} else {
Log.e(TAG, "Error updating audio attributes");
@@ -175,6 +184,38 @@ public final class PlaybackActivityMonitor
}
}
+ private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
+ if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
+ apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ 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()) ==
+ PackageManager.PERMISSION_GRANTED) {
+ if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
+ apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if (mPrivilegedAlarmActiveCount++ == 0) {
+ mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
+ AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
+ AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM,
+ mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ }
+ } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
+ apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if (--mPrivilegedAlarmActiveCount == 0) {
+ if (AudioSystem.getStreamVolumeIndex(
+ AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
+ mMaxAlarmVolume) {
+ AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM,
+ mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ }
+ }
+ }
+ }
+ }
+ }
+
public void playerEvent(int piid, int event, int binderUid) {
if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
final boolean change;
@@ -183,12 +224,12 @@ public final class PlaybackActivityMonitor
if (apc == null) {
return;
}
- mEventLogger.log(new PlayerEvent(piid, event));
+ sEventLogger.log(new PlayerEvent(piid, event));
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
for (Integer uidInteger: mBannedUids) {
if (checkBanPlayer(apc, uidInteger.intValue())) {
// player was banned, do not update its state
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new AudioEventLogger.StringEvent(
"not starting piid:" + piid + " ,is banned"));
return;
}
@@ -200,6 +241,7 @@ public final class PlaybackActivityMonitor
}
if (checkConfigurationCaller(piid, apc, binderUid)) {
//TODO add generation counter to only update to the latest state
+ checkVolumeForPrivilegedAlarm(apc, event);
change = apc.handleStateEvent(event);
} else {
Log.e(TAG, "Error handling event " + event);
@@ -216,7 +258,7 @@ public final class PlaybackActivityMonitor
public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) {
// no check on UID yet because this is only for logging at the moment
- mEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
+ sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
}
public void releasePlayer(int piid, int binderUid) {
@@ -224,10 +266,11 @@ public final class PlaybackActivityMonitor
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new AudioEventLogger.StringEvent(
"releasing player piid:" + piid));
mPlayers.remove(new Integer(piid));
mDuckingManager.removeReleased(apc);
+ checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
}
}
@@ -278,7 +321,7 @@ public final class PlaybackActivityMonitor
}
pw.println("\n");
// log
- mEventLogger.dump(pw);
+ sEventLogger.dump(pw);
}
}
@@ -456,7 +499,8 @@ public final class PlaybackActivityMonitor
}
if (mute) {
try {
- Log.v(TAG, "call: muting player" + piid + " uid:" + apc.getClientUid());
+ sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:"
+ + piid + " uid:" + apc.getClientUid())).printLog(TAG));
apc.getPlayerProxy().setVolume(0.0f);
mMutedPlayers.add(new Integer(piid));
} catch (Exception e) {
@@ -480,7 +524,8 @@ public final class PlaybackActivityMonitor
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc != null) {
try {
- Log.v(TAG, "call: unmuting player" + piid + " uid:" + apc.getClientUid());
+ sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:"
+ + piid).printLog(TAG));
apc.getPlayerProxy().setVolume(1.0f);
} catch (Exception e) {
Log.e(TAG, "call: error unmuting player " + piid + " uid:"
@@ -669,8 +714,7 @@ public final class PlaybackActivityMonitor
return;
}
try {
- Log.v(TAG, "ducking (skipRamp=" + skipRamp + ") player piid:"
- + apc.getPlayerInterfaceId() + " uid:" + mUid);
+ sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_VSHAPE,
skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
@@ -685,7 +729,8 @@ public final class PlaybackActivityMonitor
final AudioPlaybackConfiguration apc = players.get(piid);
if (apc != null) {
try {
- Log.v(TAG, "unducking player " + piid + " uid:" + mUid);
+ sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:"
+ + piid)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_ID,
VolumeShaper.Operation.REVERSE);
@@ -772,7 +817,28 @@ public final class PlaybackActivityMonitor
}
}
- private final static class AudioAttrEvent extends AudioEventLogger.Event {
+ private static final class DuckEvent extends AudioEventLogger.Event {
+ private final int mPlayerIId;
+ private final boolean mSkipRamp;
+ private final int mClientUid;
+ private final int mClientPid;
+
+ DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ mPlayerIId = apc.getPlayerInterfaceId();
+ mSkipRamp = skipRamp;
+ mClientUid = apc.getClientUid();
+ mClientPid = apc.getClientPid();
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("ducking player piid:").append(mPlayerIId)
+ .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
+ .append(" skip ramp:").append(mSkipRamp).toString();
+ }
+ }
+
+ private static final class AudioAttrEvent extends AudioEventLogger.Event {
private final int mPlayerIId;
private final AudioAttributes mPlayerAttr;
@@ -787,6 +853,6 @@ public final class PlaybackActivityMonitor
}
}
- private final AudioEventLogger mEventLogger = new AudioEventLogger(100,
+ private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
"playback activity as reported through PlayerBase");
}
diff --git a/com/android/server/autofill/AutofillManagerService.java b/com/android/server/autofill/AutofillManagerService.java
index ddc819d3..1f4161ac 100644
--- a/com/android/server/autofill/AutofillManagerService.java
+++ b/com/android/server/autofill/AutofillManagerService.java
@@ -115,11 +115,24 @@ public final class AutofillManagerService extends SystemService {
private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
private final LocalLog mRequestsHistory = new LocalLog(20);
+ private final LocalLog mUiLatencyHistory = new LocalLog(20);
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ if (sDebug) Slog.d(TAG, "Close system dialogs");
+
+ // TODO(b/64940307): we need to destroy all sessions that are finished but showing
+ // Save UI because there is no way to show the Save UI back when the activity
+ // beneath it is brought back to top. Ideally, we should just hide the UI and
+ // bring it back when the activity resumes.
+ synchronized (mLock) {
+ for (int i = 0; i < mServicesCache.size(); i++) {
+ mServicesCache.valueAt(i).destroyFinishedSessionsLocked();
+ }
+ }
+
mUi.hideAll(null);
}
}
@@ -294,7 +307,7 @@ public final class AutofillManagerService extends SystemService {
AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
if (service == null) {
service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
- resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
+ mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
}
return service;
@@ -650,7 +663,7 @@ public final class AutofillManagerService extends SystemService {
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service == null) return false;
- return Objects.equals(packageName, service.getPackageName());
+ return Objects.equals(packageName, service.getServicePackageName());
}
}
@@ -724,6 +737,8 @@ public final class AutofillManagerService extends SystemService {
if (showHistory) {
pw.println("Requests history:");
mRequestsHistory.reverseDump(fd, pw, args);
+ pw.println("UI latency history:");
+ mUiLatencyHistory.reverseDump(fd, pw, args);
}
} finally {
setDebugLocked(oldDebug);
diff --git a/com/android/server/autofill/AutofillManagerServiceImpl.java b/com/android/server/autofill/AutofillManagerServiceImpl.java
index f2f96f82..862070ad 100644
--- a/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -35,6 +35,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
@@ -63,6 +64,8 @@ import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.HandlerCaller;
import com.android.server.autofill.ui.AutoFillUI;
@@ -89,6 +92,7 @@ final class AutofillManagerServiceImpl {
private final Context mContext;
private final Object mLock;
private final AutoFillUI mUi;
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
private RemoteCallbackList<IAutoFillManagerClient> mClients;
private AutofillServiceInfo mInfo;
@@ -96,6 +100,8 @@ final class AutofillManagerServiceImpl {
private static final Random sRandom = new Random();
private final LocalLog mRequestsHistory;
+ private final LocalLog mUiLatencyHistory;
+
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@@ -137,17 +143,19 @@ final class AutofillManagerServiceImpl {
private long mLastPrune = 0;
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
- int userId, AutoFillUI ui, boolean disabled) {
+ LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) {
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
+ mUiLatencyHistory = uiLatencyHistory;
mUserId = userId;
mUi = ui;
updateLocked(disabled);
}
+ @Nullable
CharSequence getServiceName() {
- final String packageName = getPackageName();
+ final String packageName = getServicePackageName();
if (packageName == null) {
return null;
}
@@ -162,7 +170,8 @@ final class AutofillManagerServiceImpl {
}
}
- String getPackageName() {
+ @Nullable
+ String getServicePackageName() {
final ComponentName serviceComponent = getServiceComponentName();
if (serviceComponent != null) {
return serviceComponent.getPackageName();
@@ -216,8 +225,10 @@ final class AutofillManagerServiceImpl {
if (serviceInfo != null) {
mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
serviceComponent, mUserId);
+ 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);
}
final boolean isEnabled = isEnabled();
if (wasEnabled != isEnabled) {
@@ -343,17 +354,31 @@ final class AutofillManagerServiceImpl {
}
void disableOwnedAutofillServicesLocked(int uid) {
- if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) {
+ Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
+ if (mInfo == null) return;
+
+ final ServiceInfo serviceInfo = mInfo.getServiceInfo();
+ if (serviceInfo.applicationInfo.uid != uid) {
+ Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid
+ + " instead of " + serviceInfo.applicationInfo.uid
+ + " for service " + mInfo);
return;
}
+
+
final long identity = Binder.clearCallingIdentity();
try {
final String autoFillService = getComponentNameFromSettings();
- if (mInfo.getServiceInfo().getComponentName().equals(
- ComponentName.unflattenFromString(autoFillService))) {
+ final ComponentName componentName = serviceInfo.getComponentName();
+ if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
+ mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
+ componentName.getPackageName());
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
destroySessionsLocked();
+ } else {
+ Slog.w(TAG, "disableOwnedServices(): ignored because current service ("
+ + serviceInfo + ") does not match Settings (" + autoFillService + ")");
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -377,7 +402,7 @@ final class AutofillManagerServiceImpl {
final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
sessionId, uid, activityToken, appCallbackToken, hasCallback,
- mInfo.getServiceInfo().getComponentName(), packageName);
+ mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName);
mSessions.put(newSession.id, newSession);
return newSession;
@@ -450,7 +475,7 @@ final class AutofillManagerServiceImpl {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
- if (session.isSaveUiPendingForToken(token)) {
+ if (session.isSaveUiPendingForTokenLocked(token)) {
session.onPendingSaveUi(operation, token);
return;
}
@@ -636,7 +661,7 @@ final class AutofillManagerServiceImpl {
void destroySessionsLocked() {
if (mSessions.size() == 0) {
- mUi.destroyAll(null, null);
+ mUi.destroyAll(null, null, false);
return;
}
while (mSessions.size() > 0) {
@@ -644,6 +669,18 @@ final class AutofillManagerServiceImpl {
}
}
+ // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
+ void destroyFinishedSessionsLocked() {
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ final Session session = mSessions.valueAt(i);
+ if (session.isSavingLocked()) {
+ if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
+ session.forceRemoveSelfLocked();
+ }
+ }
+ }
+
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
diff --git a/com/android/server/autofill/Helper.java b/com/android/server/autofill/Helper.java
index 086dd29f..236fbfd9 100644
--- a/com/android/server/autofill/Helper.java
+++ b/com/android/server/autofill/Helper.java
@@ -18,6 +18,7 @@ package com.android.server.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.metrics.LogMaker;
import android.os.Bundle;
import android.service.autofill.Dataset;
import android.util.ArrayMap;
@@ -25,6 +26,8 @@ import android.util.ArraySet;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -99,4 +102,14 @@ public final class Helper {
}
return fields;
}
+
+ @NonNull
+ public static LogMaker newLogMaker(int category, String packageName,
+ String servicePackageName) {
+ final LogMaker log = new LogMaker(category).setPackageName(packageName);
+ if (servicePackageName != null) {
+ log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ }
+ return log;
+ }
}
diff --git a/com/android/server/autofill/RemoteFillService.java b/com/android/server/autofill/RemoteFillService.java
index f3151485..af55807f 100644
--- a/com/android/server/autofill/RemoteFillService.java
+++ b/com/android/server/autofill/RemoteFillService.java
@@ -578,9 +578,8 @@ final class RemoteFillService implements DeathRecipient {
public void run() {
synchronized (mLock) {
if (isCancelledLocked()) {
- // TODO(b/653742740): we should probably return here, but for now we're justing
- // logging to confirm this is the problem if it happens again.
- Slog.e(LOG_TAG, "run() called after canceled: " + mRequest);
+ if (sDebug) Slog.d(LOG_TAG, "run() called after canceled: " + mRequest);
+ return;
}
}
final RemoteFillService remoteService = getService();
diff --git a/com/android/server/autofill/Session.java b/com/android/server/autofill/Session.java
index 171053fd..ed00ffed 100644
--- a/com/android/server/autofill/Session.java
+++ b/com/android/server/autofill/Session.java
@@ -50,19 +50,24 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
+import android.service.autofill.Transformation;
import android.service.autofill.ValueFinder;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -183,6 +188,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private ArrayList<String> mSelectedDatasetIds;
/**
+ * When the session started (using elapsed time since boot).
+ */
+ private final long mStartTime;
+
+ /**
+ * When the UI was shown for the first time (using elapsed time since boot).
+ */
+ @GuardedBy("mLock")
+ private long mUiShownTime;
+
+ @GuardedBy("mLock")
+ private final LocalLog mUiLatencyHistory;
+
+ /**
* Receiver of assist data from the app's {@link Activity}.
*/
private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
@@ -403,10 +422,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
@NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
@NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
- @NonNull IBinder client, boolean hasCallback,
+ @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
@NonNull ComponentName componentName, @NonNull String packageName) {
id = sessionId;
this.uid = uid;
+ mStartTime = SystemClock.elapsedRealtime();
mService = service;
mLock = lock;
mUi = ui;
@@ -414,10 +434,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mRemoteFillService = new RemoteFillService(context, componentName, userId, this);
mActivityToken = activityToken;
mHasCallback = hasCallback;
+ mUiLatencyHistory = uiLatencyHistory;
mPackageName = packageName;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
+ writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED);
}
/**
@@ -471,19 +492,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if ((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null) {
// Response is "empty" from an UI point of view, need to notify client.
- notifyUnavailableToClient();
+ notifyUnavailableToClient(false);
}
synchronized (mLock) {
processResponseLocked(response, requestFlags);
}
- final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
.setType(MetricsEvent.TYPE_SUCCESS)
- .setPackageName(mPackageName)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
- response.getDatasets() == null ? 0 : response.getDatasets().size())
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE,
- servicePackageName);
+ response.getDatasets() == null ? 0 : response.getDatasets().size());
mMetricsLogger.write(log);
}
@@ -499,10 +517,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mService.resetLastResponse();
}
- LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
- .setType(MetricsEvent.TYPE_FAILURE)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
getUiForShowing().showError(message, this);
@@ -521,11 +537,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
}
- LogMaker log = (new LogMaker(
- MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
- .setType(MetricsEvent.TYPE_SUCCESS)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_SUCCESS);
mMetricsLogger.write(log);
// Nothing left to do...
@@ -545,11 +558,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
}
- LogMaker log = (new LogMaker(
- MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
- .setType(MetricsEvent.TYPE_FAILURE)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
getUiForShowing().showError(message, this);
@@ -583,6 +593,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) {
+ if (sDebug) {
+ Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ + "; intentSender=" + intent);
+ }
final Intent fillInIntent;
synchronized (mLock) {
if (mDestroyed) {
@@ -591,6 +605,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
fillInIntent = createAuthFillInIntentLocked(requestId, extras);
+ if (fillInIntent == null) {
+ forceRemoveSelfLocked();
+ return;
+ }
}
mService.setAuthenticationSelected(id, mClientState);
@@ -746,21 +764,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
if (sDebug) Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result);
if (result instanceof FillResponse) {
- final FillResponse response = (FillResponse) result;
- mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName);
- replaceResponseLocked(authenticatedResponse, response);
+ writeLog(MetricsEvent.AUTOFILL_AUTHENTICATED);
+ replaceResponseLocked(authenticatedResponse, (FillResponse) result);
} else if (result instanceof Dataset) {
- // TODO: add proper metric
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
+ writeLog(MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
final Dataset dataset = (Dataset) result;
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
autoFill(requestId, datasetIdx, dataset, false);
+ } else {
+ writeLog(MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
}
} else {
if (result != null) {
Slog.w(TAG, "service returned invalid auth type: " + result);
}
- // TODO: add proper metric (on else)
+ writeLog(MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
processNullResponseLocked(0);
}
}
@@ -774,43 +793,57 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mHasCallback = hasIt;
}
- /**
- * Shows the save UI, when session can be saved.
- *
- * @return {@code true} if session is done, or {@code false} if it's pending user action.
- */
- public boolean showSaveLocked() {
- if (mDestroyed) {
- Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
- + id + " destroyed");
- return false;
- }
+ @Nullable
+ private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
if (mContexts == null) {
- Slog.d(TAG, "showSaveLocked(): no contexts");
- return true;
+ if (sDebug && logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
+ return null;
}
if (mResponses == null) {
// Happens when the activity / session was finished before the service replied, or
// when the service cannot autofill it (and returned a null response).
- if (sVerbose) {
- Slog.v(TAG, "showSaveLocked(): no responses on session");
+ if (sVerbose && logPrefix != null) {
+ Slog.v(TAG, logPrefix + ": no responses on session");
}
- return true;
+ return null;
}
final int lastResponseIdx = getLastResponseIndexLocked();
if (lastResponseIdx < 0) {
- Slog.w(TAG, "showSaveLocked(): did not get last response. mResponses=" + mResponses
- + ", mViewStates=" + mViewStates);
- return true;
+ if (logPrefix != null) {
+ Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
+ }
+ return null;
}
final FillResponse response = mResponses.valueAt(lastResponseIdx);
- final SaveInfo saveInfo = response.getSaveInfo();
- if (sVerbose) {
- Slog.v(TAG, "showSaveLocked(): mResponses=" + mResponses + ", mContexts=" + mContexts
+ if (sVerbose && logPrefix != null) {
+ Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts
+ ", mViewStates=" + mViewStates);
}
+ return response;
+ }
+
+ @Nullable
+ private SaveInfo getSaveInfoLocked() {
+ final FillResponse response = getLastResponseLocked(null);
+ return response == null ? null : response.getSaveInfo();
+ }
+
+ /**
+ * Shows the save UI, when session can be saved.
+ *
+ * @return {@code true} if session is done, or {@code false} if it's pending user action.
+ */
+ public boolean showSaveLocked() {
+ if (mDestroyed) {
+ Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
+ + id + " destroyed");
+ return false;
+ }
+ final FillResponse response = getLastResponseLocked("showSaveLocked()");
+ final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
/*
* The Save dialog is only shown if all conditions below are met:
@@ -825,6 +858,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return true;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
+
// Cache used to make sure changed fields do not belong to a dataset.
final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>();
final ArraySet<AutofillId> allIds = new ArraySet<>();
@@ -864,6 +899,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
break;
}
}
+ value = getSanitizedValue(sanitizers, id, value);
currentValues.put(id, value);
final AutofillValue filledValue = viewState.getAutofilledValue();
@@ -922,15 +958,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final InternalValidator validator = saveInfo.getValidator();
if (validator != null) {
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_VALIDATION);
boolean isValid;
try {
isValid = validator.isValid(valueFinder);
+ log.setType(isValid
+ ? MetricsEvent.TYPE_SUCCESS
+ : MetricsEvent.TYPE_DISMISS);
} catch (Exception e) {
- Slog.e(TAG, "Not showing save UI because of exception during validation "
- + e.getClass());
+ Slog.e(TAG, "Not showing save UI because validation failed:", e);
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ mMetricsLogger.write(log);
return true;
}
+ mMetricsLogger.write(log);
if (!isValid) {
Slog.i(TAG, "not showing save UI because fields failed validation");
return true;
@@ -978,7 +1020,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(mActivityToken, id, client);
getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
- saveInfo, valueFinder, mPackageName, this, mPendingSaveUi);
+ mService.getServicePackageName(), saveInfo, valueFinder, mPackageName, this,
+ mPendingSaveUi);
if (client != null) {
try {
client.setSaveUiState(id, true);
@@ -999,6 +1042,48 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return true;
}
+ @Nullable
+ private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+ if (saveInfo == null) return null;
+
+ final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+ if (sanitizerKeys == null) return null;
+
+ final int size = sanitizerKeys.length ;
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+ if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+ final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+ for (int i = 0; i < size; i++) {
+ final InternalSanitizer sanitizer = sanitizerKeys[i];
+ final AutofillId[] ids = sanitizerValues[i];
+ if (sDebug) {
+ Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+ + Arrays.toString(ids));
+ }
+ for (AutofillId id : ids) {
+ sanitizers.put(id, sanitizer);
+ }
+ }
+ return sanitizers;
+ }
+
+ @NonNull
+ private AutofillValue getSanitizedValue(
+ @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
+ @NonNull AutofillId id,
+ @NonNull AutofillValue value) {
+ if (sanitizers == null) return value;
+
+ final InternalSanitizer sanitizer = sanitizers.get(id);
+ if (sanitizer == null) {
+ return value;
+ }
+
+ final AutofillValue sanitized = sanitizer.sanitize(value);
+ if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+ return sanitized;
+ }
+
/**
* Returns whether the session is currently showing the save UI
*/
@@ -1062,6 +1147,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+ createSanitizers(getSaveInfoLocked());
+
final int numContexts = mContexts.size();
for (int contextNum = 0; contextNum < numContexts; contextNum++) {
@@ -1088,7 +1176,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
- node.updateAutofillValue(value);
+ final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+
+ node.updateAutofillValue(sanitizedValue);
}
// Sanitize structure before it's sent to service.
@@ -1244,6 +1334,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
break;
case ACTION_VALUE_CHANGED:
if (value != null && !value.equals(viewState.getCurrentValue())) {
+ if (value.isEmpty()
+ && viewState.getCurrentValue() != null
+ && viewState.getCurrentValue().isText()
+ && viewState.getCurrentValue().getTextValue() != null
+ && getSaveInfoLocked() != null) {
+ final int length = viewState.getCurrentValue().getTextValue().length();
+ if (sDebug) {
+ Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
+ + length + " chars long");
+ }
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+ mMetricsLogger.write(log);
+ }
+
// Always update the internal state.
viewState.setCurrentValue(value);
@@ -1319,7 +1424,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
filterText = value.getTextValue().toString();
}
- getUiForShowing().showFillUi(filledId, response, filterText, mPackageName, this);
+ getUiForShowing().showFillUi(filledId, response, filterText,
+ mService.getServicePackageName(), mPackageName, this);
+
+ synchronized (mLock) {
+ if (mUiShownTime == 0) {
+ // Log first time UI is shown.
+ mUiShownTime = SystemClock.elapsedRealtime();
+ final long duration = mUiShownTime - mStartTime;
+ if (sDebug) {
+ final StringBuilder msg = new StringBuilder("1st UI for ")
+ .append(mActivityToken)
+ .append(" shown in ");
+ TimeUtils.formatDuration(duration, msg);
+ Slog.d(TAG, msg.toString());
+ }
+ final StringBuilder historyLog = new StringBuilder("id=").append(id)
+ .append(" app=").append(mActivityToken)
+ .append(" svc=").append(mService.getServicePackageName())
+ .append(" latency=");
+ TimeUtils.formatDuration(duration, historyLog);
+ mUiLatencyHistory.log(historyLog.toString());
+
+ final LogMaker metricsLog = newLogMaker(MetricsEvent.AUTOFILL_UI_LATENCY)
+ .setCounterValue((int) duration);
+ mMetricsLogger.write(metricsLog);
+ }
+ }
}
boolean isDestroyed() {
@@ -1334,11 +1465,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private void notifyUnavailableToClient() {
+ private void notifyUnavailableToClient(boolean sessionFinished) {
synchronized (mLock) {
- if (!mHasCallback || mCurrentViewId == null) return;
+ if (mCurrentViewId == null) return;
try {
- mClient.notifyNoFillUi(id, mCurrentViewId);
+ if (mHasCallback) {
+ mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinished);
+ } else if (sessionFinished) {
+ mClient.setSessionFinished(AutofillManager.STATE_FINISHED);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
}
@@ -1426,7 +1561,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mService.resetLastResponse();
// Nothing to be done, but need to notify client.
- notifyUnavailableToClient();
+ notifyUnavailableToClient(true);
removeSelf();
}
@@ -1545,6 +1680,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent) {
+ if (sDebug) {
+ Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ + "; dataset=" + dataset);
+ }
synchronized (mLock) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#autoFill() rejected - session: "
@@ -1565,10 +1704,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
-
+ if (fillInIntent == null) {
+ forceRemoveSelfLocked();
+ return;
+ }
final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
datasetIndex);
startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent);
+
}
}
@@ -1578,14 +1721,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ // TODO: this should never be null, but we got at least one occurrence, probably due to a race.
+ @Nullable
private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
final FillContext context = getFillContextByRequestIdLocked(requestId);
if (context == null) {
- // TODO(b/653742740): this will crash system_server. We need to handle it, but we're
- // keeping it crashing for now so we can diagnose when it happens again
- Slog.wtf(TAG, "no FillContext for requestId" + requestId + "; mContexts= " + mContexts);
+ Slog.wtf(TAG, "createAuthFillInIntentLocked(): no FillContext. requestId=" + requestId
+ + "; mContexts= " + mContexts);
+ return null;
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras);
@@ -1614,6 +1759,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
+ pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime);
+ pw.print(prefix); pw.print("Time to show UI: ");
+ if (mUiShownTime == 0) {
+ pw.println("N/A");
+ } else {
+ TimeUtils.formatDuration(mUiShownTime - mStartTime, pw);
+ pw.println();
+ }
pw.print(prefix); pw.print("mResponses: ");
if (mResponses == null) {
pw.println("null");
@@ -1732,10 +1885,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mDestroyed) {
return null;
}
- mUi.destroyAll(mPendingSaveUi, this);
+ mUi.destroyAll(mPendingSaveUi, this, true);
mUi.clearCallback(this);
mDestroyed = true;
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
+ writeLog(MetricsEvent.AUTOFILL_SESSION_FINISHED);
return mRemoteFillService;
}
@@ -1746,9 +1899,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void forceRemoveSelfLocked() {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
+ final boolean isPendingSaveUi = isSaveUiPendingLocked();
mPendingSaveUi = null;
removeSelfLocked();
- mUi.destroyAll(mPendingSaveUi, this);
+ mUi.destroyAll(mPendingSaveUi, this, false);
+ if (!isPendingSaveUi) {
+ try {
+ mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client to finish session", e);
+ }
+ }
}
/**
@@ -1771,7 +1932,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ id + " destroyed");
return;
}
- if (isSaveUiPending()) {
+ if (isSaveUiPendingLocked()) {
Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui");
return;
}
@@ -1792,14 +1953,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* a specific {@code token} created by
* {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
*/
- boolean isSaveUiPendingForToken(@NonNull IBinder token) {
- return isSaveUiPending() && token.equals(mPendingSaveUi.getToken());
+ boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
+ return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
/**
* Checks whether this session is hiding the Save UI to handle a custom description link.
*/
- private boolean isSaveUiPending() {
+ private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
@@ -1820,4 +1981,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
return lastResponseIdx;
}
+
+ private LogMaker newLogMaker(int category) {
+ return newLogMaker(category, mService.getServicePackageName());
+ }
+
+ private LogMaker newLogMaker(int category, String servicePackageName) {
+ return Helper.newLogMaker(category, mPackageName, servicePackageName);
+ }
+
+ private void writeLog(int category) {
+ mMetricsLogger.write(newLogMaker(category));
+ }
}
diff --git a/com/android/server/autofill/ui/AutoFillUI.java b/com/android/server/autofill/ui/AutoFillUI.java
index cac2bff5..36b95fc0 100644
--- a/com/android/server/autofill/ui/AutoFillUI.java
+++ b/com/android/server/autofill/ui/AutoFillUI.java
@@ -40,8 +40,9 @@ import android.view.autofill.IAutofillWindowPresenter;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiThread;
+import com.android.server.autofill.Helper;
import java.io.PrintWriter;
@@ -158,21 +159,22 @@ public final class AutoFillUI {
* @param focusedId the currently focused field
* @param response the current fill response
* @param filterText text of the view to be filled
+ * @param servicePackageName package name of the autofill service filling the activity
* @param packageName package name of the activity that is filled
* @param callback Identifier for the caller
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
- @Nullable String filterText, @NonNull String packageName,
- @NonNull AutoFillUiCallback callback) {
+ @Nullable String filterText, @Nullable String servicePackageName,
+ @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
}
- final LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI))
- .setPackageName(packageName)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
+ final LogMaker log =
+ Helper.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, packageName, servicePackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
filterText == null ? 0 : filterText.length())
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
response.getDatasets() == null ? 0 : response.getDatasets().size());
mHandler.post(() -> {
@@ -184,7 +186,7 @@ public final class AutoFillUI {
filterText, mOverlayControl, new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
- log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
+ log.setType(MetricsEvent.TYPE_DETAIL);
hideFillUiUiThread(callback);
if (mCallback != null) {
mCallback.authenticate(response.getRequestId(),
@@ -195,7 +197,7 @@ public final class AutoFillUI {
@Override
public void onDatasetPicked(Dataset dataset) {
- log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ log.setType(MetricsEvent.TYPE_ACTION);
hideFillUiUiThread(callback);
if (mCallback != null) {
final int datasetIndex = response.getDatasets().indexOf(dataset);
@@ -205,14 +207,14 @@ public final class AutoFillUI {
@Override
public void onCanceled() {
- log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
+ log.setType(MetricsEvent.TYPE_DISMISS);
hideFillUiUiThread(callback);
}
@Override
public void onDestroy() {
- if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
- log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+ if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
+ log.setType(MetricsEvent.TYPE_CLOSE);
}
mMetricsLogger.write(log);
}
@@ -246,37 +248,39 @@ public final class AutoFillUI {
* Shows the UI asking the user to save for autofill.
*/
public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
- @NonNull SaveInfo info,@NonNull ValueFinder valueFinder, @NonNull String packageName,
+ @Nullable String servicePackageName, @NonNull SaveInfo info,
+ @NonNull ValueFinder valueFinder, @NonNull String packageName,
@NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi) {
if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info);
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
- LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_SAVE_UI))
- .setPackageName(packageName).addTaggedData(
- MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
+ final LogMaker log =
+ Helper.newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, packageName, servicePackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
mHandler.post(() -> {
if (callback != mCallback) {
return;
}
hideAllUiThread(callback);
- mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon, info,
- valueFinder, mOverlayControl, new SaveUi.OnSaveListener() {
+ mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon,
+ servicePackageName, packageName, info, valueFinder, mOverlayControl,
+ new SaveUi.OnSaveListener() {
@Override
public void onSave() {
- log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ log.setType(MetricsEvent.TYPE_ACTION);
hideSaveUiUiThread(mCallback);
if (mCallback != null) {
mCallback.save();
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
@Override
public void onCancel(IntentSender listener) {
- log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
+ log.setType(MetricsEvent.TYPE_DISMISS);
hideSaveUiUiThread(mCallback);
if (listener != null) {
try {
@@ -289,13 +293,13 @@ public final class AutoFillUI {
if (mCallback != null) {
mCallback.cancelSave();
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
@Override
public void onDestroy() {
- if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
- log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+ if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
+ log.setType(MetricsEvent.TYPE_CLOSE);
if (mCallback != null) {
mCallback.cancelSave();
@@ -331,8 +335,8 @@ public final class AutoFillUI {
* Destroy all UI affordances.
*/
public void destroyAll(@Nullable PendingUi pendingSaveUi,
- @Nullable AutoFillUiCallback callback) {
- mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback));
+ @Nullable AutoFillUiCallback callback, boolean notifyClient) {
+ mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback, notifyClient));
}
public void dump(PrintWriter pw) {
@@ -375,7 +379,7 @@ public final class AutoFillUI {
}
@android.annotation.UiThread
- private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi) {
+ private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) {
if (mSaveUi == null) {
// Calling destroySaveUiUiThread() twice is normal - it usually happens when the
// first call is made after the SaveUI is hidden and the second when the session is
@@ -387,7 +391,7 @@ public final class AutoFillUI {
if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): " + pendingSaveUi);
mSaveUi.destroy();
mSaveUi = null;
- if (pendingSaveUi != null) {
+ if (pendingSaveUi != null && notifyClient) {
try {
if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): notifying client");
pendingSaveUi.client.setSaveUiState(pendingSaveUi.id, false);
@@ -399,9 +403,9 @@ public final class AutoFillUI {
@android.annotation.UiThread
private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi,
- @Nullable AutoFillUiCallback callback) {
+ @Nullable AutoFillUiCallback callback, boolean notifyClient) {
hideFillUiUiThread(callback);
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, notifyClient);
}
@android.annotation.UiThread
@@ -413,7 +417,7 @@ public final class AutoFillUI {
Slog.d(TAG, "hideAllUiThread(): "
+ "destroying Save UI because pending restoration is finished");
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
}
}
diff --git a/com/android/server/autofill/ui/FillUi.java b/com/android/server/autofill/ui/FillUi.java
index 371e74d6..bf442dce 100644
--- a/com/android/server/autofill/ui/FillUi.java
+++ b/com/android/server/autofill/ui/FillUi.java
@@ -55,6 +55,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
final class FillUi {
private static final String TAG = "FillUi";
@@ -164,15 +165,18 @@ final class FillUi {
Slog.e(TAG, "Error inflating remote views", e);
continue;
}
- final AutofillValue value = dataset.getFieldValues().get(index);
+ final Pattern filter = dataset.getFilter(index);
String valueText = null;
- // If the dataset needs auth - don't add its text to allow guessing
- // its content based on how filtering behaves.
- if (value != null && value.isText() && dataset.getAuthentication() == null) {
- valueText = value.getTextValue().toString().toLowerCase();
+ if (filter == null) {
+ final AutofillValue value = dataset.getFieldValues().get(index);
+ // If the dataset needs auth - don't add its text to allow guessing
+ // its content based on how filtering behaves.
+ if (value != null && value.isText() && dataset.getAuthentication() == null) {
+ valueText = value.getTextValue().toString().toLowerCase();
+ }
}
- items.add(new ViewItem(dataset, valueText, view));
+ items.add(new ViewItem(dataset, filter, valueText, view));
}
}
@@ -331,11 +335,17 @@ final class FillUi {
private final String mValue;
private final Dataset mDataset;
private final View mView;
+ private final Pattern mFilter;
- ViewItem(Dataset dataset, String value, View view) {
+ ViewItem(Dataset dataset, Pattern filter, String value, View view) {
mDataset = dataset;
mValue = value;
mView = view;
+ mFilter = filter;
+ }
+
+ public Pattern getFilter() {
+ return mFilter;
}
public View getView() {
@@ -349,12 +359,6 @@ final class FillUi {
public String getValue() {
return mValue;
}
-
- @Override
- public String toString() {
- // Used for filtering in the adapter
- return mValue;
- }
}
private final class AutofillWindowPresenter extends IAutofillWindowPresenter.Stub {
@@ -516,10 +520,16 @@ final class FillUi {
for (int i = 0; i < itemCount; i++) {
final ViewItem item = mAllItems.get(i);
final String value = item.getValue();
- // No value, i.e. null, matches any filter
- if ((value == null && item.mDataset.getAuthentication() == null)
- || (value != null
- && value.toLowerCase().startsWith(constraintLowerCase))) {
+ final Pattern filter = item.getFilter();
+ final boolean matches;
+ if (filter != null) {
+ matches = filter.matcher(constraintLowerCase).matches();
+ } else {
+ matches = (value == null)
+ ? (item.mDataset.getAuthentication() == null)
+ : value.toLowerCase().startsWith(constraintLowerCase);
+ }
+ if (matches) {
filteredItems.add(item);
}
}
diff --git a/com/android/server/autofill/ui/SaveUi.java b/com/android/server/autofill/ui/SaveUi.java
index d0b2e924..d48f23ca 100644
--- a/com/android/server/autofill/ui/SaveUi.java
+++ b/com/android/server/autofill/ui/SaveUi.java
@@ -20,16 +20,15 @@ import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -53,6 +52,8 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiThread;
import java.io.PrintWriter;
@@ -111,6 +112,7 @@ final class SaveUi {
}
private final Handler mHandler = UiThread.getHandler();
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
private final @NonNull Dialog mDialog;
@@ -121,16 +123,21 @@ final class SaveUi {
private final CharSequence mTitle;
private final CharSequence mSubTitle;
private final PendingUi mPendingUi;
+ private final String mServicePackageName;
+ private final String mPackageName;
private boolean mDestroyed;
SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
+ @Nullable String servicePackageName, @NonNull String packageName,
@NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
@NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) {
mPendingUi= pendingUi;
mListener = new OneTimeListener(listener);
mOverlayControl = overlayControl;
+ mServicePackageName = servicePackageName;
+ mPackageName = packageName;
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -181,6 +188,8 @@ final class SaveUi {
ScrollView subtitleContainer = null;
final CustomDescription customDescription = info.getCustomDescription();
if (customDescription != null) {
+ writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION, type);
+
mSubTitle = null;
if (sDebug) Slog.d(TAG, "Using custom description");
@@ -190,40 +199,35 @@ final class SaveUi {
@Override
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent intent) {
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, type);
// We need to hide the Save UI before launching the pending intent, and
// restore back it once the activity is finished, and that's achieved by
// adding a custom extra in the activity intent.
- if (pendingIntent != null) {
- if (intent == null) {
- Slog.w(TAG,
- "remote view on custom description does not have intent");
- return false;
- }
- if (!pendingIntent.isActivity()) {
- Slog.w(TAG, "ignoring custom description pending intent that's not "
- + "for an activity: " + pendingIntent);
- return false;
- }
- if (sVerbose) {
- Slog.v(TAG,
- "Intercepting custom description intent: " + intent);
- }
- final IBinder token = mPendingUi.getToken();
- intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
- try {
- pendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
- intent);
- mPendingUi.setState(PendingUi.STATE_PENDING);
- if (sDebug) {
- Slog.d(TAG, "hiding UI until restored with token " + token);
- }
- hide();
- } catch (RemoteException e) {
- Slog.w(TAG, "error triggering pending intent: " + intent);
- return false;
- }
+ final boolean isValid = isValidLink(pendingIntent, intent);
+ if (!isValid) {
+ log.setType(MetricsEvent.TYPE_UNKNOWN);
+ mMetricsLogger.write(log);
+ return false;
+ }
+ if (sVerbose) Slog.v(TAG, "Intercepting custom description intent");
+ final IBinder token = mPendingUi.getToken();
+ intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
+ try {
+ pendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
+ intent);
+ mPendingUi.setState(PendingUi.STATE_PENDING);
+ if (sDebug) Slog.d(TAG, "hiding UI until restored with token " + token);
+ hide();
+ log.setType(MetricsEvent.TYPE_OPEN);
+ mMetricsLogger.write(log);
+ return true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "error triggering pending intent: " + intent);
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ mMetricsLogger.write(log);
+ return false;
}
- return true;
}
};
@@ -241,6 +245,7 @@ final class SaveUi {
} else {
mSubTitle = info.getDescription();
if (mSubTitle != null) {
+ writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_SUBTITLE, type);
subtitleContainer = view.findViewById(R.id.autofill_save_custom_subtitle);
final TextView subtitleView = new TextView(context);
subtitleView.setText(mSubTitle);
@@ -258,9 +263,7 @@ final class SaveUi {
} else {
noButton.setText(R.string.autofill_save_no);
}
- final View.OnClickListener cancelListener =
- (v) -> mListener.onCancel(info.getNegativeActionListener());
- noButton.setOnClickListener(cancelListener);
+ noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener()));
final View yesButton = view.findViewById(R.id.autofill_save_yes);
yesButton.setOnClickListener((v) -> mListener.onSave());
@@ -268,8 +271,9 @@ final class SaveUi {
mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
mDialog.setContentView(view);
- // Dialog can be dismissed when touched outside.
- mDialog.setOnDismissListener((d) -> mListener.onCancel(info.getNegativeActionListener()));
+ // Dialog can be dismissed when touched outside, but the negative listener should not be
+ // notified (hence the null argument).
+ mDialog.setOnDismissListener((d) -> mListener.onCancel(null));
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -300,7 +304,7 @@ final class SaveUi {
if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
if (sDebug) {
- Slog.d(TAG, "Addingservice icon "
+ Slog.d(TAG, "Adding service icon "
+ "(" + actualWidth + "x" + actualHeight + ") as it's less than maximum "
+ "(" + maxWidth + "x" + maxHeight + ").");
}
@@ -309,8 +313,39 @@ final class SaveUi {
Slog.w(TAG, "Not adding service icon of size "
+ "(" + actualWidth + "x" + actualHeight + ") because maximum is "
+ "(" + maxWidth + "x" + maxHeight + ").");
- iconView.setVisibility(View.INVISIBLE);
+ ((ViewGroup)iconView.getParent()).removeView(iconView);
+ }
+ }
+
+ private static boolean isValidLink(PendingIntent pendingIntent, Intent intent) {
+ if (pendingIntent == null) {
+ Slog.w(TAG, "isValidLink(): custom description without pending intent");
+ return false;
}
+ if (!pendingIntent.isActivity()) {
+ Slog.w(TAG, "isValidLink(): pending intent not for activity");
+ return false;
+ }
+ if (intent == null) {
+ Slog.w(TAG, "isValidLink(): no intent");
+ return false;
+ }
+ return true;
+ }
+
+ private LogMaker newLogMaker(int category, int saveType) {
+ return newLogMaker(category)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SAVE_TYPE, saveType);
+ }
+
+ private LogMaker newLogMaker(int category) {
+ return new LogMaker(category)
+ .setPackageName(mPackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, mServicePackageName);
+ }
+
+ private void writeLog(int category, int saveType) {
+ mMetricsLogger.write(newLogMaker(category, saveType));
}
/**
@@ -326,17 +361,25 @@ final class SaveUi {
+ mPendingUi.getToken());
return;
}
- switch (operation) {
- case AutofillManager.PENDING_UI_OPERATION_RESTORE:
- if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token);
- show();
- break;
- case AutofillManager.PENDING_UI_OPERATION_CANCEL:
- if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token);
- hide();
- break;
- default:
- Slog.w(TAG, "restore(): invalid operation " + operation);
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_PENDING_SAVE_UI_OPERATION);
+ try {
+ switch (operation) {
+ case AutofillManager.PENDING_UI_OPERATION_RESTORE:
+ if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token);
+ log.setType(MetricsEvent.TYPE_OPEN);
+ show();
+ break;
+ case AutofillManager.PENDING_UI_OPERATION_CANCEL:
+ log.setType(MetricsEvent.TYPE_DISMISS);
+ if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token);
+ hide();
+ break;
+ default:
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ Slog.w(TAG, "restore(): invalid operation " + operation);
+ }
+ } finally {
+ mMetricsLogger.write(log);
}
mPendingUi.setState(PendingUi.STATE_FINISHED);
}
@@ -385,6 +428,8 @@ final class SaveUi {
pw.print(prefix); pw.print("title: "); pw.println(mTitle);
pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle);
pw.print(prefix); pw.print("pendingUi: "); pw.println(mPendingUi);
+ pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
+ pw.print(prefix); pw.print("app: "); pw.println(mPackageName);
final View view = mDialog.getWindow().getDecorView();
final int[] loc = view.getLocationOnScreen();
diff --git a/com/android/server/backup/BackupManagerConstants.java b/com/android/server/backup/BackupManagerConstants.java
index cd601826..245241cb 100644
--- a/com/android/server/backup/BackupManagerConstants.java
+++ b/com/android/server/backup/BackupManagerConstants.java
@@ -123,7 +123,7 @@ class BackupManagerConstants extends ContentObserver {
// group the calls of these methods in a block syncrhonized on
// a reference of this object.
public synchronized long getKeyValueBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupIntervalMilliseconds(...) returns "
+ mKeyValueBackupIntervalMilliseconds);
}
@@ -131,7 +131,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized long getKeyValueBackupFuzzMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupFuzzMilliseconds(...) returns "
+ mKeyValueBackupFuzzMilliseconds);
}
@@ -139,7 +139,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized boolean getKeyValueBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupRequireCharging(...) returns "
+ mKeyValueBackupRequireCharging);
}
@@ -147,7 +147,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized int getKeyValueBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupRequiredNetworkType(...) returns "
+ mKeyValueBackupRequiredNetworkType);
}
@@ -155,7 +155,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized long getFullBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupIntervalMilliseconds(...) returns "
+ mFullBackupIntervalMilliseconds);
}
@@ -163,7 +163,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized boolean getFullBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
}
return mFullBackupRequireCharging;
@@ -171,7 +171,7 @@ class BackupManagerConstants extends ContentObserver {
}
public synchronized int getFullBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequiredNetworkType(...) returns "
+ mFullBackupRequiredNetworkType);
}
diff --git a/com/android/server/backup/BackupManagerService.java b/com/android/server/backup/BackupManagerService.java
index f5257978..eabe21fe 100644
--- a/com/android/server/backup/BackupManagerService.java
+++ b/com/android/server/backup/BackupManagerService.java
@@ -192,6 +192,10 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
+/**
+ * @Deprecated Use RefactoredBackupManagerService instead. This class is only
+ * kept for fallback and archeology reasons and will be removed soon.
+ */
public class BackupManagerService implements BackupManagerServiceInterface {
private static final String TAG = "BackupManagerService";
@@ -397,43 +401,51 @@ public class BackupManagerService implements BackupManagerServiceInterface {
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
- sInstance.initialize(userId);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ sInstance.unlockSystemUser();
+ }
+ }
+ }
+
+ // 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() {
+ 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(userId)) {
+ // 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) {
if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
}
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, userId);
- if (enableState >= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
- }
- writeBackupEnableState(enableState != 0, userId);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, userId);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
- }
+ 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(userId));
- } 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 {
@@ -1983,7 +1995,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
if (uri == null) {
return;
}
- String pkgName = uri.getSchemeSpecificPart();
+ final String pkgName = uri.getSchemeSpecificPart();
if (pkgName != null) {
pkgList = new String[] { pkgName };
}
@@ -1991,7 +2003,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
// At package-changed we only care about looking at new transport states
if (changed) {
- String[] components =
+ final String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
@@ -2001,7 +2013,8 @@ public class BackupManagerService implements BackupManagerServiceInterface {
}
}
- mTransportManager.onPackageChanged(pkgName, components);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
return; // nothing more to do in the PACKAGE_CHANGED case
}
@@ -2033,7 +2046,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
}
// If they're full-backup candidates, add them there instead
final long now = System.currentTimeMillis();
- for (String packageName : pkgList) {
+ for (final String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
if (appGetsFullBackup(app)
@@ -2050,7 +2063,8 @@ public class BackupManagerService implements BackupManagerServiceInterface {
writeFullBackupScheduleAsync();
}
- mTransportManager.onPackageAdded(packageName);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
// doesn't really exist; ignore it
@@ -2074,8 +2088,9 @@ public class BackupManagerService implements BackupManagerServiceInterface {
removePackageParticipantsLocked(pkgList, uid);
}
}
- for (String pkgName : pkgList) {
- mTransportManager.onPackageRemoved(pkgName);
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
}
}
}
diff --git a/com/android/server/backup/BackupManagerServiceInterface.java b/com/android/server/backup/BackupManagerServiceInterface.java
index 5dfa6302..041f9ed5 100644
--- a/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/com/android/server/backup/BackupManagerServiceInterface.java
@@ -39,6 +39,8 @@ import java.io.PrintWriter;
*/
public interface BackupManagerServiceInterface {
+ void unlockSystemUser();
+
// Utility: build a new random integer token
int generateRandomIntegerToken();
diff --git a/com/android/server/backup/FullBackupJob.java b/com/android/server/backup/FullBackupJob.java
index 82638b4e..b81a54d3 100644
--- a/com/android/server/backup/FullBackupJob.java
+++ b/com/android/server/backup/FullBackupJob.java
@@ -61,7 +61,7 @@ public class FullBackupJob extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
mParams = params;
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
return service.beginFullBackup(this);
}
@@ -69,7 +69,7 @@ public class FullBackupJob extends JobService {
public boolean onStopJob(JobParameters params) {
if (mParams != null) {
mParams = null;
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
service.endFullBackup();
}
return false;
diff --git a/com/android/server/backup/KeyValueAdbBackupEngine.java b/com/android/server/backup/KeyValueAdbBackupEngine.java
index 279c8284..b38b25a3 100644
--- a/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@ import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
+import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -19,6 +19,8 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.util.Slog;
+import com.android.server.backup.utils.FullBackupUtils;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -78,7 +80,7 @@ public class KeyValueAdbBackupEngine {
mNewStateName = new File(mStateDir,
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
- mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mManifestFile = new File(mDataDir, RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME);
}
public void backupOnePackage() throws IOException {
@@ -188,7 +190,7 @@ public class KeyValueAdbBackupEngine {
if (DEBUG) {
Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
}
- BackupManagerService.writeAppManifest(
+ FullBackupUtils.writeAppManifest(
mPackage, mPackageManager, mManifestFile, false, false);
FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
mDataDir.getAbsolutePath(),
@@ -251,7 +253,7 @@ public class KeyValueAdbBackupEngine {
t.start();
// Now pull data from the app and stuff it into the output
- BackupManagerService.routeSocketDataToOutput(pipes[0], mOutput);
+ FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
if (!mBackupManagerService.waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName);
diff --git a/com/android/server/backup/KeyValueAdbRestoreEngine.java b/com/android/server/backup/KeyValueAdbRestoreEngine.java
index b62bb5c9..a2de8e73 100644
--- a/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,6 +13,8 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.backup.restore.PerformAdbRestoreTask;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -41,7 +43,7 @@ public class KeyValueAdbRestoreEngine implements Runnable {
private final File mDataDir;
FileMetadata mInfo;
- BackupManagerService.PerformAdbRestoreTask mRestoreTask;
+ PerformAdbRestoreTask mRestoreTask;
ParcelFileDescriptor mInFD;
IBackupAgent mAgent;
int mToken;
diff --git a/com/android/server/backup/KeyValueBackupJob.java b/com/android/server/backup/KeyValueBackupJob.java
index d8411e2a..5dfb0bce 100644
--- a/com/android/server/backup/KeyValueBackupJob.java
+++ b/com/android/server/backup/KeyValueBackupJob.java
@@ -71,7 +71,7 @@ public class KeyValueBackupJob extends JobService {
if (delay <= 0) {
delay = interval + new Random().nextInt((int) fuzz);
}
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
@@ -110,7 +110,7 @@ public class KeyValueBackupJob extends JobService {
}
// Time to run a key/value backup!
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
try {
service.backupNow();
} catch (RemoteException e) {}
diff --git a/com/android/server/backup/PackageManagerBackupAgent.java b/com/android/server/backup/PackageManagerBackupAgent.java
index 8d91e0dd..f658f22d 100644
--- a/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/com/android/server/backup/PackageManagerBackupAgent.java
@@ -30,6 +30,8 @@ import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
+import com.android.server.backup.utils.AppBackupUtils;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -140,7 +142,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
pkgs.remove(a);
}
}
diff --git a/com/android/server/backup/ProcessedPackagesJournal.java b/com/android/server/backup/ProcessedPackagesJournal.java
index 187d5d93..e29b7d58 100644
--- a/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/com/android/server/backup/ProcessedPackagesJournal.java
@@ -21,7 +21,10 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.backup.RefactoredBackupManagerService;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
import java.io.EOFException;
+import java.io.FileInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@@ -130,7 +133,8 @@ final class ProcessedPackagesJournal {
return;
}
- try (RandomAccessFile oldJournal = new RandomAccessFile(journalFile, "r")) {
+ try (DataInputStream oldJournal = new DataInputStream(
+ new BufferedInputStream(new FileInputStream(journalFile)))) {
while (true) {
String packageName = oldJournal.readUTF();
if (DEBUG) {
diff --git a/com/android/server/backup/RefactoredBackupManagerService.java b/com/android/server/backup/RefactoredBackupManagerService.java
index 141f9207..f2980659 100644
--- a/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/com/android/server/backup/RefactoredBackupManagerService.java
@@ -77,6 +77,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
@@ -149,6 +150,7 @@ import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
public class RefactoredBackupManagerService implements BackupManagerServiceInterface {
@@ -546,37 +548,51 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- sInstance.initialize(userId);
+ sInstance.unlockSystemUser();
+ }
+ }
+ }
+
+ // 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() {
+ 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
- if (!backupSettingMigrated(userId)) {
+ // 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) {
if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
}
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, userId);
- if (enableState >= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
- }
- writeBackupEnableState(enableState != 0, userId);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, userId);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
- }
+ 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);
- try {
- sInstance.setBackupEnabled(readBackupEnableState(userId));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
+ 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
@@ -619,6 +635,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
private final Object mCurrentOpLock = new Object();
private final Random mTokenGenerator = new Random();
+ final AtomicInteger mNextToken = new AtomicInteger();
private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
@@ -644,7 +661,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// Persistently track the need to do a full init
private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
- private ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
// Round-robin queue for scheduling full backup passes
private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
@@ -658,18 +675,41 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
@GuardedBy("mQueueLock")
private ArrayList<FullBackupEntry> mFullBackupQueue;
- // Utility: build a new random integer token
+ // Utility: build a new random integer token. The low bits are the ordinal of the
+ // operation for near-time uniqueness, and the upper bits are random for app-
+ // side unpredictability.
@Override
public int generateRandomIntegerToken() {
- int token;
- do {
- synchronized (mTokenGenerator) {
- token = mTokenGenerator.nextInt();
- }
- } while (token < 0);
+ int token = mTokenGenerator.nextInt();
+ if (token < 0) token = -token;
+ token &= ~0xFF;
+ token |= (mNextToken.incrementAndGet() & 0xFF);
return token;
}
+ /*
+ * Construct a backup agent instance for the metadata pseudopackage. This is a
+ * process-local non-lifecycle agent instance, so we manually set up the context
+ * topology for it.
+ */
+ public PackageManagerBackupAgent makeMetadataAgent() {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ /*
+ * Same as above but with the explicit package-set configuration.
+ */
+ public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+ PackageManagerBackupAgent pmAgent =
+ new PackageManagerBackupAgent(mPackageManager, packages);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
// ----- Debug-only backup operation trace -----
public void addBackupTrace(String s) {
if (DEBUG_BACKUP_TRACE) {
@@ -800,17 +840,18 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// Remember our ancestral dataset
mTokenFile = new File(mBaseStateDir, "ancestral");
- try (RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r")) {
- int version = tf.readInt();
+ try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
+ new FileInputStream(mTokenFile)))) {
+ int version = tokenStream.readInt();
if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
- mAncestralToken = tf.readLong();
- mCurrentToken = tf.readLong();
+ mAncestralToken = tokenStream.readLong();
+ mCurrentToken = tokenStream.readLong();
- int numPackages = tf.readInt();
+ int numPackages = tokenStream.readInt();
if (numPackages >= 0) {
mAncestralPackages = new HashSet<>();
for (int i = 0; i < numPackages; i++) {
- String pkgName = tf.readUTF();
+ String pkgName = tokenStream.readUTF();
mAncestralPackages.add(pkgName);
}
}
@@ -878,7 +919,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
if (AppBackupUtils.appGetsFullBackup(pkg)
&& AppBackupUtils.appIsEligibleForBackup(
- pkg.applicationInfo)) {
+ pkg.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -899,7 +940,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
for (PackageInfo app : apps) {
if (AppBackupUtils.appGetsFullBackup(app)
&& AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo)) {
+ app.applicationInfo, mPackageManager)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -925,7 +966,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
schedule = new ArrayList<>(apps.size());
for (PackageInfo info : apps) {
if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo)) {
+ info.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -1155,7 +1196,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
if (uri == null) {
return;
}
- String pkgName = uri.getSchemeSpecificPart();
+ final String pkgName = uri.getSchemeSpecificPart();
if (pkgName != null) {
pkgList = new String[]{pkgName};
}
@@ -1163,7 +1204,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// At package-changed we only care about looking at new transport states
if (changed) {
- String[] components =
+ final String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
@@ -1173,7 +1214,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
}
- mTransportManager.onPackageChanged(pkgName, components);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
return; // nothing more to do in the PACKAGE_CHANGED case
}
@@ -1205,12 +1247,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
// If they're full-backup candidates, add them there instead
final long now = System.currentTimeMillis();
- for (String packageName : pkgList) {
+ for (final String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
if (AppBackupUtils.appGetsFullBackup(app)
&& AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo)) {
+ app.applicationInfo, mPackageManager)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -1223,7 +1265,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
writeFullBackupScheduleAsync();
}
- mTransportManager.onPackageAdded(packageName);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
// doesn't really exist; ignore it
@@ -1247,8 +1290,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
removePackageParticipantsLocked(pkgList, uid);
}
}
- for (String pkgName : pkgList) {
- mTransportManager.onPackageRemoved(pkgName);
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
}
}
}
@@ -1507,7 +1551,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
long token = mAncestralToken;
synchronized (mQueueLock) {
- if (mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
+ if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "App in ever-stored, so using current token");
}
@@ -1568,7 +1612,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+ mPackageManager)) {
BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
@@ -1759,8 +1804,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
// called after we receive cancel here. We need this op's state there.
- // Remove all pending timeout messages for this operation type.
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
+ // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+ // doesn't require cancellation.
+ if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ }
}
mCurrentOpLock.notifyAll();
}
@@ -2108,14 +2157,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
// so tear down any ongoing backup task right away.
@Override
public void endFullBackup() {
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Telling running backup to stop");
+ // offload the mRunningFullBackupTask.handleCancel() call to another thread,
+ // as we might have to wait for mCancelLock
+ Runnable endFullBackupRunnable = new Runnable() {
+ @Override
+ public void run() {
+ PerformFullTransportBackupTask pftbt = null;
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ pftbt = mRunningFullBackupTask;
+ }
+ }
+ if (pftbt != null) {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Telling running backup to stop");
+ }
+ pftbt.handleCancel(true);
}
- mRunningFullBackupTask.handleCancel(true);
}
- }
+ };
+ new Thread(endFullBackupRunnable, "end-full-backup").start();
}
// Used by both incremental and full restore
@@ -2800,8 +2861,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
final long oldId = Binder.clearCallingIdentity();
try {
String prevTransport = mTransportManager.selectTransport(transport);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, transport);
+ updateStateForTransport(transport);
Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
+ " returning " + prevTransport);
return prevTransport;
@@ -2826,9 +2886,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
@Override
public void onSuccess(String transportName) {
mTransportManager.selectTransport(transportName);
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT,
- mTransportManager.getCurrentTransportName());
+ updateStateForTransport(mTransportManager.getCurrentTransportName());
Slog.v(TAG, "Transport successfully selected: "
+ transport.flattenToShortString());
try {
@@ -2853,6 +2911,28 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
Binder.restoreCallingIdentity(oldId);
}
+ private void updateStateForTransport(String newTransportName) {
+ // Publish the name change
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+ // And update our current-dataset bookkeeping
+ IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName);
+ if (transport != null) {
+ try {
+ mCurrentToken = transport.getCurrentRestoreSet();
+ } catch (Exception e) {
+ // Oops. We can't know the current dataset token, so reset and figure it out
+ // when we do the next k/v backup operation on this transport.
+ mCurrentToken = 0;
+ }
+ } else {
+ // The named transport isn't bound at this particular moment, so we can't
+ // know yet what its current dataset token is. Reset as above.
+ mCurrentToken = 0;
+ }
+ }
+
// Supply the configuration Intent for the given transport. If the name is not one
// of the available transports, or if the transport does not supply any configuration
// UI, the method returns null.
@@ -3162,19 +3242,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
}
}
- // We also avoid backups of 'disabled' apps
- private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
- switch (pm.getApplicationEnabledSetting(app.packageName)) {
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
- return true;
-
- default:
- return false;
- }
- }
-
@Override
public boolean isAppEligibleForBackup(String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -3182,9 +3249,10 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo) ||
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+ mPackageManager) ||
AppBackupUtils.appIsStopped(packageInfo.applicationInfo) ||
- appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
+ AppBackupUtils.appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
return false;
}
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
diff --git a/com/android/server/backup/Trampoline.java b/com/android/server/backup/Trampoline.java
index fcd929a7..9739e380 100644
--- a/com/android/server/backup/Trampoline.java
+++ b/com/android/server/backup/Trampoline.java
@@ -98,7 +98,7 @@ public class Trampoline extends IBackupManager.Stub {
protected boolean isRefactoredServiceEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 1) == 0;
+ Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 0) == 0;
}
protected int binderGetCallingUid() {
@@ -139,6 +139,13 @@ public class Trampoline extends IBackupManager.Stub {
}
}
+ void unlockSystemUser() {
+ BackupManagerServiceInterface svc = mService;
+ if (svc != null) {
+ svc.unlockSystemUser();
+ }
+ }
+
public void setBackupServiceActive(final int userHandle, boolean makeActive) {
// Only the DPM should be changing the active state of backup
final int caller = binderGetCallingUid();
diff --git a/com/android/server/backup/TransportManager.java b/com/android/server/backup/TransportManager.java
index 9aae3841..7a0173f6 100644
--- a/com/android/server/backup/TransportManager.java
+++ b/com/android/server/backup/TransportManager.java
@@ -341,9 +341,9 @@ public class TransportManager {
private class TransportConnection implements ServiceConnection {
// Hold mTransportsLock to access these fields so as to provide a consistent view of them.
- private IBackupTransport mBinder;
+ private volatile IBackupTransport mBinder;
private final List<TransportReadyCallback> mListeners = new ArrayList<>();
- private String mTransportName;
+ private volatile String mTransportName;
private final ComponentName mTransportComponent;
@@ -426,25 +426,24 @@ public class TransportManager {
+ rebindTimeout + "ms");
}
+ // Intentionally not synchronized -- the variable is volatile and changes to its value
+ // are inside synchronized blocks, providing a memory sync barrier; and this method
+ // does not touch any other state protected by that lock.
private IBackupTransport getBinder() {
- synchronized (mTransportLock) {
- return mBinder;
- }
+ return mBinder;
}
+ // Intentionally not synchronized; same as getBinder()
private String getName() {
- synchronized (mTransportLock) {
- return mTransportName;
- }
+ return mTransportName;
}
+ // Intentionally not synchronized; same as getBinder()
private void bindIfUnbound() {
- synchronized (mTransportLock) {
- if (mBinder == null) {
- Slog.d(TAG,
- "Rebinding to transport " + mTransportComponent.flattenToShortString());
- bindToTransport(mTransportComponent, this);
- }
+ if (mBinder == null) {
+ Slog.d(TAG,
+ "Rebinding to transport " + mTransportComponent.flattenToShortString());
+ bindToTransport(mTransportComponent, this);
}
}
diff --git a/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 4085f63a..f0b3e4a0 100644
--- a/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -236,12 +236,11 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
obbConnection.establish(); // we'll want this later
sendStartBackup();
+ PackageManager pm = backupManagerService.getPackageManager();
// doAllApps supersedes the package set if any
if (mAllApps) {
- List<PackageInfo> allPackages =
- backupManagerService.getPackageManager().getInstalledPackages(
- PackageManager.GET_SIGNATURES);
+ List<PackageInfo> allPackages = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
for (int i = 0; i < allPackages.size(); i++) {
PackageInfo pkg = allPackages.get(i);
// Exclude system apps if we've been asked to do so
@@ -288,7 +287,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo)
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)
|| AppBackupUtils.appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
diff --git a/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index bc7c1174..90134e1a 100644
--- a/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -140,10 +140,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
for (String pkg : whichPackages) {
try {
- PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(pkg,
- PackageManager.GET_SIGNATURES);
+ PackageManager pm = backupManagerService.getPackageManager();
+ PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
mCurrentPackage = info;
- if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -306,6 +306,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
final int N = mPackages.size();
final byte[] buffer = new byte[8192];
for (int i = 0; i < N; i++) {
+ mBackupRunner = null;
PackageInfo currentPackage = mPackages.get(i);
String packageName = currentPackage.packageName;
if (DEBUG) {
@@ -491,7 +492,13 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
"transport rejected");
- // Do nothing, clean up, and continue looping.
+ // This failure state can come either a-priori from the transport, or
+ // from the preflight pass. If we got as far as preflight, we now need
+ // to tear down the target process.
+ if (mBackupRunner != null) {
+ backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ }
+ // ... and continue looping.
} else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
BackupObserverUtils
.sendBackupOnPackageResult(mBackupObserver, packageName,
@@ -501,6 +508,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
packageName);
}
+ backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
BackupObserverUtils
@@ -527,6 +535,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
// Abort entire backup pass.
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
+ backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
return;
} else {
// Success!
diff --git a/com/android/server/backup/internal/PerformBackupTask.java b/com/android/server/backup/internal/PerformBackupTask.java
index ce4f906e..7a8a920e 100644
--- a/com/android/server/backup/internal/PerformBackupTask.java
+++ b/com/android/server/backup/internal/PerformBackupTask.java
@@ -227,9 +227,8 @@ public class PerformBackupTask implements BackupRestoreTask {
if (!mFinished) {
finalizeBackup();
} else {
- Slog.e(TAG, "Duplicate finish");
+ Slog.e(TAG, "Duplicate finish of K/V pass");
}
- mFinished = true;
break;
}
}
@@ -322,8 +321,7 @@ public class PerformBackupTask implements BackupRestoreTask {
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
if (mStatus == BackupTransport.TRANSPORT_OK) {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- backupManagerService.getPackageManager());
+ PackageManagerBackupAgent pmAgent = backupManagerService.makeMetadataAgent();
mStatus = invokeAgentForBackup(
PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
@@ -391,11 +389,9 @@ public class PerformBackupTask implements BackupRestoreTask {
// to sanity-check here. This also gives us the classname of the
// package's backup agent.
try {
- mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
- request.packageName,
- PackageManager.GET_SIGNATURES);
- if (!AppBackupUtils.appIsEligibleForBackup(
- mCurrentPackage.applicationInfo)) {
+ PackageManager pm = backupManagerService.getPackageManager();
+ mCurrentPackage = pm.getPackageInfo(request.packageName, PackageManager.GET_SIGNATURES);
+ if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) {
// The manifest has changed but we had a stale backup request pending.
// This won't happen again because the app won't be requesting further
// backups.
@@ -609,6 +605,7 @@ public class PerformBackupTask implements BackupRestoreTask {
break;
}
}
+ mFinished = true;
Slog.i(TAG, "K/V backup pass finished.");
// Only once we're entirely finished do we release the wakelock for k/v backup.
backupManagerService.getWakelock().release();
diff --git a/com/android/server/backup/internal/RunInitializeReceiver.java b/com/android/server/backup/internal/RunInitializeReceiver.java
index a6897d0e..1df0bf0c 100644
--- a/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -23,6 +23,7 @@ import static com.android.server.backup.RefactoredBackupManagerService.TAG;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.server.backup.RefactoredBackupManagerService;
@@ -38,19 +39,22 @@ public class RunInitializeReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
synchronized (backupManagerService.getQueueLock()) {
+ final ArraySet<String> pendingInits = backupManagerService.getPendingInits();
if (DEBUG) {
- Slog.v(TAG, "Running a device init");
+ Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
}
- String[] pendingInits = (String[]) backupManagerService.getPendingInits().toArray();
- backupManagerService.clearPendingInits();
- PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
- pendingInits, null);
+ if (pendingInits.size() > 0) {
+ final String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
+ PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
+ transports, null);
- // Acquire the wakelock and pass it to the init thread. it will
- // be released once init concludes.
- backupManagerService.getWakelock().acquire();
- backupManagerService.getBackupHandler().post(initTask);
+ // Acquire the wakelock and pass it to the init thread. it will
+ // be released once init concludes.
+ backupManagerService.clearPendingInits();
+ backupManagerService.getWakelock().acquire();
+ backupManagerService.getBackupHandler().post(initTask);
+ }
}
}
}
diff --git a/com/android/server/backup/restore/PerformAdbRestoreTask.java b/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 62ae065b..22691bb6 100644
--- a/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -150,8 +150,7 @@ public class PerformAdbRestoreTask implements Runnable {
mObserver = observer;
mLatchObject = latch;
mAgent = null;
- mPackageManagerBackupAgent = new PackageManagerBackupAgent(
- backupManagerService.getPackageManager());
+ mPackageManagerBackupAgent = backupManagerService.makeMetadataAgent();
mAgentPackage = null;
mTargetApp = null;
mObbConnection = new FullBackupObbConnection(backupManagerService);
diff --git a/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 21d5dc21..b538c6d4 100644
--- a/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -198,8 +198,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
boolean hasSettings = false;
for (int i = 0; i < filterSet.length; i++) {
try {
- PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(
- filterSet[i], 0);
+ PackageManager pm = backupManagerService.getPackageManager();
+ PackageInfo info = pm.getPackageInfo(filterSet[i], 0);
if ("android".equals(info.packageName)) {
hasSystem = true;
continue;
@@ -209,8 +209,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
continue;
}
- if (AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo)) {
+ if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
@@ -387,8 +386,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// Pull the Package Manager metadata from the restore set first
mCurrentPackage = new PackageInfo();
mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
- mPmAgent = new PackageManagerBackupAgent(backupManagerService.getPackageManager(),
- null);
+ mPmAgent = backupManagerService.makeMetadataAgent(null);
mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
if (MORE_DEBUG) {
Slog.v(TAG, "initiating restore for PMBA");
@@ -779,6 +777,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
private void restoreFinished() {
+ if (DEBUG) {
+ Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
+ }
try {
backupManagerService
.prepareOperationTimeout(mEphemeralOpToken,
diff --git a/com/android/server/backup/utils/AppBackupUtils.java b/com/android/server/backup/utils/AppBackupUtils.java
index 4abf18ad..d7cac777 100644
--- a/com/android/server/backup/utils/AppBackupUtils.java
+++ b/com/android/server/backup/utils/AppBackupUtils.java
@@ -22,6 +22,7 @@ import static com.android.server.backup.RefactoredBackupManagerService.TAG;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Process;
import android.util.Slog;
@@ -44,7 +45,7 @@ public class AppBackupUtils {
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*/
- public static boolean appIsEligibleForBackup(ApplicationInfo app) {
+ public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
@@ -60,11 +61,33 @@ public class AppBackupUtils {
return false;
}
- return true;
+ // 4. it is an "instant" app
+ if (app.isInstantApp()) {
+ return false;
+ }
+
+ // Everything else checks out; the only remaining roadblock would be if the
+ // package were disabled
+ return !appIsDisabled(app, pm);
+ }
+
+ /** Avoid backups of 'disabled' apps. */
+ public static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
+ switch (pm.getApplicationEnabledSetting(app.packageName)) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ return true;
+
+ default:
+ return false;
+ }
}
/**
- * Checks if the app is in a stopped state, that means it won't receive broadcasts.
+ * Checks if the app is in a stopped state. This is not part of the general "eligible for
+ * backup?" check because we *do* still need to restore data to apps in this state (e.g.
+ * newly-installing ones)
*/
public static boolean appIsStopped(ApplicationInfo app) {
return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
diff --git a/com/android/server/connectivity/IpConnectivityEventBuilder.java b/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 22330e66..67e72167 100644
--- a/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -126,7 +126,7 @@ final public class IpConnectivityEventBuilder {
wakeupStats.systemWakeups = in.systemWakeups;
wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
wakeupStats.applicationWakeups = in.applicationWakeups;
- wakeupStats.unroutedWakeups = in.unroutedWakeups;
+ wakeupStats.noUidWakeups = in.noUidWakeups;
final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
out.setWakeupStats(wakeupStats);
return out;
diff --git a/com/android/server/connectivity/IpConnectivityMetrics.java b/com/android/server/connectivity/IpConnectivityMetrics.java
index 475d786a..f2445fa3 100644
--- a/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -34,6 +34,7 @@ import android.util.Base64;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.SystemService;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
@@ -44,7 +45,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.ToIntFunction;
-/** {@hide} */
+/**
+ * Event buffering service for core networking and connectivity metrics.
+ *
+ * {@hide}
+ */
final public class IpConnectivityMetrics extends SystemService {
private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
private static final boolean DBG = false;
@@ -58,7 +63,10 @@ final public class IpConnectivityMetrics extends SystemService {
private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
- // Default size of the event buffer. Once the buffer is full, incoming events are dropped.
+ // Default size of the event rolling log for bug report dumps.
+ private static final int DEFAULT_LOG_SIZE = 500;
+ // Default size of the event buffer for metrics reporting.
+ // Once the buffer is full, incoming events are dropped.
private static final int DEFAULT_BUFFER_SIZE = 2000;
// Maximum size of the event buffer.
private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
@@ -67,24 +75,38 @@ final public class IpConnectivityMetrics extends SystemService {
private static final int ERROR_RATE_LIMITED = -1;
- // Lock ensuring that concurrent manipulations of the event buffer are correct.
+ // Lock ensuring that concurrent manipulations of the event buffers are correct.
// There are three concurrent operations to synchronize:
// - appending events to the buffer.
// - iterating throught the buffer.
// - flushing the buffer content and replacing it by a new buffer.
private final Object mLock = new Object();
+ // Implementation instance of IIpConnectivityMetrics.aidl.
@VisibleForTesting
public final Impl impl = new Impl();
+ // Subservice listening to Netd events via INetdEventListener.aidl.
@VisibleForTesting
NetdEventListenerService mNetdListener;
+ // Rolling log of the most recent events. This log is used for dumping
+ // connectivity events in bug reports.
+ @GuardedBy("mLock")
+ private final RingBuffer<ConnectivityMetricsEvent> mEventLog =
+ new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE);
+ // Buffer of connectivity events used for metrics reporting. This buffer
+ // does not rotate automatically and instead saturates when it becomes full.
+ // It is flushed at metrics reporting.
@GuardedBy("mLock")
private ArrayList<ConnectivityMetricsEvent> mBuffer;
+ // Total number of events dropped from mBuffer since last metrics reporting.
@GuardedBy("mLock")
private int mDropped;
+ // Capacity of mBuffer
@GuardedBy("mLock")
private int mCapacity;
+ // A list of rate limiting counters keyed by connectivity event types for
+ // metrics reporting mBuffer.
@GuardedBy("mLock")
private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
@@ -132,6 +154,7 @@ final public class IpConnectivityMetrics extends SystemService {
private int append(ConnectivityMetricsEvent event) {
if (DBG) Log.d(TAG, "logEvent: " + event);
synchronized (mLock) {
+ mEventLog.append(event);
final int left = mCapacity - mBuffer.size();
if (event == null) {
return left;
@@ -216,6 +239,23 @@ final public class IpConnectivityMetrics extends SystemService {
}
}
+ /**
+ * Prints for bug reports the content of the rolling event log and the
+ * content of Netd event listener.
+ */
+ private void cmdDumpsys(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final ConnectivityMetricsEvent[] events;
+ synchronized (mLock) {
+ events = mEventLog.toArray();
+ }
+ for (ConnectivityMetricsEvent ev : events) {
+ pw.println(ev.toString());
+ }
+ if (mNetdListener != null) {
+ mNetdListener.list(pw);
+ }
+ }
+
private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
pw.println("Buffered events: " + mBuffer.size());
@@ -258,7 +298,8 @@ final public class IpConnectivityMetrics extends SystemService {
cmdFlush(fd, pw, args);
return;
case CMD_DUMPSYS:
- // Fallthrough to CMD_LIST when dumpsys.cpp dumps services states (bug reports)
+ cmdDumpsys(fd, pw, args);
+ return;
case CMD_LIST:
cmdList(fd, pw, args);
return;
diff --git a/com/android/server/connectivity/Nat464Xlat.java b/com/android/server/connectivity/Nat464Xlat.java
index e6585ad1..fceacba4 100644
--- a/com/android/server/connectivity/Nat464Xlat.java
+++ b/com/android/server/connectivity/Nat464Xlat.java
@@ -20,6 +20,7 @@ import android.net.InterfaceConfiguration;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkInfo;
import android.net.RouteInfo;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -44,12 +45,18 @@ public class Nat464Xlat extends BaseNetworkObserver {
// This must match the interface prefix in clatd.c.
private static final String CLAT_PREFIX = "v4-";
- // The network types we will start clatd on,
+ // The network types on which we will start clatd,
// allowing clat only on networks for which we can support IPv6-only.
private static final int[] NETWORK_TYPES = {
- ConnectivityManager.TYPE_MOBILE,
- ConnectivityManager.TYPE_WIFI,
- ConnectivityManager.TYPE_ETHERNET,
+ ConnectivityManager.TYPE_MOBILE,
+ ConnectivityManager.TYPE_WIFI,
+ ConnectivityManager.TYPE_ETHERNET,
+ };
+
+ // The network states in which running clatd is supported.
+ private static final NetworkInfo.State[] NETWORK_STATES = {
+ NetworkInfo.State.CONNECTED,
+ NetworkInfo.State.SUSPENDED,
};
private final INetworkManagementService mNMService;
@@ -81,11 +88,8 @@ public class Nat464Xlat extends BaseNetworkObserver {
*/
public static boolean requiresClat(NetworkAgentInfo nai) {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
- final int netType = nai.networkInfo.getType();
final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
- // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping
- // clatd in SUSPENDED state.
- final boolean connected = nai.networkInfo.isConnected();
+ final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
// We only run clat on networks that don't have a native IPv4 address.
final boolean hasIPv4Address =
(nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
@@ -148,7 +152,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
* turn ND offload off if on WiFi.
*/
private void enterRunningState() {
- maybeSetIpv6NdOffload(mBaseIface, false);
mState = State.RUNNING;
}
@@ -156,10 +159,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
* Stop clatd, and turn ND offload on if it had been turned off.
*/
private void enterStoppingState() {
- if (isRunning()) {
- maybeSetIpv6NdOffload(mBaseIface, true);
- }
-
try {
mNMService.stopClatd(mBaseIface);
} catch(RemoteException|IllegalStateException e) {
@@ -275,19 +274,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
}
- private void maybeSetIpv6NdOffload(String iface, boolean on) {
- // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
- if (mNetwork.networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
- return;
- }
- try {
- Slog.d(TAG, (on ? "En" : "Dis") + "abling ND offload on " + iface);
- mNMService.setInterfaceIpv6NdOffload(iface, on);
- } catch(RemoteException|IllegalStateException e) {
- Slog.w(TAG, "Changing IPv6 ND offload on " + iface + "failed: " + e);
- }
- }
-
/**
* Adds stacked link on base link and transitions to RUNNING state.
*/
diff --git a/com/android/server/connectivity/NetdEventListenerService.java b/com/android/server/connectivity/NetdEventListenerService.java
index 6f7ace2f..6206dfcd 100644
--- a/com/android/server/connectivity/NetdEventListenerService.java
+++ b/com/android/server/connectivity/NetdEventListenerService.java
@@ -38,6 +38,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import java.io.PrintWriter;
@@ -82,9 +83,8 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
// Ring buffer array for storing packet wake up events sent by Netd.
@GuardedBy("this")
- private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
- @GuardedBy("this")
- private long mWakeupEventCursor = 0;
+ private final RingBuffer<WakeupEvent> mWakeupEvents =
+ new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
private final ConnectivityManager mCm;
@@ -170,18 +170,16 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
timestampMs = System.currentTimeMillis();
}
- addWakupEvent(iface, timestampMs, uid);
+ addWakeupEvent(iface, timestampMs, uid);
}
@GuardedBy("this")
- private void addWakupEvent(String iface, long timestampMs, int uid) {
- int index = wakeupEventIndex(mWakeupEventCursor);
- mWakeupEventCursor++;
+ private void addWakeupEvent(String iface, long timestampMs, int uid) {
WakeupEvent event = new WakeupEvent();
event.iface = iface;
event.timestampMs = timestampMs;
event.uid = uid;
- mWakeupEvents[index] = event;
+ mWakeupEvents.append(event);
WakeupStats stats = mWakeupStats.get(iface);
if (stats == null) {
stats = new WakeupStats(iface);
@@ -190,23 +188,6 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
stats.countEvent(event);
}
- @GuardedBy("this")
- private WakeupEvent[] getWakeupEvents() {
- int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
- WakeupEvent[] out = new WakeupEvent[length];
- // Reverse iteration from youngest event to oldest event.
- long inCursor = mWakeupEventCursor - 1;
- int outIdx = out.length - 1;
- while (outIdx >= 0) {
- out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
- }
- return out;
- }
-
- private static int wakeupEventIndex(long cursor) {
- return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
- }
-
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
@@ -230,7 +211,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
for (int i = 0; i < mWakeupStats.size(); i++) {
pw.println(mWakeupStats.valueAt(i));
}
- for (WakeupEvent wakeup : getWakeupEvents()) {
+ for (WakeupEvent wakeup : mWakeupEvents.toArray()) {
pw.println(wakeup);
}
}
diff --git a/com/android/server/connectivity/tethering/OffloadController.java b/com/android/server/connectivity/tethering/OffloadController.java
index 5eafe5f9..cff216c7 100644
--- a/com/android/server/connectivity/tethering/OffloadController.java
+++ b/com/android/server/connectivity/tethering/OffloadController.java
@@ -52,6 +52,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -73,6 +74,8 @@ public class OffloadController {
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
+ private static enum UpdateType { IF_NEEDED, FORCE };
+
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
@@ -185,8 +188,8 @@ public class OffloadController {
updateStatsForAllUpstreams();
forceTetherStatsPoll();
// [2] (Re)Push all state.
- // TODO: computeAndPushLocalPrefixes()
- // TODO: push all downstream state.
+ computeAndPushLocalPrefixes(UpdateType.FORCE);
+ pushAllDownstreamState();
pushUpstreamParameters(null);
}
@@ -319,7 +322,7 @@ public class OffloadController {
}
private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occuring on this upstream.
+ // setDataLimit may only be called while offload is occurring on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
return true;
}
@@ -368,15 +371,15 @@ public class OffloadController {
// upstream parameters fails (probably just wait for a subsequent
// onOffloadEvent() callback to tell us offload is available again and
// then reapply all state).
- computeAndPushLocalPrefixes();
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
pushUpstreamParameters(prevUpstream);
}
public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
- if (!started()) return;
-
mExemptPrefixes = localPrefixes;
- computeAndPushLocalPrefixes();
+
+ if (!started()) return;
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
}
public void notifyDownstreamLinkProperties(LinkProperties lp) {
@@ -385,27 +388,38 @@ public class OffloadController {
if (Objects.equals(oldLp, lp)) return;
if (!started()) return;
+ pushDownstreamState(oldLp, lp);
+ }
- final List<RouteInfo> oldRoutes = (oldLp != null) ? oldLp.getRoutes() : new ArrayList<>();
- final List<RouteInfo> newRoutes = lp.getRoutes();
+ private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) {
+ final String ifname = newLp.getInterfaceName();
+ final List<RouteInfo> oldRoutes =
+ (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST;
+ final List<RouteInfo> newRoutes = newLp.getRoutes();
// For each old route, if not in new routes: remove.
- for (RouteInfo oldRoute : oldRoutes) {
- if (shouldIgnoreDownstreamRoute(oldRoute)) continue;
- if (!newRoutes.contains(oldRoute)) {
- mHwInterface.removeDownstreamPrefix(ifname, oldRoute.getDestination().toString());
+ for (RouteInfo ri : oldRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!newRoutes.contains(ri)) {
+ mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
// For each new route, if not in old routes: add.
- for (RouteInfo newRoute : newRoutes) {
- if (shouldIgnoreDownstreamRoute(newRoute)) continue;
- if (!oldRoutes.contains(newRoute)) {
- mHwInterface.addDownstreamPrefix(ifname, newRoute.getDestination().toString());
+ for (RouteInfo ri : newRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!oldRoutes.contains(ri)) {
+ mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
}
+ private void pushAllDownstreamState() {
+ for (LinkProperties lp : mDownstreams.values()) {
+ pushDownstreamState(null, lp);
+ }
+ }
+
public void removeDownstreamInterface(String ifname) {
final LinkProperties lp = mDownstreams.remove(ifname);
if (lp == null) return;
@@ -484,10 +498,11 @@ public class OffloadController {
return success;
}
- private boolean computeAndPushLocalPrefixes() {
+ private boolean computeAndPushLocalPrefixes(UpdateType how) {
+ final boolean force = (how == UpdateType.FORCE);
final Set<String> localPrefixStrs = computeLocalPrefixStrings(
mExemptPrefixes, mUpstreamLinkProperties);
- if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
+ if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
mLastLocalPrefixStrs = localPrefixStrs;
return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
@@ -581,9 +596,10 @@ public class OffloadController {
}
mNatUpdateCallbacksReceived++;
+ final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
+ protoName, srcAddr, srcPort, dstAddr, dstPort);
if (DBG) {
- mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
- protoName, srcAddr, srcPort, dstAddr, dstPort));
+ mLog.log("NAT timeout update: " + natDescription);
}
final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
@@ -594,7 +610,7 @@ public class OffloadController {
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
} catch (ErrnoException e) {
mNatUpdateNetlinkErrors++;
- mLog.e("Error updating NAT conntrack entry: " + e
+ mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
+ ", msg: " + NetlinkConstants.hexify(msg));
mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
diff --git a/com/android/server/content/SyncManager.java b/com/android/server/content/SyncManager.java
index 2f3b5596..9cd52d77 100644
--- a/com/android/server/content/SyncManager.java
+++ b/com/android/server/content/SyncManager.java
@@ -46,8 +46,8 @@ import android.content.SyncStatusInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
@@ -143,6 +143,7 @@ public class SyncManager {
private static final boolean DEBUG_ACCOUNT_ACCESS = false;
+ // Only do the check on a debuggable build.
private static final boolean ENABLE_SUSPICIOUS_CHECK = Build.IS_DEBUGGABLE;
/** Delay a sync due to local changes this long. In milliseconds */
@@ -537,9 +538,11 @@ public class SyncManager {
* @return whether the device most likely has some periodic syncs.
*/
private boolean likelyHasPeriodicSyncs() {
- // STOPSHIP Remove the google specific string.
try {
- return AccountManager.get(mContext).getAccountsByType("com.google").length > 0;
+ // Each sync adapter has a daily periodic sync by default, but sync adapters can remove
+ // them by themselves. So here, we use an arbitrary threshold. If there are more than
+ // this many sync endpoints, surely one of them should have a periodic sync...
+ return mSyncStorageEngine.getAuthorityCount() >= 6;
} catch (Throwable th) {
// Just in case.
}
@@ -3775,48 +3778,10 @@ public class SyncManager {
}
if (op.isPeriodic) {
mLogger.log("Removing periodic sync ", op, " for ", why);
-
- if (ENABLE_SUSPICIOUS_CHECK && isSuspiciousPeriodicSyncRemoval(op)) {
- wtfWithLog("Suspicious removal of " + op + " for " + why);
- }
}
getJobScheduler().cancel(op.jobId);
}
- private boolean isSuspiciousPeriodicSyncRemoval(SyncOperation op) {
- // STOPSHIP Remove the google specific string.
- if (!op.isPeriodic){
- return false;
- }
- boolean found = false;
- for (UserInfo user : UserManager.get(mContext).getUsers(/*excludeDying=*/ true)) {
- if (op.target.userId == user.id) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false; // User is being removed, okay.
- }
- switch (op.target.provider) {
- case "gmail-ls":
- case "com.android.contacts.metadata":
- break;
- default:
- return false;
- }
- final Account account = op.target.account;
- final Account[] accounts = AccountManager.get(mContext)
- .getAccountsByTypeAsUser(account.type, UserHandle.of(op.target.userId));
- for (Account a : accounts) {
- if (a.equals(account)) {
- return true; // Account still exists. Suspicious!
- }
- }
- // Account no longer exists. Makes sense...
- return false;
- }
-
private void wtfWithLog(String message) {
Slog.wtf(TAG, message);
mLogger.log("WTF: ", message);
diff --git a/com/android/server/content/SyncStorageEngine.java b/com/android/server/content/SyncStorageEngine.java
index 7b277c06..3591871f 100644
--- a/com/android/server/content/SyncStorageEngine.java
+++ b/com/android/server/content/SyncStorageEngine.java
@@ -911,6 +911,12 @@ public class SyncStorageEngine extends Handler {
}
}
+ public int getAuthorityCount() {
+ synchronized (mAuthorities) {
+ return mAuthorities.size();
+ }
+ }
+
public AuthorityInfo getAuthority(int authorityId) {
synchronized (mAuthorities) {
return mAuthorities.get(authorityId);
diff --git a/com/android/server/devicepolicy/DevicePolicyManagerService.java b/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6c859f76..c59f44e7 100644
--- a/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -97,8 +97,8 @@ import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
@@ -5350,7 +5350,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private void forceWipeUser(int userId) {
+ private void forceWipeUser(int userId, String wipeReasonForUser) {
try {
IActivityManager am = mInjector.getIActivityManager();
if (am.getCurrentUser().id == userId) {
@@ -5361,7 +5361,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!userRemoved) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
} else if (isManagedProfile(userId)) {
- sendWipeProfileNotification();
+ sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
// Shouldn't happen
@@ -5369,23 +5369,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public void wipeData(int flags) {
+ public void wipeDataWithReason(int flags, String wipeReasonForUser) {
if (!mHasFeature) {
return;
}
+ Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
synchronized (this) {
admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
}
- String reason = "DevicePolicyManager.wipeData() from "
+ String internalReason = "DevicePolicyManager.wipeDataWithReason() from "
+ admin.info.getComponent().flattenToShortString();
wipeDataNoLock(
- admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier());
+ admin.info.getComponent(), flags, internalReason, wipeReasonForUser,
+ admin.getUserHandle().getIdentifier());
}
- private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) {
+ private void wipeDataNoLock(ComponentName admin, int flags, String internalReason,
+ String wipeReasonForUser, int userId) {
wtfIfInLock();
long ident = mInjector.binderClearCallingIdentity();
@@ -5420,25 +5423,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// (rather than system), we should probably trigger factory reset. Current code just
// removes that user (but still clears FRP...)
if (userId == UserHandle.USER_SYSTEM) {
- forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0,
- reason, /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
+ forceWipeDeviceNoLock(/*wipeExtRequested=*/ (
+ flags & WIPE_EXTERNAL_STORAGE) != 0,
+ internalReason,
+ /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
} else {
- forceWipeUser(userId);
+ forceWipeUser(userId, wipeReasonForUser);
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
- private void sendWipeProfileNotification() {
- String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe);
+ private void sendWipeProfileNotification(String wipeReasonForUser) {
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setContentTitle(mContext.getString(R.string.work_profile_deleted))
- .setContentText(contentText)
+ .setContentText(wipeReasonForUser)
.setColor(mContext.getColor(R.color.system_notification_accent_color))
- .setStyle(new Notification.BigTextStyle().bigText(contentText))
+ .setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser))
.build();
mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification);
}
@@ -5610,9 +5614,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// able to do so).
// IMPORTANT: Call without holding the lock to prevent deadlock.
try {
+ String wipeReasonForUser = mContext.getString(
+ R.string.work_profile_deleted_reason_maximum_password_failure);
wipeDataNoLock(strictestAdmin.info.getComponent(),
/*flags=*/ 0,
/*reason=*/ "reportFailedPasswordAttempt()",
+ wipeReasonForUser,
userId);
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Failed to wipe user " + userId
@@ -5621,7 +5628,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (mInjector.securityLogIsLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
+ /*result*/ 0,
/*method strength*/ 1);
}
}
diff --git a/com/android/server/display/DisplayDeviceInfo.java b/com/android/server/display/DisplayDeviceInfo.java
index ef6de4c1..fddb81ba 100644
--- a/com/android/server/display/DisplayDeviceInfo.java
+++ b/com/android/server/display/DisplayDeviceInfo.java
@@ -98,6 +98,12 @@ final class DisplayDeviceInfo {
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9;
/**
+ * Flag: This display will destroy its content on removal.
+ * @hide
+ */
+ public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/com/android/server/display/LocalDisplayAdapter.java b/com/android/server/display/LocalDisplayAdapter.java
index 87564846..d61a418c 100644
--- a/com/android/server/display/LocalDisplayAdapter.java
+++ b/com/android/server/display/LocalDisplayAdapter.java
@@ -473,17 +473,18 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
// If the state change was from or to VR, then we need to tell the light
- // so that it can apply appropriate VR brightness settings. This should
- // happen prior to changing the brightness but also if there is no
- // brightness change at all.
+ // so that it can apply appropriate VR brightness settings. Also, update the
+ // brightness so the state is propogated to light.
+ boolean vrModeChange = false;
if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
currentState != state) {
setVrMode(state == Display.STATE_VR);
+ vrModeChange = true;
}
// Apply brightness changes given that we are in a non-suspended state.
- if (brightnessChanged) {
+ if (brightnessChanged || vrModeChange) {
setDisplayBrightness(brightness);
}
diff --git a/com/android/server/display/LogicalDisplay.java b/com/android/server/display/LogicalDisplay.java
index addad0b4..78a54079 100644
--- a/com/android/server/display/LogicalDisplay.java
+++ b/com/android/server/display/LogicalDisplay.java
@@ -238,6 +238,9 @@ final class LogicalDisplay {
// For private displays by default content is destroyed on removal.
mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
+ }
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
}
diff --git a/com/android/server/display/NightDisplayService.java b/com/android/server/display/NightDisplayService.java
index aafc6317..9cf13672 100644
--- a/com/android/server/display/NightDisplayService.java
+++ b/com/android/server/display/NightDisplayService.java
@@ -48,8 +48,10 @@ import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Calendar;
import java.util.TimeZone;
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
@@ -306,7 +308,7 @@ public final class NightDisplayService extends SystemService
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
if (mAutoMode != null) {
@@ -315,7 +317,7 @@ public final class NightDisplayService extends SystemService
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
if (mAutoMode != null) {
@@ -414,6 +416,36 @@ public final class NightDisplayService extends SystemService
outTemp[10] = blue;
}
+ /**
+ * Returns the first date time corresponding to the local time that occurs before the
+ * provided date time.
+ *
+ * @param compareTime the LocalDateTime to compare against
+ * @return the prior LocalDateTime corresponding to this local time
+ */
+ public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+ final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+ compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+ // Check if the local time has passed, if so return the same time yesterday.
+ return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
+ }
+
+ /**
+ * Returns the first date time corresponding to this local time that occurs after the
+ * provided date time.
+ *
+ * @param compareTime the LocalDateTime to compare against
+ * @return the next LocalDateTime corresponding to this local time
+ */
+ public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+ final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+ compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+ // Check if the local time has passed, if so return the same time tomorrow.
+ return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
+ }
+
private abstract class AutoMode implements NightDisplayController.Callback {
public abstract void onStart();
@@ -425,10 +457,10 @@ public final class NightDisplayService extends SystemService
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
- private NightDisplayController.LocalTime mStartTime;
- private NightDisplayController.LocalTime mEndTime;
+ private LocalTime mStartTime;
+ private LocalTime mEndTime;
- private Calendar mLastActivatedTime;
+ private LocalDateTime mLastActivatedTime;
CustomAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -441,31 +473,15 @@ public final class NightDisplayService extends SystemService
}
private void updateActivated() {
- final Calendar now = Calendar.getInstance();
- final Calendar startTime = mStartTime.getDateTimeBefore(now);
- final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
+ final LocalDateTime now = LocalDateTime.now();
+ final LocalDateTime start = getDateTimeBefore(mStartTime, now);
+ final LocalDateTime end = getDateTimeAfter(mEndTime, start);
+ boolean activate = now.isBefore(end);
- boolean activate = now.before(endTime);
if (mLastActivatedTime != null) {
- // Convert mLastActivatedTime to the current timezone if needed.
- final TimeZone currentTimeZone = now.getTimeZone();
- if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
- final int year = mLastActivatedTime.get(Calendar.YEAR);
- final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
- final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
- final int minute = mLastActivatedTime.get(Calendar.MINUTE);
-
- mLastActivatedTime.setTimeZone(currentTimeZone);
- mLastActivatedTime.set(Calendar.YEAR, year);
- mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
- mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
- mLastActivatedTime.set(Calendar.MINUTE, minute);
- }
-
// Maintain the existing activated state if within the current period.
- if (mLastActivatedTime.before(now)
- && mLastActivatedTime.after(startTime)
- && (mLastActivatedTime.after(endTime) || now.before(endTime))) {
+ if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
+ && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
activate = mController.isActivated();
}
}
@@ -473,14 +489,16 @@ public final class NightDisplayService extends SystemService
if (mIsActivated == null || mIsActivated != activate) {
mController.setActivated(activate);
}
+
updateNextAlarm(mIsActivated, now);
}
- private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
+ private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
if (activated != null) {
- final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
- : mStartTime.getDateTimeAfter(now);
- mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
+ final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
+ : getDateTimeAfter(mStartTime, now);
+ final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
}
}
@@ -510,18 +528,18 @@ public final class NightDisplayService extends SystemService
@Override
public void onActivated(boolean activated) {
mLastActivatedTime = mController.getLastActivatedTime();
- updateNextAlarm(activated, Calendar.getInstance());
+ updateNextAlarm(activated, LocalDateTime.now());
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
mStartTime = startTime;
mLastActivatedTime = null;
updateActivated();
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
mEndTime = endTime;
mLastActivatedTime = null;
updateActivated();
@@ -550,15 +568,14 @@ public final class NightDisplayService extends SystemService
}
boolean activate = state.isNight();
- final Calendar lastActivatedTime = mController.getLastActivatedTime();
+ final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
if (lastActivatedTime != null) {
- final Calendar now = Calendar.getInstance();
- final Calendar sunrise = state.sunrise();
- final Calendar sunset = state.sunset();
-
+ final LocalDateTime now = LocalDateTime.now();
+ final LocalDateTime sunrise = state.sunrise();
+ final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (lastActivatedTime.before(now)
- && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
+ if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
+ ^ lastActivatedTime.isBefore(sunset))) {
activate = mController.isActivated();
}
}
diff --git a/com/android/server/display/VirtualDisplayAdapter.java b/com/android/server/display/VirtualDisplayAdapter.java
index d6ab8881..f86d5763 100644
--- a/com/android/server/display/VirtualDisplayAdapter.java
+++ b/com/android/server/display/VirtualDisplayAdapter.java
@@ -24,6 +24,8 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+import static android.hardware.display.DisplayManager
+ .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
@@ -363,6 +365,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/com/android/server/fingerprint/FingerprintService.java b/com/android/server/fingerprint/FingerprintService.java
index b1c165ef..1df9c861 100644
--- a/com/android/server/fingerprint/FingerprintService.java
+++ b/com/android/server/fingerprint/FingerprintService.java
@@ -63,6 +63,8 @@ import android.service.fingerprint.FingerprintActionStatsProto;
import android.service.fingerprint.FingerprintServiceDumpProto;
import android.service.fingerprint.FingerprintUserStatsProto;
import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -81,7 +83,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -89,18 +90,19 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint -related events.
+ * fingerprint-related events.
*
* @hide
*/
public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
static final String TAG = "FingerprintService";
static final boolean DEBUG = true;
- private static final boolean CLEANUP_UNUSED_FP = false;
+ private static final boolean CLEANUP_UNUSED_FP = true;
private static final String FP_DATA_DIR = "fpdata";
private static final int MSG_USER_SWITCHING = 10;
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
private class PerformanceStats {
int accept; // number of accepted fingerprints
@@ -128,8 +130,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private Context mContext;
private long mHalDeviceId;
- private boolean mTimedLockoutCleared;
- private int mFailedAttempts;
+ private SparseBooleanArray mTimedLockoutCleared;
+ private SparseIntArray mFailedAttempts;
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
private final PowerManager mPowerManager;
@@ -139,10 +141,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
-
private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>();
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints
+ private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
private class UserFingerprint {
Fingerprint f;
@@ -177,15 +177,17 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
- resetFailedAttempts(false /* clearAttemptCounter */);
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
}
}
};
- private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
+ private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() {
@Override
public void run() {
- resetFailedAttempts(true /* clearAttemptCounter */);
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
}
};
@@ -221,6 +223,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
RESET_FINGERPRINT_LOCKOUT, null /* handler */);
mUserManager = UserManager.get(mContext);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
}
@Override
@@ -233,7 +237,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
public synchronized IBiometricsFingerprint getFingerprintDaemon() {
if (mDaemon == null) {
- Slog.v(TAG, "mDeamon was null, reconnect to fingerprint");
+ Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
try {
mDaemon = IBiometricsFingerprint.getService();
} catch (java.util.NoSuchElementException e) {
@@ -259,7 +263,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
if (mHalDeviceId != 0) {
loadAuthenticatorIds();
updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doFingerprintCleanup(ActivityManager.getCurrentUser());
+ doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
@@ -288,52 +292,41 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
}
}
- private void doFingerprintCleanup(int userId) {
+ /**
+ * This method should be called upon connection to the daemon, and when user switches.
+ * @param userId
+ */
+ private void doFingerprintCleanupForUser(int userId) {
if (CLEANUP_UNUSED_FP) {
- resetEnumerateState();
- mEnumeratingUserIds.push(userId);
- enumerateNextUser();
+ enumerateUser(userId);
}
}
- private void resetEnumerateState() {
- if (DEBUG) Slog.v(TAG, "Enumerate cleaning up");
- mEnumeratingUserIds.clear();
+ private void clearEnumerateState() {
+ if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
mUnknownFingerprints.clear();
}
- private void enumerateNextUser() {
- int nextUser = mEnumeratingUserIds.getFirst();
- updateActiveGroup(nextUser, null);
+ private void enumerateUser(int userId) {
+ if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-
- if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of "
- + mEnumeratingUserIds.size() + " remaining users");
-
- startEnumerate(mToken, nextUser, null, restricted, true /* internal */);
+ startEnumerate(mToken, userId, null, restricted, true /* internal */);
}
// Remove unknown fingerprints from hardware
private void cleanupUnknownFingerprints() {
if (!mUnknownFingerprints.isEmpty()) {
- Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size());
UserFingerprint uf = mUnknownFingerprints.get(0);
mUnknownFingerprints.remove(uf);
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- updateActiveGroup(uf.userId, null);
startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
restricted, true /* internal */);
} else {
- resetEnumerateState();
+ clearEnumerateState();
}
}
protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
ClientMonitor client = mCurrentClient;
if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
@@ -343,24 +336,21 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
// All fingerprints in hardware for this user were enumerated
if (remaining == 0) {
- mEnumeratingUserIds.poll();
-
if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList();
- Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion");
- for (Fingerprint f : enrolled) {
+ List<Fingerprint> unknownFingerprints =
+ ((InternalEnumerateClient) client).getUnknownFingerprints();
+
+ if (!unknownFingerprints.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownFingerprints.size() +
+ " fingerprints for deletion");
+ }
+ for (Fingerprint f : unknownFingerprints) {
mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
}
- }
-
- removeClient(client);
-
- if (!mEnumeratingUserIds.isEmpty()) {
- enumerateNextUser();
- } else if (client instanceof InternalEnumerateClient) {
- if (DEBUG) Slog.v(TAG, "Finished enumerating all users");
- // This will start a chain of InternalRemovalClients
+ removeClient(client);
cleanupUnknownFingerprints();
+ } else {
+ removeClient(client);
}
}
}
@@ -368,7 +358,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
protected void handleError(long deviceId, int error, int vendorCode) {
ClientMonitor client = mCurrentClient;
if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
- resetEnumerateState();
+ clearEnumerateState();
}
if (client != null && client.onError(error, vendorCode)) {
removeClient(client);
@@ -412,7 +402,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
cleanupUnknownFingerprints();
} else if (client instanceof InternalRemovalClient){
- resetEnumerateState();
+ clearEnumerateState();
}
}
@@ -466,8 +456,14 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
}
void handleUserSwitching(int userId) {
+ if (mCurrentClient instanceof InternalRemovalClient
+ || mCurrentClient instanceof InternalEnumerateClient) {
+ Slog.w(TAG, "User switched while performing cleanup");
+ removeClient(mCurrentClient);
+ clearEnumerateState();
+ }
updateActiveGroup(userId, null);
- doFingerprintCleanup(userId);
+ doFingerprintCleanupForUser(userId);
}
private void removeClient(ClientMonitor client) {
@@ -488,27 +484,32 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
}
private int getLockoutMode() {
- if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
- (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+ } else if (failedAttempts > 0 &&
+ mTimedLockoutCleared.get(currentUser, false) == false
+ && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
return AuthenticationClient.LOCKOUT_TIMED;
}
return AuthenticationClient.LOCKOUT_NONE;
}
- private void scheduleLockoutReset() {
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent());
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
}
- private void cancelLockoutReset() {
- mAlarmManager.cancel(getLockoutResetIntent());
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
}
- private PendingIntent getLockoutResetIntent() {
- return PendingIntent.getBroadcast(mContext, 0,
- new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(mContext, userId,
+ new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
}
public long startPreEnroll(IBinder token) {
@@ -555,6 +556,12 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
// This condition means we're currently running internal diagnostics to
// remove extra fingerprints in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
+ if (newClient != null) {
+ Slog.w(TAG, "Internal cleanup in progress but trying to start client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ }
}
else {
currentClient.stop(initiatedByClient);
@@ -567,7 +574,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
if (DEBUG) Slog.v(TAG, "starting client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient + ")");
+ + ", initiatedByClient = " + initiatedByClient);
notifyClientActiveCallbacks(true);
newClient.start();
@@ -813,8 +820,9 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
@Override
public int handleFailedAttempt() {
- mFailedAttempts++;
- mTimedLockoutCleared = false;
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
final int lockoutMode = getLockoutMode();
if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
mPerformanceStats.permanentLockout++;
@@ -824,7 +832,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
// Failing multiple times will continue to push out the lockout time
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutReset();
+ scheduleLockoutResetForUser(currentUser);
return lockoutMode;
}
return AuthenticationClient.LOCKOUT_NONE;
@@ -832,7 +840,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
@Override
public void resetFailedAttempts() {
- FingerprintService.this.resetFailedAttempts(true /* clearAttemptCounter */);
+ FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
}
@Override
@@ -886,17 +895,17 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
// attempt counter should only be cleared when Keyguard goes away or when
// a fingerprint is successfully authenticated
- protected void resetFailedAttempts(boolean clearAttemptCounter) {
+ protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
}
if (clearAttemptCounter) {
- mFailedAttempts = 0;
+ mFailedAttempts.put(userId, 0);
}
- mTimedLockoutCleared = true;
+ mTimedLockoutCleared.put(userId, true);
// If we're asked to reset failed attempts externally (i.e. from Keyguard),
// the alarm might still be pending; remove it.
- cancelLockoutReset();
+ cancelLockoutResetForUser(userId);
notifyLockoutResetMonitors();
}
@@ -1277,7 +1286,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
public void resetTimeout(byte [] token) {
checkPermission(RESET_FINGERPRINT_LOCKOUT);
// TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(mResetFailedAttemptsRunnable);
+ mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
}
@Override
@@ -1338,6 +1347,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ set.put("permanentLockoutCrypto",
+ (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
sets.put(set);
}
@@ -1367,7 +1378,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
proto.write(FingerprintActionStatsProto.REJECT, normal.reject);
proto.write(FingerprintActionStatsProto.ACQUIRE, normal.acquire);
proto.write(FingerprintActionStatsProto.LOCKOUT, normal.lockout);
- proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+ proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.permanentLockout);
proto.end(countsToken);
}
@@ -1380,7 +1391,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
proto.write(FingerprintActionStatsProto.REJECT, crypto.reject);
proto.write(FingerprintActionStatsProto.ACQUIRE, crypto.acquire);
proto.write(FingerprintActionStatsProto.LOCKOUT, crypto.lockout);
- proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+ proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.permanentLockout);
proto.end(countsToken);
}
diff --git a/com/android/server/fingerprint/InternalEnumerateClient.java b/com/android/server/fingerprint/InternalEnumerateClient.java
index 88d9ef43..434db98a 100644
--- a/com/android/server/fingerprint/InternalEnumerateClient.java
+++ b/com/android/server/fingerprint/InternalEnumerateClient.java
@@ -30,7 +30,7 @@ import java.util.List;
public abstract class InternalEnumerateClient extends EnumerateClient {
private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete
+ private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int groupId, int userId,
@@ -47,7 +47,6 @@ public abstract class InternalEnumerateClient extends EnumerateClient {
if (mEnrolledList.get(i).getFingerId() == fingerId) {
mEnrolledList.remove(i);
matched = true;
- Slog.e(TAG, "Matched fingerprint fid=" + fingerId);
break;
}
}
@@ -55,7 +54,7 @@ public abstract class InternalEnumerateClient extends EnumerateClient {
// fingerId 0 means no fingerprints are in hardware
if (!matched && fingerId != 0) {
Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
- mEnumeratedList.add(fingerprint);
+ mUnknownFingerprints.add(fingerprint);
}
}
@@ -76,8 +75,8 @@ public abstract class InternalEnumerateClient extends EnumerateClient {
mEnrolledList.clear();
}
- public List<Fingerprint> getEnumeratedList() {
- return mEnumeratedList;
+ public List<Fingerprint> getUnknownFingerprints() {
+ return mUnknownFingerprints;
}
@Override
diff --git a/com/android/server/job/JobSchedulerService.java b/com/android/server/job/JobSchedulerService.java
index ac807941..78aa2f94 100644
--- a/com/android/server/job/JobSchedulerService.java
+++ b/com/android/server/job/JobSchedulerService.java
@@ -795,18 +795,22 @@ public final class JobSchedulerService extends com.android.server.SystemService
* @param uid Uid to check against for removal of a job.
*
*/
- public void cancelJobsForUid(int uid, String reason) {
+ public boolean cancelJobsForUid(int uid, String reason) {
if (uid == Process.SYSTEM_UID) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
- return;
+ return false;
}
+
+ boolean jobsCanceled = false;
synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
cancelJobImplLocked(toRemove, null, reason);
+ jobsCanceled = true;
}
}
+ return jobsCanceled;
}
/**
@@ -816,13 +820,14 @@ public final class JobSchedulerService extends com.android.server.SystemService
* @param uid Uid of the calling client.
* @param jobId Id of the job, provided at schedule-time.
*/
- public void cancelJob(int uid, int jobId) {
+ public boolean cancelJob(int uid, int jobId) {
JobStatus toCancel;
synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
cancelJobImplLocked(toCancel, null, "cancel() called by app");
}
+ return (toCancel != null);
}
}
@@ -2147,6 +2152,39 @@ public final class JobSchedulerService extends com.android.server.SystemService
return 0;
}
+ // Shell command infrastructure: cancel a scheduled job
+ int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
+ boolean hasJobId, int jobId) {
+ if (DEBUG) {
+ Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
+ }
+
+ int pkgUid = -1;
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ pkgUid = pm.getPackageUid(pkgName, 0, userId);
+ } catch (RemoteException e) { /* can't happen */ }
+
+ if (pkgUid < 0) {
+ pw.println("Package " + pkgName + " not found.");
+ return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
+ }
+
+ if (!hasJobId) {
+ pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
+ if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
+ pw.println("No matching jobs found.");
+ }
+ } else {
+ pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
+ if (!cancelJob(pkgUid, jobId)) {
+ pw.println("No matching job found.");
+ }
+ }
+
+ return 0;
+ }
+
void setMonitorBattery(boolean enabled) {
synchronized (mLock) {
if (mBatteryController != null) {
diff --git a/com/android/server/job/JobSchedulerShellCommand.java b/com/android/server/job/JobSchedulerShellCommand.java
index a53c0885..d630aab6 100644
--- a/com/android/server/job/JobSchedulerShellCommand.java
+++ b/com/android/server/job/JobSchedulerShellCommand.java
@@ -48,6 +48,8 @@ public final class JobSchedulerShellCommand extends ShellCommand {
return runJob(pw);
case "timeout":
return timeout(pw);
+ case "cancel":
+ return cancelJob(pw);
case "monitor-battery":
return monitorBattery(pw);
case "get-battery-seq":
@@ -205,6 +207,42 @@ public final class JobSchedulerShellCommand extends ShellCommand {
}
}
+ private int cancelJob(PrintWriter pw) throws Exception {
+ checkPermission("cancel jobs");
+
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ if (userId < 0) {
+ pw.println("Error: must specify a concrete user ID");
+ return -1;
+ }
+
+ final String pkgName = getNextArg();
+ final String jobIdStr = getNextArg();
+ final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mInternal.executeCancelCommand(pw, pkgName, userId, jobIdStr != null, jobId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private int monitorBattery(PrintWriter pw) throws Exception {
checkPermission("change battery monitoring");
String opt = getNextArgRequired();
@@ -315,6 +353,12 @@ public final class JobSchedulerShellCommand extends ShellCommand {
pw.println(" Options:");
pw.println(" -u or --user: specify which user's job is to be run; the default is");
pw.println(" all users");
+ pw.println(" cancel [-u | --user USER_ID] PACKAGE [JOB_ID]");
+ pw.println(" Cancel a scheduled job. If a job ID is not supplied, all jobs scheduled");
+ pw.println(" by that package will be canceled. USE WITH CAUTION.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's job is to be run; the default is");
+ pw.println(" the primary or system user");
pw.println(" monitor-battery [on|off]");
pw.println(" Control monitoring of all battery changes. Off by default. Turning");
pw.println(" on makes get-battery-seq useful.");
diff --git a/com/android/server/locksettings/LockSettingsService.java b/com/android/server/locksettings/LockSettingsService.java
index 11043bd1..a1a01061 100644
--- a/com/android/server/locksettings/LockSettingsService.java
+++ b/com/android/server/locksettings/LockSettingsService.java
@@ -376,7 +376,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) {
- return new SyntheticPasswordManager(storage, getUserManager());
+ return new SyntheticPasswordManager(getContext(), storage, getUserManager());
}
public int binderGetCallingUid() {
@@ -763,7 +763,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private void migrateOldDataAfterSystemReady() {
try {
// Migrate the FRP credential to the persistent data block
- if (LockPatternUtils.frpCredentialEnabled() && !getBoolean("migrated_frp", false, 0)) {
+ if (LockPatternUtils.frpCredentialEnabled(mContext)
+ && !getBoolean("migrated_frp", false, 0)) {
migrateFrpCredential();
setBoolean("migrated_frp", true, 0);
Slog.i(TAG, "Migrated migrated_frp.");
@@ -784,7 +785,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
for (UserInfo userInfo : mUserManager.getUsers()) {
- if (userOwnsFrpCredential(userInfo) && isUserSecure(userInfo.id)) {
+ if (userOwnsFrpCredential(mContext, userInfo) && isUserSecure(userInfo.id)) {
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userInfo.id)) {
int actualQuality = (int) getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
@@ -2366,6 +2367,13 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
+ if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ // Most likely, an untrusted credential reset happened in the past which
+ // changed the synthetic password
+ Slog.e(TAG, "Obsolete token: synthetic password derived but it fails GK "
+ + "verification.");
+ return false;
+ }
// Update PASSWORD_TYPE_KEY since it's needed by notifyActivePasswordMetricsAvailable()
// called by setLockCredentialWithAuthTokenLocked().
// TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740
@@ -2497,7 +2505,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
public void onSystemReady() {
- if (frpCredentialEnabled()) {
+ if (frpCredentialEnabled(mContext)) {
updateRegistration();
} else {
// If we don't intend to use frpCredentials and we're not provisioned yet, send
@@ -2526,7 +2534,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private void clearFrpCredentialIfOwnerNotSecure() {
List<UserInfo> users = mUserManager.getUsers();
for (UserInfo user : users) {
- if (userOwnsFrpCredential(user)) {
+ if (userOwnsFrpCredential(mContext, user)) {
if (!isUserSecure(user.id)) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, user.id,
0, null);
diff --git a/com/android/server/locksettings/LockSettingsStrongAuth.java b/com/android/server/locksettings/LockSettingsStrongAuth.java
index 542b929d..c9c93293 100644
--- a/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -27,6 +27,7 @@ import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -74,7 +75,10 @@ public class LockSettingsStrongAuth {
}
public void systemReady() {
- mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ }
}
private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
diff --git a/com/android/server/locksettings/SyntheticPasswordManager.java b/com/android/server/locksettings/SyntheticPasswordManager.java
index 33a9a995..9440f171 100644
--- a/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -19,6 +19,7 @@ package com.android.server.locksettings;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.weaver.V1_0.IWeaver;
import android.hardware.weaver.V1_0.WeaverConfig;
@@ -255,13 +256,16 @@ public class SyntheticPasswordManager {
byte[] aggregatedSecret;
}
+ private final Context mContext;
private LockSettingsStorage mStorage;
private IWeaver mWeaver;
private WeaverConfig mWeaverConfig;
private final UserManager mUserManager;
- public SyntheticPasswordManager(LockSettingsStorage storage, UserManager userManager) {
+ public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
+ UserManager userManager) {
+ mContext = context;
mStorage = storage;
mUserManager = userManager;
}
@@ -645,7 +649,7 @@ public class SyntheticPasswordManager {
public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(userInfo)) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
userInfo.id));
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -662,7 +666,8 @@ public class SyntheticPasswordManager {
private void synchronizeFrpPassword(PasswordData pwd,
int requestedQuality, int userId) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext,
+ mUserManager.getUserInfo(userId))) {
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
pwd.toBytes());
@@ -675,7 +680,8 @@ public class SyntheticPasswordManager {
private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
int weaverSlot) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext,
+ mUserManager.getUserInfo(userId))) {
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
requestedQuality, pwd.toBytes());
diff --git a/com/android/server/media/MediaRouterService.java b/com/android/server/media/MediaRouterService.java
index 3795b7f3..1cfd5f02 100644
--- a/com/android/server/media/MediaRouterService.java
+++ b/com/android/server/media/MediaRouterService.java
@@ -271,14 +271,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public boolean isGlobalBluetoothA2doOn() {
- synchronized (mLock) {
- return mGlobalBluetoothA2dpOn;
- }
- }
-
- // Binder call
- @Override
public void setDiscoveryRequest(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
if (client == null) {
@@ -383,7 +375,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
synchronized (mLock) {
a2dpOn = mGlobalBluetoothA2dpOn;
}
- Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")");
+ Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
mAudioService.setBluetoothA2dpOn(a2dpOn);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn.");
diff --git a/com/android/server/media/MediaSessionRecord.java b/com/android/server/media/MediaSessionRecord.java
index 89e10503..0b11479a 100644
--- a/com/android/server/media/MediaSessionRecord.java
+++ b/com/android/server/media/MediaSessionRecord.java
@@ -462,18 +462,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mHandler.post(new Runnable() {
@Override
public void run() {
- if (useSuggested) {
- if (AudioSystem.isStreamActive(stream, 0)) {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
- flags, packageName, uid);
+ try {
+ if (useSuggested) {
+ if (AudioSystem.isStreamActive(stream, 0)) {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
+ direction, flags, packageName, uid);
+ } else {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
+ flags | previousFlagPlaySound, packageName, uid);
+ }
} else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, packageName, uid);
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
}
- } else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
+ + stream + ", flags=" + flags + ", packageName=" + packageName
+ + ", uid=" + uid + ", useSuggested=" + useSuggested
+ + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
}
}
});
diff --git a/com/android/server/media/MediaSessionService.java b/com/android/server/media/MediaSessionService.java
index b77ed913..b9a2d184 100644
--- a/com/android/server/media/MediaSessionService.java
+++ b/com/android/server/media/MediaSessionService.java
@@ -1363,6 +1363,10 @@ public class MediaSessionService extends SystemService implements Monitor {
flags, packageName, TAG);
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction
+ + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
+ e);
}
}
});
diff --git a/com/android/server/net/NetworkPolicyManagerService.java b/com/android/server/net/NetworkPolicyManagerService.java
index 90dab2c3..b4056b33 100644
--- a/com/android/server/net/NetworkPolicyManagerService.java
+++ b/com/android/server/net/NetworkPolicyManagerService.java
@@ -331,7 +331,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
private static final int MSG_POLICIES_CHANGED = 13;
- private static final int MSG_SET_FIREWALL_RULES = 14;
private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
private static final int UID_MSG_STATE_CHANGED = 100;
@@ -3138,9 +3137,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
- setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE);
+ setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE);
} else {
- setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE);
+ setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE);
}
}
@@ -3207,7 +3206,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
+ setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -3906,18 +3905,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
removeInterfaceQuota((String) msg.obj);
return true;
}
- case MSG_SET_FIREWALL_RULES: {
- final int chain = msg.arg1;
- final int toggle = msg.arg2;
- final SparseIntArray uidRules = (SparseIntArray) msg.obj;
- if (uidRules != null) {
- setUidFirewallRules(chain, uidRules);
- }
- if (toggle != CHAIN_TOGGLE_NONE) {
- enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
- }
- return true;
- }
case MSG_RESET_FIREWALL_RULES_BY_UID: {
resetUidFirewallRules(msg.arg1);
return true;
@@ -4063,15 +4050,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/**
* Calls {@link #setUidFirewallRules(int, SparseIntArray)} and
- * {@link #enableFirewallChainUL(int, boolean)} asynchronously.
+ * {@link #enableFirewallChainUL(int, boolean)} synchronously.
*
* @param chain firewall chain.
* @param uidRules new UID rules; if {@code null}, only toggles chain state.
* @param toggle whether the chain should be enabled, disabled, or not changed.
*/
- private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules,
+ private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
- mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget();
+ if (uidRules != null) {
+ setUidFirewallRulesUL(chain, uidRules);
+ }
+ if (toggle != CHAIN_TOGGLE_NONE) {
+ enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
+ }
}
/**
@@ -4079,7 +4071,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
- private void setUidFirewallRules(int chain, SparseIntArray uidRules) {
+ private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
try {
int size = uidRules.size();
int[] uids = new int[size];
diff --git a/com/android/server/notification/ConditionProviders.java b/com/android/server/notification/ConditionProviders.java
index 3444ef3e..c0fbfbb2 100644
--- a/com/android/server/notification/ConditionProviders.java
+++ b/com/android/server/notification/ConditionProviders.java
@@ -186,6 +186,11 @@ public class ConditionProviders extends ManagedServices {
super.onPackagesChanged(removingPackage, pkgList, uid);
}
+ @Override
+ protected boolean isValidEntry(String packageOrComponent, int userId) {
+ return true;
+ }
+
public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
synchronized(mMutex) {
return checkServiceTokenLocked(provider);
diff --git a/com/android/server/notification/ImportanceExtractor.java b/com/android/server/notification/ImportanceExtractor.java
index 46ec92b1..452121c0 100644
--- a/com/android/server/notification/ImportanceExtractor.java
+++ b/com/android/server/notification/ImportanceExtractor.java
@@ -22,7 +22,7 @@ import android.util.Slog;
* Determines the importance of the given notification.
*/
public class ImportanceExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantTopicExtractor";
+ private static final String TAG = "ImportanceExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/com/android/server/notification/ManagedServices.java b/com/android/server/notification/ManagedServices.java
index add4184f..019c7c2f 100644
--- a/com/android/server/notification/ManagedServices.java
+++ b/com/android/server/notification/ManagedServices.java
@@ -45,12 +45,16 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.service.notification.ManagedServiceInfoProto;
+import android.service.notification.ManagedServicesProto;
+import android.service.notification.ManagedServicesProto.ServiceProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -214,6 +218,53 @@ abstract public class ManagedServices {
}
}
+ public void dump(ProtoOutputStream proto, DumpFilter filter) {
+ proto.write(ManagedServicesProto.CAPTION, getCaption());
+ final int N = mApproved.size();
+ for (int i = 0 ; i < N; i++) {
+ final int userId = mApproved.keyAt(i);
+ final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ if (approvedByType != null) {
+ final int M = approvedByType.size();
+ for (int j = 0; j < M; j++) {
+ final boolean isPrimary = approvedByType.keyAt(j);
+ final ArraySet<String> approved = approvedByType.valueAt(j);
+ if (approvedByType != null && approvedByType.size() > 0) {
+ final long sToken = proto.start(ManagedServicesProto.APPROVED);
+ for (String s : approved) {
+ proto.write(ServiceProto.NAME, s);
+ }
+ proto.write(ServiceProto.USER_ID, userId);
+ proto.write(ServiceProto.IS_PRIMARY, isPrimary);
+ proto.end(sToken);
+ }
+ }
+ }
+ }
+
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+
+ final long cToken = proto.start(ManagedServicesProto.ENABLED);
+ cmpt.toProto(proto);
+ proto.end(cToken);
+ }
+
+ for (ManagedServiceInfo info : mServices) {
+ if (filter != null && !filter.matches(info.component)) continue;
+
+ final long lToken = proto.start(ManagedServicesProto.LIVE_SERVICES);
+ info.toProto(proto, this);
+ proto.end(lToken);
+ }
+
+ for (ComponentName name : mSnoozingForCurrentProfiles) {
+ final long cToken = proto.start(ManagedServicesProto.SNOOZED);
+ name.toProto(proto);
+ proto.end(cToken);
+ }
+ }
+
protected void onSettingRestored(String element, String value, int backupSdkInt, int userId) {
if (!mUseXml) {
Slog.d(TAG, "Restored managed service setting: " + element);
@@ -294,6 +345,7 @@ abstract public class ManagedServices {
}
if (type == XmlPullParser.START_TAG) {
if (TAG_MANAGED_SERVICES.equals(tag)) {
+ Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");
final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
@@ -353,6 +405,8 @@ abstract public class ManagedServices {
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
boolean isPrimary, boolean enabled) {
+ Slog.i(TAG,
+ (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent);
ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId);
if (allowedByType == null) {
allowedByType = new ArrayMap<>();
@@ -460,6 +514,7 @@ abstract public class ManagedServices {
}
public void onUserRemoved(int user) {
+ Slog.i(TAG, "Removing approved services for removed user " + user);
mApproved.remove(user);
rebindServices(true);
}
@@ -491,6 +546,17 @@ abstract public class ManagedServices {
return null;
}
+ protected boolean isServiceTokenValidLocked(IInterface service) {
+ if (service == null) {
+ return false;
+ }
+ ManagedServiceInfo info = getServiceFromTokenLocked(service);
+ if (info != null) {
+ return true;
+ }
+ return false;
+ }
+
protected ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
checkNotNull(service);
ManagedServiceInfo info = getServiceFromTokenLocked(service);
@@ -543,10 +609,8 @@ abstract public class ManagedServices {
}
// State changed
- if (DEBUG) {
- Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
- component.flattenToShortString());
- }
+ Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
+ component.flattenToShortString());
synchronized (mMutex) {
final int[] userIds = mUserProfiles.getCurrentProfileIds();
@@ -628,12 +692,10 @@ abstract public class ManagedServices {
int P = approved.size();
for (int k = P - 1; k >= 0; k--) {
final String approvedPackageOrComponent = approved.valueAt(k);
- if (!hasMatchingServices(approvedPackageOrComponent, userId)){
+ if (!isValidEntry(approvedPackageOrComponent, userId)){
approved.removeAt(k);
- if (DEBUG) {
- Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no matching services found");
- }
+ Slog.v(TAG, "Removing " + approvedPackageOrComponent
+ + " from approved list; no matching services found");
} else {
if (DEBUG) {
Slog.v(TAG, "Keeping " + approvedPackageOrComponent
@@ -678,6 +740,10 @@ abstract public class ManagedServices {
}
}
+ protected boolean isValidEntry(String packageOrComponent, int userId) {
+ return hasMatchingServices(packageOrComponent, userId);
+ }
+
private boolean hasMatchingServices(String packageOrComponent, int userId) {
if (!TextUtils.isEmpty(packageOrComponent)) {
final String packageName = getPackageName(packageOrComponent);
@@ -774,7 +840,12 @@ abstract public class ManagedServices {
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
- if (info == null || !mConfig.bindPermission.equals(info.permission)) {
+ if (info == null) {
+ Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ + ": service not found");
+ continue;
+ }
+ if (!mConfig.bindPermission.equals(info.permission)) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": it does not require the permission " + mConfig.bindPermission);
continue;
@@ -830,8 +901,7 @@ abstract public class ManagedServices {
if (name.equals(info.component)
&& info.userid == userid) {
// cut old connections
- if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": "
- + info.service);
+ Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service);
removeServiceLocked(i);
if (info.connection != null) {
mContext.unbindService(info.connection);
@@ -859,7 +929,7 @@ abstract public class ManagedServices {
appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
try {
- if (DEBUG) Slog.v(TAG, "binding: " + intent);
+ Slog.v(TAG, "binding: " + intent);
ServiceConnection serviceConnection = new ServiceConnection() {
IInterface mService;
@@ -917,8 +987,7 @@ abstract public class ManagedServices {
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
- if (name.equals(info.component)
- && info.userid == userid) {
+ if (name.equals(info.component) && info.userid == userid) {
removeServiceLocked(i);
if (info.connection != null) {
try {
@@ -945,9 +1014,8 @@ abstract public class ManagedServices {
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
- if (info.service.asBinder() == service.asBinder()
- && info.userid == userid) {
- if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
+ if (info.service.asBinder() == service.asBinder() && info.userid == userid) {
+ Slog.d(TAG, "Removing active service " + info.component);
serviceInfo = removeServiceLocked(i);
}
}
@@ -1035,6 +1103,16 @@ abstract public class ManagedServices {
.append(']').toString();
}
+ public void toProto(ProtoOutputStream proto, ManagedServices host) {
+ final long cToken = proto.start(ManagedServiceInfoProto.COMPONENT);
+ component.toProto(proto);
+ proto.end(cToken);
+ proto.write(ManagedServiceInfoProto.USER_ID, userid);
+ proto.write(ManagedServiceInfoProto.SERVICE, service.getClass().getName());
+ proto.write(ManagedServiceInfoProto.IS_SYSTEM, isSystem);
+ proto.write(ManagedServiceInfoProto.IS_GUEST, isGuest(host));
+ }
+
public boolean enabledAndUserMatches(int nid) {
if (!isEnabledForCurrentProfiles()) {
return false;
diff --git a/com/android/server/notification/NotificationAdjustmentExtractor.java b/com/android/server/notification/NotificationAdjustmentExtractor.java
index 7c828458..3bfd93fd 100644
--- a/com/android/server/notification/NotificationAdjustmentExtractor.java
+++ b/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -22,7 +22,7 @@ import android.util.Slog;
* Applies adjustments from the group helper and notification assistant
*/
public class NotificationAdjustmentExtractor implements NotificationSignalExtractor {
- private static final String TAG = "BadgeExtractor";
+ private static final String TAG = "AdjustmentExtractor";
private static final boolean DBG = false;
@@ -35,7 +35,6 @@ public class NotificationAdjustmentExtractor implements NotificationSignalExtrac
if (DBG) Slog.d(TAG, "skipping empty notification");
return null;
}
-
record.applyAdjustments();
return null;
diff --git a/com/android/server/notification/NotificationChannelExtractor.java b/com/android/server/notification/NotificationChannelExtractor.java
index 46ab556f..11c7ab75 100644
--- a/com/android/server/notification/NotificationChannelExtractor.java
+++ b/com/android/server/notification/NotificationChannelExtractor.java
@@ -22,7 +22,7 @@ import android.util.Slog;
* Stores the latest notification channel information for this notification
*/
public class NotificationChannelExtractor implements NotificationSignalExtractor {
- private static final String TAG = "BadgeExtractor";
+ private static final String TAG = "ChannelExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/com/android/server/notification/NotificationDelegate.java b/com/android/server/notification/NotificationDelegate.java
index 6a1401c2..36bc0962 100644
--- a/com/android/server/notification/NotificationDelegate.java
+++ b/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import android.service.notification.NotificationStats;
+
import com.android.internal.statusbar.NotificationVisibility;
public interface NotificationDelegate {
@@ -24,7 +26,8 @@ public interface NotificationDelegate {
void onNotificationClick(int callingUid, int callingPid, String key);
void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex);
void onNotificationClear(int callingUid, int callingPid,
- String pkg, String tag, int id, int userId);
+ String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface);
void onNotificationError(int callingUid, int callingPid,
String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
@@ -35,4 +38,6 @@ public interface NotificationDelegate {
NotificationVisibility[] newlyVisibleKeys,
NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationDirectReplied(String key);
+ void onNotificationSettingsViewed(String key);
}
diff --git a/com/android/server/notification/NotificationManagerInternal.java b/com/android/server/notification/NotificationManagerInternal.java
index 4923b06e..f1476b34 100644
--- a/com/android/server/notification/NotificationManagerInternal.java
+++ b/com/android/server/notification/NotificationManagerInternal.java
@@ -17,8 +17,10 @@
package com.android.server.notification;
import android.app.Notification;
+import android.app.NotificationChannel;
public interface NotificationManagerInternal {
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId);
diff --git a/com/android/server/notification/NotificationManagerService.java b/com/android/server/notification/NotificationManagerService.java
index fe39fccb..14cd0555 100644
--- a/com/android/server/notification/NotificationManagerService.java
+++ b/com/android/server/notification/NotificationManagerService.java
@@ -16,8 +16,10 @@
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -125,12 +127,14 @@ import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.ListenersDisablingEffectsProto;
import android.service.notification.NotificationAssistantService;
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;
import android.service.notification.ZenModeConfig;
@@ -675,7 +679,14 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationClear(int callingUid, int callingPid,
- String pkg, String tag, int id, int userId) {
+ String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordDismissalSurface(dismissalSurface);
+ }
+ }
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
true, userId, REASON_CANCEL, null);
@@ -761,12 +772,35 @@ public class NotificationManagerService extends SystemService {
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
+ if (expanded) {
+ r.recordExpanded();
+ }
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
}
}
}
+
+ @Override
+ public void onNotificationDirectReplied(String key) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordDirectReplied();
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationSettingsViewed(String key) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordViewedSettings();
+ }
+ }
+ }
};
@GuardedBy("mNotificationLock")
@@ -1142,6 +1176,12 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
+ NotificationRecord getNotificationRecord(String key) {
+ return mNotificationsByKey.get(key);
+ }
+
+
+ @VisibleForTesting
void setSystemReady(boolean systemReady) {
mSystemReady = systemReady;
}
@@ -1216,7 +1256,7 @@ public class NotificationManagerService extends SystemService {
mUsageStats = usageStats;
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
- getContext().getPackageManager(),
+ mPackageManagerClient,
mRankingHandler,
mUsageStats,
extractorNames);
@@ -1269,13 +1309,11 @@ public class NotificationManagerService extends SystemService {
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
-
mInCallNotificationUri = Uri.parse("file://" +
resources.getString(R.string.config_inCallNotificationSound));
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
- .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
@@ -1476,7 +1514,7 @@ public class NotificationManagerService extends SystemService {
}
}
}
- mRankingHelper.updateNotificationChannel(pkg, uid, channel);
+ mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
if (!fromListener) {
final NotificationChannel modifiedChannel =
@@ -3239,14 +3277,51 @@ public class NotificationManagerService extends SystemService {
}
}
proto.end(records);
- }
- long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
- mZenModeHelper.dump(proto);
- for (ComponentName suppressor : mEffectsSuppressors) {
- proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
+ long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
+ mZenModeHelper.dump(proto);
+ for (ComponentName suppressor : mEffectsSuppressors) {
+ proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
+ }
+ proto.end(zenLog);
+
+ long listenersToken = proto.start(NotificationServiceDumpProto.NOTIFICATION_LISTENERS);
+ mListeners.dump(proto, filter);
+ proto.end(listenersToken);
+
+ proto.write(NotificationServiceDumpProto.LISTENER_HINTS, mListenerHints);
+
+ for (int i = 0; i < mListenersDisablingEffects.size(); ++i) {
+ long effectsToken = proto.start(
+ NotificationServiceDumpProto.LISTENERS_DISABLING_EFFECTS);
+
+ proto.write(
+ ListenersDisablingEffectsProto.HINT, mListenersDisablingEffects.keyAt(i));
+ final ArraySet<ManagedServiceInfo> listeners =
+ mListenersDisablingEffects.valueAt(i);
+ for (int j = 0; j < listeners.size(); j++) {
+ final ManagedServiceInfo listener = listeners.valueAt(i);
+ listenersToken = proto.start(ListenersDisablingEffectsProto.LISTENERS);
+ listener.toProto(proto, null);
+ proto.end(listenersToken);
+ }
+
+ proto.end(effectsToken);
+ }
+
+ long assistantsToken = proto.start(
+ NotificationServiceDumpProto.NOTIFICATION_ASSISTANTS);
+ mAssistants.dump(proto, filter);
+ proto.end(assistantsToken);
+
+ long conditionsToken = proto.start(NotificationServiceDumpProto.CONDITION_PROVIDERS);
+ mConditionProviders.dump(proto, filter);
+ proto.end(conditionsToken);
+
+ long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
+ mRankingHelper.dump(proto, filter);
+ proto.end(rankingToken);
}
- proto.end(zenLog);
proto.flush();
}
@@ -3401,6 +3476,12 @@ public class NotificationManagerService extends SystemService {
*/
private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
@Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String
+ channelId) {
+ return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
+ }
+
+ @Override
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
@@ -3519,6 +3600,21 @@ public class NotificationManagerService extends SystemService {
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
+ && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
+ && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
+ // Increase the importance of foreground service notifications unless the user had an
+ // opinion otherwise
+ if (TextUtils.isEmpty(channelId)
+ || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
+ r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
+ } else {
+ channel.setImportance(IMPORTANCE_LOW);
+ mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
+ r.updateNotificationChannel(channel);
+ }
+ }
+
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.sbn.getOverrideGroupKey() != null)) {
return;
@@ -3752,6 +3848,8 @@ public class NotificationManagerService extends SystemService {
MetricsLogger.action(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsEvent.TYPE_CLOSE)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS,
+ mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -3763,6 +3861,7 @@ public class NotificationManagerService extends SystemService {
} else {
mSnoozeHelper.snooze(r, mDuration);
}
+ r.recordSnoozed();
savePolicyFile();
}
}
@@ -3902,7 +4001,7 @@ public class NotificationManagerService extends SystemService {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n,
- NotificationListenerService.REASON_ERROR);
+ NotificationListenerService.REASON_ERROR, null);
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -4028,19 +4127,19 @@ public class NotificationManagerService extends SystemService {
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
-
long[] vibration = record.getVibration();
// Demote sound to vibration if vibration missing & phone in vibration mode.
if (vibration == null
&& hasValidSound
&& (mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_VIBRATE)) {
+ == AudioManager.RINGER_MODE_VIBRATE)
+ && mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
-
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
@@ -4137,8 +4236,9 @@ public class NotificationManagerService extends SystemService {
boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
// do not play notifications if there is a user of exclusive audio focus
// or the device is in vibrate mode
- if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
- != AudioManager.RINGER_MODE_VIBRATE) {
+ if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
+ != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -4394,6 +4494,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<String> groupKeyBefore = new ArrayList<>(N);
ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
+ ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -4403,6 +4504,7 @@ public class NotificationManagerService extends SystemService {
groupKeyBefore.add(r.getGroupKey());
overridePeopleBefore.add(r.getPeopleOverride());
snoozeCriteriaBefore.add(r.getSnoozeCriteria());
+ userSentimentBefore.add(r.getUserSentiment());
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -4414,7 +4516,8 @@ public class NotificationManagerService extends SystemService {
|| !Objects.equals(channelBefore.get(i), r.getChannel())
|| !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
|| !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
- || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+ || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
+ || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())) {
mHandler.scheduleSendRankingUpdate();
return;
}
@@ -4607,6 +4710,10 @@ public class NotificationManagerService extends SystemService {
// Record caller.
recordCallerLocked(r);
+ if (r.getStats().getDismissalSurface() == NotificationStats.DISMISSAL_NOT_DISMISSED) {
+ r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
+ }
+
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -4627,7 +4734,7 @@ public class NotificationManagerService extends SystemService {
if (reason != REASON_SNOOZED) {
r.isCanceled = true;
}
- mListeners.notifyRemovedLocked(r.sbn, reason);
+ mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -5221,6 +5328,7 @@ public class NotificationManagerService extends SystemService {
Bundle overridePeople = new Bundle();
Bundle snoozeCriteria = new Bundle();
Bundle showBadge = new Bundle();
+ Bundle userSentiment = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -5246,6 +5354,7 @@ public class NotificationManagerService extends SystemService {
overridePeople.putStringArrayList(key, record.getPeopleOverride());
snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
showBadge.putBoolean(key, record.canShowBadge());
+ userSentiment.putInt(key, record.getUserSentiment());
}
final int M = keys.size();
String[] keysAr = keys.toArray(new String[M]);
@@ -5256,7 +5365,7 @@ public class NotificationManagerService extends SystemService {
}
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge);
+ channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -5345,7 +5454,7 @@ public class NotificationManagerService extends SystemService {
@Override
protected Config getConfig() {
Config c = new Config();
- c.caption = "notification assistant service";
+ c.caption = "notification assistant";
c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
@@ -5388,8 +5497,6 @@ public class NotificationManagerService extends SystemService {
continue;
}
- final int importance = r.getImportance();
- final boolean fromUser = r.isImportanceFromUser();
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
@@ -5420,6 +5527,10 @@ public class NotificationManagerService extends SystemService {
final String snoozeCriterionId) {
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info);
+ if (!sbnVisible) {
+ continue;
+ }
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
@@ -5541,7 +5652,8 @@ public class NotificationManagerService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
+ notifyRemoved(
+ info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
}
});
continue;
@@ -5561,7 +5673,8 @@ public class NotificationManagerService extends SystemService {
* asynchronously notify all listeners about a removed notification
*/
@GuardedBy("mNotificationLock")
- public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
+ public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
+ NotificationStats notificationStats) {
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
@@ -5570,11 +5683,14 @@ public class NotificationManagerService extends SystemService {
if (!isVisibleToListener(sbn, info)) {
continue;
}
+ // Only assistants can get stats
+ final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
+ ? notificationStats : null;
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, sbnLight, update, reason);
+ notifyRemoved(info, sbnLight, update, stats, reason);
}
});
}
@@ -5679,14 +5795,14 @@ public class NotificationManagerService extends SystemService {
}
private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
- NotificationRankingUpdate rankingUpdate, int reason) {
+ NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
+ listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
@@ -5772,10 +5888,9 @@ public class NotificationManagerService extends SystemService {
final DumpFilter filter = new DumpFilter();
for (int ai = 0; ai < args.length; ai++) {
final String a = args[ai];
- if ("--proto".equals(args[0])) {
+ if ("--proto".equals(a)) {
filter.proto = true;
- }
- if ("--noredact".equals(a) || "--reveal".equals(a)) {
+ } else if ("--noredact".equals(a) || "--reveal".equals(a)) {
filter.redact = false;
} else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
if (ai < args.length-1) {
@@ -5848,8 +5963,8 @@ public class NotificationManagerService extends SystemService {
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
- + "allow_listener COMPONENT\n"
- + "disallow_listener COMPONENT\n"
+ + "allow_listener COMPONENT [user_id]\n"
+ + "disallow_listener COMPONENT [user_id]\n"
+ "set_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
@@ -5880,7 +5995,13 @@ public class NotificationManagerService extends SystemService {
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, true);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, true);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), true);
+ }
}
break;
case "disallow_listener": {
@@ -5889,7 +6010,13 @@ public class NotificationManagerService extends SystemService {
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, false);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, false);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), false);
+ }
}
break;
case "allow_assistant": {
diff --git a/com/android/server/notification/NotificationRecord.java b/com/android/server/notification/NotificationRecord.java
index 77bf9e30..faa300f2 100644
--- a/com/android/server/notification/NotificationRecord.java
+++ b/com/android/server/notification/NotificationRecord.java
@@ -20,6 +20,8 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEUTRAL;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -41,6 +43,7 @@ import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRecordProto;
+import android.service.notification.NotificationStats;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
@@ -84,8 +87,6 @@ public final class NotificationRecord {
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
- /** Whether the notification was seen by the user via one of the notification listeners. */
- boolean mIsSeen;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -136,6 +137,8 @@ public final class NotificationRecord {
private String mChannelIdLogTag;
private final List<Adjustment> mAdjustments;
+ private final NotificationStats mStats;
+ private int mUserSentiment;
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
@@ -156,6 +159,7 @@ public final class NotificationRecord {
mImportance = calculateImportance();
mLight = calculateLights();
mAdjustments = new ArrayList<>();
+ mStats = new NotificationStats();
}
private boolean isPreChannelsNotification() {
@@ -395,7 +399,7 @@ public final class NotificationRecord {
pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + "pri=" + notification.priority);
pw.println(prefix + "key=" + sbn.getKey());
- pw.println(prefix + "seen=" + mIsSeen);
+ pw.println(prefix + "seen=" + mStats.hasSeen());
pw.println(prefix + "groupKey=" + getGroupKey());
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
pw.println(prefix + "contentIntent=" + notification.contentIntent);
@@ -572,6 +576,10 @@ public final class NotificationRecord {
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
setOverrideGroupKey(groupOverrideKey);
}
+ if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
+ setUserSentiment(adjustment.getSignals().getInt(
+ Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+ }
}
}
}
@@ -740,6 +748,7 @@ public final class NotificationRecord {
.setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
if (visible) {
+ setSeen();
MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
}
EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
@@ -777,12 +786,12 @@ public final class NotificationRecord {
/** Check if any of the listeners have marked this notification as seen by the user. */
public boolean isSeen() {
- return mIsSeen;
+ return mStats.hasSeen();
}
/** Mark the notification as seen by the user. */
public void setSeen() {
- mIsSeen = true;
+ mStats.setSeen();
}
public void setAuthoritativeRank(int authoritativeRank) {
@@ -883,6 +892,38 @@ public final class NotificationRecord {
mSnoozeCriteria = snoozeCriteria;
}
+ private void setUserSentiment(int userSentiment) {
+ mUserSentiment = userSentiment;
+ }
+
+ public int getUserSentiment() {
+ return mUserSentiment;
+ }
+
+ public NotificationStats getStats() {
+ return mStats;
+ }
+
+ public void recordExpanded() {
+ mStats.setExpanded();
+ }
+
+ public void recordDirectReplied() {
+ mStats.setDirectReplied();
+ }
+
+ public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
+ mStats.setDismissalSurface(surface);
+ }
+
+ public void recordSnoozed() {
+ mStats.setSnoozed();
+ }
+
+ public void recordViewedSettings() {
+ mStats.setViewedSettings();
+ }
+
public LogMaker getLogMaker(long now) {
if (mLogMaker == null) {
// initialize fields that only change on update (so a new record)
diff --git a/com/android/server/notification/PriorityExtractor.java b/com/android/server/notification/PriorityExtractor.java
index 5d5d39de..7a287dba 100644
--- a/com/android/server/notification/PriorityExtractor.java
+++ b/com/android/server/notification/PriorityExtractor.java
@@ -23,7 +23,7 @@ import android.util.Slog;
* Determines if the given notification can bypass Do Not Disturb.
*/
public class PriorityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantTopicExtractor";
+ private static final String TAG = "PriorityExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/com/android/server/notification/RankingConfig.java b/com/android/server/notification/RankingConfig.java
index b5ef1c60..b9c0d907 100644
--- a/com/android/server/notification/RankingConfig.java
+++ b/com/android/server/notification/RankingConfig.java
@@ -39,7 +39,7 @@ public interface RankingConfig {
int uid, boolean includeDeleted);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp);
- void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
+ void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/com/android/server/notification/RankingHelper.java b/com/android/server/notification/RankingHelper.java
index fc24581f..d7e9cf37 100644
--- a/com/android/server/notification/RankingHelper.java
+++ b/com/android/server/notification/RankingHelper.java
@@ -36,10 +36,13 @@ import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.RankingHelperProto;
+import android.service.notification.RankingHelperProto.RecordProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONArray;
import org.json.JSONException;
@@ -228,7 +231,11 @@ public class RankingHelper implements RankingConfig {
if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
NotificationChannel channel = new NotificationChannel(id,
channelName, channelImportance);
- channel.populateFromXml(parser);
+ if (forRestore) {
+ channel.populateFromXmlForRestore(parser, mContext);
+ } else {
+ channel.populateFromXml(parser);
+ }
r.channels.put(id, channel);
}
}
@@ -391,7 +398,11 @@ public class RankingHelper implements RankingConfig {
}
for (NotificationChannel channel : r.channels.values()) {
- if (!forBackup || (forBackup && !channel.isDeleted())) {
+ if (forBackup) {
+ if (!channel.isDeleted()) {
+ channel.writeXmlForBackup(out, mContext);
+ }
+ } else {
channel.writeXml(out);
}
}
@@ -613,7 +624,8 @@ public class RankingHelper implements RankingConfig {
}
@Override
- public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) {
+ public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
+ boolean fromUser) {
Preconditions.checkNotNull(updatedChannel);
Preconditions.checkNotNull(updatedChannel.getId());
Record r = getOrCreateRecord(pkg, uid);
@@ -627,7 +639,11 @@ public class RankingHelper implements RankingConfig {
if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
- lockFieldsForUpdate(channel, updatedChannel);
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
+ updatedChannel.lockFields(channel.getUserLockedFields());
+ if (fromUser) {
+ lockFieldsForUpdate(channel, updatedChannel);
+ }
r.channels.put(updatedChannel.getId(), updatedChannel);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
@@ -874,8 +890,6 @@ public class RankingHelper implements RankingConfig {
@VisibleForTesting
void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
- update.unlockFields(update.getUserLockedFields());
- update.lockFields(original.getUserLockedFields());
if (original.canBypassDnd() != update.canBypassDnd()) {
update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
}
@@ -912,8 +926,7 @@ public class RankingHelper implements RankingConfig {
pw.print(" ");
pw.println(mSignalExtractors[i]);
}
- }
- if (filter == null) {
+
pw.print(prefix);
pw.println("per-package config:");
}
@@ -925,6 +938,52 @@ public class RankingHelper implements RankingConfig {
dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
+ public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) {
+ final int N = mSignalExtractors.length;
+ for (int i = 0; i < N; i++) {
+ proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
+ mSignalExtractors[i].getClass().getSimpleName());
+ }
+ synchronized (mRecords) {
+ dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords);
+ }
+ dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+ mRestoredWithoutUids);
+ }
+
+ private static void dumpRecords(ProtoOutputStream proto, long fieldId,
+ NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
+ final int N = records.size();
+ long fToken;
+ for (int i = 0; i < N; i++) {
+ final Record r = records.valueAt(i);
+ if (filter == null || filter.matches(r.pkg)) {
+ fToken = proto.start(fieldId);
+
+ proto.write(RecordProto.PACKAGE, r.pkg);
+ proto.write(RecordProto.UID, r.uid);
+ proto.write(RecordProto.IMPORTANCE, r.importance);
+ proto.write(RecordProto.PRIORITY, r.priority);
+ proto.write(RecordProto.VISIBILITY, r.visibility);
+ proto.write(RecordProto.SHOW_BADGE, r.showBadge);
+
+ long token;
+ for (NotificationChannel channel : r.channels.values()) {
+ token = proto.start(RecordProto.CHANNELS);
+ channel.toProto(proto);
+ proto.end(token);
+ }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ token = proto.start(RecordProto.CHANNEL_GROUPS);
+ group.toProto(proto);
+ proto.end(token);
+ }
+
+ proto.end(fToken);
+ }
+ }
+ }
+
private static void dumpRecords(PrintWriter pw, String prefix,
NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
final int N = records.size();
diff --git a/com/android/server/notification/ScheduleCalendar.java b/com/android/server/notification/ScheduleCalendar.java
index 9e8b2e34..40230bd2 100644
--- a/com/android/server/notification/ScheduleCalendar.java
+++ b/com/android/server/notification/ScheduleCalendar.java
@@ -42,7 +42,8 @@ public class ScheduleCalendar {
public void maybeSetNextAlarm(long now, long nextAlarm) {
if (mSchedule != null) {
- if (mSchedule.exitAtAlarm && now > mSchedule.nextAlarm) {
+ if (mSchedule.exitAtAlarm
+ && (now > mSchedule.nextAlarm || nextAlarm < mSchedule.nextAlarm)) {
mSchedule.nextAlarm = nextAlarm;
}
}
diff --git a/com/android/server/notification/ZenModeHelper.java b/com/android/server/notification/ZenModeHelper.java
index ffdafc56..9fcc67df 100644
--- a/com/android/server/notification/ZenModeHelper.java
+++ b/com/android/server/notification/ZenModeHelper.java
@@ -567,7 +567,7 @@ public class ZenModeHelper {
proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString());
}
}
- proto.write(ZenModeProto.POLICY, mConfig.toNotificationPolicy().toString());
+ mConfig.toNotificationPolicy().toProto(proto, ZenModeProto.POLICY);
proto.write(ZenModeProto.SUPPRESSED_EFFECTS, mSuppressedEffects);
}
}
diff --git a/com/android/server/oemlock/OemLockService.java b/com/android/server/oemlock/OemLockService.java
index 40c66394..5b3d1eca 100644
--- a/com/android/server/oemlock/OemLockService.java
+++ b/com/android/server/oemlock/OemLockService.java
@@ -31,6 +31,7 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.service.oemlock.IOemLockService;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Slog;
import com.android.server.LocalServices;
@@ -98,6 +99,7 @@ public class OemLockService extends SystemService {
!newRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET);
if (!unlockAllowedByAdmin) {
mOemLock.setOemUnlockAllowedByDevice(false);
+ setPersistentDataBlockOemUnlockAllowedBit(false);
}
}
}
@@ -158,6 +160,7 @@ public class OemLockService extends SystemService {
}
mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
+ setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -202,6 +205,20 @@ public class OemLockService extends SystemService {
}
};
+ /**
+ * Always synchronize the OemUnlockAllowed bit to the FRP partition, which
+ * is used to erase FRP information on a unlockable device.
+ */
+ private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
+ final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
+ mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
+ if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)) {
+ Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
+ pdbm.setOemUnlockEnabled(allowed);
+ }
+ }
+
private boolean isOemUnlockAllowedByAdmin() {
return !UserManager.get(mContext)
.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM);
diff --git a/com/android/server/pm/BackgroundDexOptService.java b/com/android/server/pm/BackgroundDexOptService.java
index 415c9a9c..6d8cac0c 100644
--- a/com/android/server/pm/BackgroundDexOptService.java
+++ b/com/android/server/pm/BackgroundDexOptService.java
@@ -342,8 +342,7 @@ public class BackgroundDexOptService extends JobService {
DexoptOptions.DEXOPT_BOOT_COMPLETE |
(downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0);
if (is_for_primary_dex) {
- int result = pm.performDexOptWithStatus(new DexoptOptions(pkg,
- PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason,
dexoptFlags));
success = result != PackageDexOptimizer.DEX_OPT_FAILED;
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -351,8 +350,7 @@ public class BackgroundDexOptService extends JobService {
}
} else {
success = pm.performDexOpt(new DexoptOptions(pkg,
- PackageManagerService.REASON_BACKGROUND_DEXOPT,
- dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
+ reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
}
if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
diff --git a/com/android/server/pm/BasePermission.java b/com/android/server/pm/BasePermission.java
deleted file mode 100644
index 30fda1e5..00000000
--- a/com/android/server/pm/BasePermission.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.content.pm.PackageParser;
-import android.content.pm.PermissionInfo;
-import android.os.UserHandle;
-
-final class BasePermission {
- final static int TYPE_NORMAL = 0;
-
- final static int TYPE_BUILTIN = 1;
-
- final static int TYPE_DYNAMIC = 2;
-
- final String name;
-
- String sourcePackage;
-
- PackageSettingBase packageSetting;
-
- final int type;
-
- int protectionLevel;
-
- PackageParser.Permission perm;
-
- PermissionInfo pendingInfo;
-
- /** UID that owns the definition of this permission */
- int uid;
-
- /** Additional GIDs given to apps granted this permission */
- private int[] gids;
-
- /**
- * Flag indicating that {@link #gids} should be adjusted based on the
- * {@link UserHandle} the granted app is running as.
- */
- private boolean perUser;
-
- BasePermission(String _name, String _sourcePackage, int _type) {
- name = _name;
- sourcePackage = _sourcePackage;
- type = _type;
- // Default to most conservative protection level.
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
- }
-
- @Override
- public String toString() {
- return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
- + "}";
- }
-
- public void setGids(int[] gids, boolean perUser) {
- this.gids = gids;
- this.perUser = perUser;
- }
-
- public int[] computeGids(int userId) {
- if (perUser) {
- final int[] userGids = new int[gids.length];
- for (int i = 0; i < gids.length; i++) {
- userGids[i] = UserHandle.getUid(userId, gids[i]);
- }
- return userGids;
- } else {
- return gids;
- }
- }
-
- public boolean isRuntime() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS;
- }
-
- public boolean isDevelopment() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_SIGNATURE
- && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
- }
-
- public boolean isInstant() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
- }
-
- public boolean isRuntimeOnly() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
- }
-}
diff --git a/com/android/server/pm/DefaultPermissionGrantPolicy.java b/com/android/server/pm/DefaultPermissionGrantPolicy.java
deleted file mode 100644
index a3811baf..00000000
--- a/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ /dev/null
@@ -1,1244 +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.server.pm;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.DownloadManager;
-import android.app.admin.DevicePolicyManager;
-import android.companion.CompanionDeviceManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal.PackagesProvider;
-import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
-import android.content.pm.PackageParser;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.print.PrintManager;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.TelephonyManager;
-import android.security.Credentials;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static android.os.Process.FIRST_APPLICATION_UID;
-
-/**
- * This class is the policy for granting runtime permissions to
- * platform components and default handlers in the system such
- * that the device is usable out-of-the-box. For example, the
- * shell UID is a part of the system and the Phone app should
- * have phone related permission by default.
- */
-final class DefaultPermissionGrantPolicy {
- private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
- private static final boolean DEBUG = false;
-
- private static final int DEFAULT_FLAGS =
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
- private static final String AUDIO_MIME_TYPE = "audio/mpeg";
-
- private static final String TAG_EXCEPTIONS = "exceptions";
- private static final String TAG_EXCEPTION = "exception";
- private static final String TAG_PERMISSION = "permission";
- private static final String ATTR_PACKAGE = "package";
- private static final String ATTR_NAME = "name";
- private static final String ATTR_FIXED = "fixed";
-
- private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
- static {
- PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
- PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
- PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
- PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
- PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
- PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
- PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
- }
-
- private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
- static {
- CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
- CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
- CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
- }
-
- private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
- static {
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
- }
-
- private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
- static {
- CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
- CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
- }
-
- private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
- static {
- SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
- SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
- }
-
- private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
- static {
- MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
- }
-
- private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
- static {
- CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
- }
-
- private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
- static {
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- }
-
- private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
- static {
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
- STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- }
-
- private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
-
- private static final String ACTION_TRACK = "com.android.fitness.TRACK";
-
- private final PackageManagerService mService;
- private final Handler mHandler;
-
- private PackagesProvider mLocationPackagesProvider;
- private PackagesProvider mVoiceInteractionPackagesProvider;
- private PackagesProvider mSmsAppPackagesProvider;
- private PackagesProvider mDialerAppPackagesProvider;
- private PackagesProvider mSimCallManagerPackagesProvider;
- private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
-
- private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
-
- public DefaultPermissionGrantPolicy(PackageManagerService service) {
- mService = service;
- mHandler = new Handler(mService.mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
- synchronized (mService.mPackages) {
- if (mGrantExceptions == null) {
- mGrantExceptions = readDefaultPermissionExceptionsLPw();
- }
- }
- }
- }
- };
- }
-
- public void setLocationPackagesProviderLPw(PackagesProvider provider) {
- mLocationPackagesProvider = provider;
- }
-
- public void setVoiceInteractionPackagesProviderLPw(PackagesProvider provider) {
- mVoiceInteractionPackagesProvider = provider;
- }
-
- public void setSmsAppPackagesProviderLPw(PackagesProvider provider) {
- mSmsAppPackagesProvider = provider;
- }
-
- public void setDialerAppPackagesProviderLPw(PackagesProvider provider) {
- mDialerAppPackagesProvider = provider;
- }
-
- public void setSimCallManagerPackagesProviderLPw(PackagesProvider provider) {
- mSimCallManagerPackagesProvider = provider;
- }
-
- public void setSyncAdapterPackagesProviderLPw(SyncAdapterPackagesProvider provider) {
- mSyncAdapterPackagesProvider = provider;
- }
-
- public void grantDefaultPermissions(int userId) {
- if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- grantAllRuntimePermissions(userId);
- } else {
- grantPermissionsToSysComponentsAndPrivApps(userId);
- grantDefaultSystemHandlerPermissions(userId);
- grantDefaultPermissionExceptions(userId);
- }
- }
-
- private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
- Set<String> permissions = new ArraySet<>();
- for (String permission : pkg.requestedPermissions) {
- BasePermission bp = mService.mSettings.mPermissions.get(permission);
- if (bp != null && bp.isRuntime()) {
- permissions.add(permission);
- }
- }
- if (!permissions.isEmpty()) {
- grantRuntimePermissionsLPw(pkg, permissions, true, userId);
- }
- }
-
- private void grantAllRuntimePermissions(int userId) {
- Log.i(TAG, "Granting all runtime permissions for user " + userId);
- synchronized (mService.mPackages) {
- for (PackageParser.Package pkg : mService.mPackages.values()) {
- grantRuntimePermissionsForPackageLocked(userId, pkg);
- }
- }
- }
-
- public void scheduleReadDefaultPermissionExceptions() {
- mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
- }
-
- private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
- Log.i(TAG, "Granting permissions to platform components for user " + userId);
-
- synchronized (mService.mPackages) {
- for (PackageParser.Package pkg : mService.mPackages.values()) {
- if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
- || !doesPackageSupportRuntimePermissions(pkg)
- || pkg.requestedPermissions.isEmpty()) {
- continue;
- }
- grantRuntimePermissionsForPackageLocked(userId, pkg);
- }
- }
- }
-
- private void grantDefaultSystemHandlerPermissions(int userId) {
- Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
-
- final PackagesProvider locationPackagesProvider;
- final PackagesProvider voiceInteractionPackagesProvider;
- final PackagesProvider smsAppPackagesProvider;
- final PackagesProvider dialerAppPackagesProvider;
- final PackagesProvider simCallManagerPackagesProvider;
- final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
-
- synchronized (mService.mPackages) {
- locationPackagesProvider = mLocationPackagesProvider;
- voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
- smsAppPackagesProvider = mSmsAppPackagesProvider;
- dialerAppPackagesProvider = mDialerAppPackagesProvider;
- simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
- syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
- }
-
- String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
- ? voiceInteractionPackagesProvider.getPackages(userId) : null;
- String[] locationPackageNames = (locationPackagesProvider != null)
- ? locationPackagesProvider.getPackages(userId) : null;
- String[] smsAppPackageNames = (smsAppPackagesProvider != null)
- ? smsAppPackagesProvider.getPackages(userId) : null;
- String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
- ? dialerAppPackagesProvider.getPackages(userId) : null;
- String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
- ? simCallManagerPackagesProvider.getPackages(userId) : null;
- String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
- syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
- String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
- syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
-
- synchronized (mService.mPackages) {
- // Installer
- PackageParser.Package installerPackage = getSystemPackageLPr(
- mService.mRequiredInstallerPackage);
- if (installerPackage != null
- && doesPackageSupportRuntimePermissions(installerPackage)) {
- grantRuntimePermissionsLPw(installerPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Verifier
- PackageParser.Package verifierPackage = getSystemPackageLPr(
- mService.mRequiredVerifierPackage);
- if (verifierPackage != null
- && doesPackageSupportRuntimePermissions(verifierPackage)) {
- grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId);
- }
-
- // SetupWizard
- PackageParser.Package setupPackage = getSystemPackageLPr(
- mService.mSetupWizardPackage);
- if (setupPackage != null
- && doesPackageSupportRuntimePermissions(setupPackage)) {
- grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, CAMERA_PERMISSIONS, userId);
- }
-
- // Camera
- Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr(
- cameraIntent, userId);
- if (cameraPackage != null
- && doesPackageSupportRuntimePermissions(cameraPackage)) {
- grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Media provider
- PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackageLPr(
- MediaStore.AUTHORITY, userId);
- if (mediaStorePackage != null) {
- grantRuntimePermissionsLPw(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Downloads provider
- PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackageLPr(
- "downloads", userId);
- if (downloadsPackage != null) {
- grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Downloads UI
- Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
- PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackageLPr(
- downloadsUiIntent, userId);
- if (downloadsUiPackage != null
- && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
- grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Storage provider
- PackageParser.Package storagePackage = getDefaultProviderAuthorityPackageLPr(
- "com.android.externalstorage.documents", userId);
- if (storagePackage != null) {
- grantRuntimePermissionsLPw(storagePackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // CertInstaller
- Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
- PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackageLPr(
- certInstallerIntent, userId);
- if (certInstallerPackage != null
- && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
- grantRuntimePermissionsLPw(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Dialer
- if (dialerAppPackageNames == null) {
- Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
- PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackageLPr(
- dialerIntent, userId);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
- }
- } else {
- for (String dialerAppPackageName : dialerAppPackageNames) {
- PackageParser.Package dialerPackage = getSystemPackageLPr(dialerAppPackageName);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
- }
- }
- }
-
- // Sim call manager
- if (simCallManagerPackageNames != null) {
- for (String simCallManagerPackageName : simCallManagerPackageNames) {
- PackageParser.Package simCallManagerPackage =
- getSystemPackageLPr(simCallManagerPackageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage,
- userId);
- }
- }
- }
-
- // SMS
- if (smsAppPackageNames == null) {
- Intent smsIntent = new Intent(Intent.ACTION_MAIN);
- smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
- PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackageLPr(
- smsIntent, userId);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
- }
- } else {
- for (String smsPackageName : smsAppPackageNames) {
- PackageParser.Package smsPackage = getSystemPackageLPr(smsPackageName);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
- }
- }
- }
-
- // Cell Broadcast Receiver
- Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
- PackageParser.Package cbrPackage =
- getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId);
- if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
- grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, userId);
- }
-
- // Carrier Provisioning Service
- Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
- PackageParser.Package carrierProvPackage =
- getDefaultSystemHandlerServicePackageLPr(carrierProvIntent, userId);
- if (carrierProvPackage != null && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
- grantRuntimePermissionsLPw(carrierProvPackage, SMS_PERMISSIONS, false, userId);
- }
-
- // Calendar
- Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
- calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
- PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackageLPr(
- calendarIntent, userId);
- if (calendarPackage != null
- && doesPackageSupportRuntimePermissions(calendarPackage)) {
- grantRuntimePermissionsLPw(calendarPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(calendarPackage, CONTACTS_PERMISSIONS, userId);
- }
-
- // Calendar provider
- PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackageLPr(
- CalendarContract.AUTHORITY, userId);
- if (calendarProviderPackage != null) {
- grantRuntimePermissionsLPw(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(calendarProviderPackage, CALENDAR_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Calendar provider sync adapters
- List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
- calendarSyncAdapterPackages, userId);
- final int calendarSyncAdapterCount = calendarSyncAdapters.size();
- for (int i = 0; i < calendarSyncAdapterCount; i++) {
- PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
- grantRuntimePermissionsLPw(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
- }
- }
-
- // Contacts
- Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
- contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
- PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackageLPr(
- contactsIntent, userId);
- if (contactsPackage != null
- && doesPackageSupportRuntimePermissions(contactsPackage)) {
- grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId);
- }
-
- // Contacts provider sync adapters
- List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
- contactsSyncAdapterPackages, userId);
- final int contactsSyncAdapterCount = contactsSyncAdapters.size();
- for (int i = 0; i < contactsSyncAdapterCount; i++) {
- PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
- grantRuntimePermissionsLPw(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
- }
- }
-
- // Contacts provider
- PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackageLPr(
- ContactsContract.AUTHORITY, userId);
- if (contactsProviderPackage != null) {
- grantRuntimePermissionsLPw(contactsProviderPackage, CONTACTS_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(contactsProviderPackage, PHONE_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Device provisioning
- Intent deviceProvisionIntent = new Intent(
- DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
- PackageParser.Package deviceProvisionPackage =
- getDefaultSystemHandlerActivityPackageLPr(deviceProvisionIntent, userId);
- if (deviceProvisionPackage != null
- && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
- grantRuntimePermissionsLPw(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
- }
-
- // Maps
- Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
- mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
- PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackageLPr(
- mapsIntent, userId);
- if (mapsPackage != null
- && doesPackageSupportRuntimePermissions(mapsPackage)) {
- grantRuntimePermissionsLPw(mapsPackage, LOCATION_PERMISSIONS, userId);
- }
-
- // Gallery
- Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
- galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
- PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackageLPr(
- galleryIntent, userId);
- if (galleryPackage != null
- && doesPackageSupportRuntimePermissions(galleryPackage)) {
- grantRuntimePermissionsLPw(galleryPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Email
- Intent emailIntent = new Intent(Intent.ACTION_MAIN);
- emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
- PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackageLPr(
- emailIntent, userId);
- if (emailPackage != null
- && doesPackageSupportRuntimePermissions(emailPackage)) {
- grantRuntimePermissionsLPw(emailPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(emailPackage, CALENDAR_PERMISSIONS, userId);
- }
-
- // Browser
- PackageParser.Package browserPackage = null;
- String defaultBrowserPackage = mService.getDefaultBrowserPackageName(userId);
- if (defaultBrowserPackage != null) {
- browserPackage = getPackageLPr(defaultBrowserPackage);
- }
- if (browserPackage == null) {
- Intent browserIntent = new Intent(Intent.ACTION_MAIN);
- browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
- browserPackage = getDefaultSystemHandlerActivityPackageLPr(
- browserIntent, userId);
- }
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, userId);
- }
-
- // Voice interaction
- if (voiceInteractPackageNames != null) {
- for (String voiceInteractPackageName : voiceInteractPackageNames) {
- PackageParser.Package voiceInteractPackage = getSystemPackageLPr(
- voiceInteractPackageName);
- if (voiceInteractPackage != null
- && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
- grantRuntimePermissionsLPw(voiceInteractPackage,
- CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- LOCATION_PERMISSIONS, userId);
- }
- }
- }
-
- if (ActivityManager.isLowRamDeviceStatic()) {
- // Allow voice search on low-ram devices
- Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
- PackageParser.Package globalSearchPickerPackage =
- getDefaultSystemHandlerActivityPackageLPr(globalSearchIntent, userId);
-
- if (globalSearchPickerPackage != null
- && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
- grantRuntimePermissionsLPw(globalSearchPickerPackage,
- MICROPHONE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(globalSearchPickerPackage,
- LOCATION_PERMISSIONS, true, userId);
- }
- }
-
- // Voice recognition
- Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
- voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
- PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackageLPr(
- voiceRecoIntent, userId);
- if (voiceRecoPackage != null
- && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
- grantRuntimePermissionsLPw(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
- }
-
- // Location
- if (locationPackageNames != null) {
- for (String packageName : locationPackageNames) {
- PackageParser.Package locationPackage = getSystemPackageLPr(packageName);
- if (locationPackage != null
- && doesPackageSupportRuntimePermissions(locationPackage)) {
- grantRuntimePermissionsLPw(locationPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, LOCATION_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(locationPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, SENSORS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, STORAGE_PERMISSIONS, userId);
- }
- }
- }
-
- // Music
- Intent musicIntent = new Intent(Intent.ACTION_VIEW);
- musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
- musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
- AUDIO_MIME_TYPE);
- PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackageLPr(
- musicIntent, userId);
- if (musicPackage != null
- && doesPackageSupportRuntimePermissions(musicPackage)) {
- grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Home
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
- PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackageLPr(
- homeIntent, userId);
- if (homePackage != null
- && doesPackageSupportRuntimePermissions(homePackage)) {
- grantRuntimePermissionsLPw(homePackage, LOCATION_PERMISSIONS, false, userId);
- }
-
- // Watches
- if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
- // Home application on watches
- Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
- wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
-
- PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
- wearHomeIntent, userId);
-
- if (wearHomePackage != null
- && doesPackageSupportRuntimePermissions(wearHomePackage)) {
- grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
- userId);
- grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
- userId);
- grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
- userId);
- }
-
- // Fitness tracking on watches
- Intent trackIntent = new Intent(ACTION_TRACK);
- PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackageLPr(
- trackIntent, userId);
- if (trackPackage != null
- && doesPackageSupportRuntimePermissions(trackPackage)) {
- grantRuntimePermissionsLPw(trackPackage, SENSORS_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(trackPackage, LOCATION_PERMISSIONS, false, userId);
- }
- }
-
- // Print Spooler
- PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
- PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
- if (printSpoolerPackage != null
- && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
- grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
- }
-
- // EmergencyInfo
- Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
- PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackageLPr(
- emergencyInfoIntent, userId);
- if (emergencyInfoPckg != null
- && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
- grantRuntimePermissionsLPw(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
- }
-
- // NFC Tag viewer
- Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
- nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
- PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackageLPr(
- nfcTagIntent, userId);
- if (nfcTagPkg != null
- && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
- grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
- }
-
- // Storage Manager
- Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
- PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackageLPr(
- storageManagerIntent, userId);
- if (storageManagerPckg != null
- && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
- grantRuntimePermissionsLPw(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Companion devices
- PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackageLPr(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
- if (companionDeviceDiscoveryPackage != null
- && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
- grantRuntimePermissionsLPw(companionDeviceDiscoveryPackage,
- LOCATION_PERMISSIONS, true, userId);
- }
-
- // Ringtone Picker
- Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
- PackageParser.Package ringtonePickerPackage =
- getDefaultSystemHandlerActivityPackageLPr(ringtonePickerIntent, userId);
- if (ringtonePickerPackage != null
- && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
- grantRuntimePermissionsLPw(ringtonePickerPackage,
- STORAGE_PERMISSIONS, true, userId);
- }
-
- mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
- PackageParser.Package dialerPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- boolean isPhonePermFixed =
- mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
- grantRuntimePermissionsLPw(
- dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
- grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSystemSmsAppLPr(
- PackageParser.Package smsPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package smsPackage = getPackageLPr(packageName);
- if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultDialerAppLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package dialerPackage = getPackageLPr(packageName);
- if (dialerPackage != null
- && doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSimCallManagerLPr(
- PackageParser.Package simCallManagerPackage, int userId) {
- Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
- if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
- grantRuntimePermissionsLPw(simCallManagerPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultSimCallManagerLPr(String packageName, int userId) {
- if (packageName == null) {
- return;
- }
- PackageParser.Package simCallManagerPackage = getPackageLPr(packageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage, userId);
- }
- }
-
- public void grantDefaultPermissionsToEnabledCarrierAppsLPr(String[] packageNames, int userId) {
- Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
- if (packageNames == null) {
- return;
- }
- for (String packageName : packageNames) {
- PackageParser.Package carrierPackage = getSystemPackageLPr(packageName);
- if (carrierPackage != null
- && doesPackageSupportRuntimePermissions(carrierPackage)) {
- grantRuntimePermissionsLPw(carrierPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(carrierPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(carrierPackage, SMS_PERMISSIONS, userId);
- }
- }
- }
-
- public void grantDefaultPermissionsToEnabledImsServicesLPr(String[] packageNames, int userId) {
- Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
- if (packageNames == null) {
- return;
- }
- for (String packageName : packageNames) {
- PackageParser.Package imsServicePackage = getSystemPackageLPr(packageName);
- if (imsServicePackage != null
- && doesPackageSupportRuntimePermissions(imsServicePackage)) {
- grantRuntimePermissionsLPw(imsServicePackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, CAMERA_PERMISSIONS, userId);
- }
- }
- }
-
- public void grantDefaultPermissionsToDefaultBrowserLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package browserPackage = getSystemPackageLPr(packageName);
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
- }
- }
-
- private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
- Intent intent, int userId) {
- ResolveInfo handler = mService.resolveIntent(intent,
- intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
- if (handler == null || handler.activityInfo == null) {
- return null;
- }
- ActivityInfo activityInfo = handler.activityInfo;
- if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
- && activityInfo.name.equals(mService.mResolveActivity.name)) {
- return null;
- }
- return getSystemPackageLPr(handler.activityInfo.packageName);
- }
-
- private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
- Intent intent, int userId) {
- List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId)
- .getList();
- if (handlers == null) {
- return null;
- }
- final int handlerCount = handlers.size();
- for (int i = 0; i < handlerCount; i++) {
- ResolveInfo handler = handlers.get(i);
- PackageParser.Package handlerPackage = getSystemPackageLPr(
- handler.serviceInfo.packageName);
- if (handlerPackage != null) {
- return handlerPackage;
- }
- }
- return null;
- }
-
- private List<PackageParser.Package> getHeadlessSyncAdapterPackagesLPr(
- String[] syncAdapterPackageNames, int userId) {
- List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
-
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- for (String syncAdapterPackageName : syncAdapterPackageNames) {
- homeIntent.setPackage(syncAdapterPackageName);
-
- ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS,
- userId);
- if (homeActivity != null) {
- continue;
- }
-
- PackageParser.Package syncAdapterPackage = getSystemPackageLPr(syncAdapterPackageName);
- if (syncAdapterPackage != null) {
- syncAdapterPackages.add(syncAdapterPackage);
- }
- }
-
- return syncAdapterPackages;
- }
-
- private PackageParser.Package getDefaultProviderAuthorityPackageLPr(
- String authority, int userId) {
- ProviderInfo provider = mService.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
- if (provider != null) {
- return getSystemPackageLPr(provider.packageName);
- }
- return null;
- }
-
- private PackageParser.Package getPackageLPr(String packageName) {
- return mService.mPackages.get(packageName);
- }
-
- private PackageParser.Package getSystemPackageLPr(String packageName) {
- PackageParser.Package pkg = getPackageLPr(packageName);
- if (pkg != null && pkg.isSystemApp()) {
- return !isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) ? pkg : null;
- }
- return null;
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- int userId) {
- grantRuntimePermissionsLPw(pkg, permissions, false, false, userId);
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- boolean systemFixed, int userId) {
- grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId);
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
- if (pkg.requestedPermissions.isEmpty()) {
- return;
- }
-
- List<String> requestedPermissions = pkg.requestedPermissions;
- Set<String> grantablePermissions = null;
-
- // If this is the default Phone or SMS app we grant permissions regardless
- // whether the version on the system image declares the permission as used since
- // selecting the app as the default Phone or SMS the user makes a deliberate
- // choice to grant this app the permissions needed to function. For all other
- // apps, (default grants on first boot and user creation) we don't grant default
- // permissions if the version on the system image does not declare them.
- if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
- PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs != null && sysPs.pkg != null) {
- if (sysPs.pkg.requestedPermissions.isEmpty()) {
- return;
- }
- if (!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) {
- grantablePermissions = new ArraySet<>(requestedPermissions);
- requestedPermissions = sysPs.pkg.requestedPermissions;
- }
- }
- }
-
- final int grantablePermissionCount = requestedPermissions.size();
- for (int i = 0; i < grantablePermissionCount; i++) {
- String permission = requestedPermissions.get(i);
-
- // If there is a disabled system app it may request a permission the updated
- // version ot the data partition doesn't, In this case skip the permission.
- if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
- continue;
- }
-
- if (permissions.contains(permission)) {
- final int flags = mService.getPermissionFlags(permission, pkg.packageName, userId);
-
- // If any flags are set to the permission, then it is either set in
- // its current state by the system or device/profile owner or the user.
- // In all these cases we do not want to clobber the current state.
- // Unless the caller wants to override user choices. The override is
- // to make sure we can grant the needed permission to the default
- // sms and phone apps after the user chooses this in the UI.
- if (flags == 0 || isDefaultPhoneOrSms) {
- // Never clobber policy or system.
- final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
- | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- if ((flags & fixedFlags) != 0) {
- continue;
- }
-
- mService.grantRuntimePermission(pkg.packageName, permission, userId);
- if (DEBUG) {
- Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
- + permission + " to default handler " + pkg.packageName);
- }
-
- int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- if (systemFixed) {
- newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- }
-
- mService.updatePermissionFlags(permission, pkg.packageName,
- newFlags, newFlags, userId);
- }
-
- // If a component gets a permission for being the default handler A
- // and also default handler B, we grant the weaker grant form.
- if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
- && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
- && !systemFixed) {
- if (DEBUG) {
- Log.i(TAG, "Granted not fixed " + permission + " to default handler "
- + pkg.packageName);
- }
- mService.updatePermissionFlags(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
- }
- }
- }
- }
-
- private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
- if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
- return true;
- }
- if (!pkg.isPrivilegedApp()) {
- return false;
- }
- PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPkg != null && sysPkg.pkg != null) {
- if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
- return false;
- }
- } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
- return false;
- }
- return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
- pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
- }
-
- private void grantDefaultPermissionExceptions(int userId) {
- synchronized (mService.mPackages) {
- mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
-
- if (mGrantExceptions == null) {
- mGrantExceptions = readDefaultPermissionExceptionsLPw();
- }
-
- // mGrantExceptions is null only before the first read and then
- // it serves as a cache of the default grants that should be
- // performed for every user. If there is an entry then the app
- // is on the system image and supports runtime permissions.
- Set<String> permissions = null;
- final int exceptionCount = mGrantExceptions.size();
- for (int i = 0; i < exceptionCount; i++) {
- String packageName = mGrantExceptions.keyAt(i);
- PackageParser.Package pkg = getSystemPackageLPr(packageName);
- List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
- final int permissionGrantCount = permissionGrants.size();
- for (int j = 0; j < permissionGrantCount; j++) {
- DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
- if (permissions == null) {
- permissions = new ArraySet<>();
- } else {
- permissions.clear();
- }
- permissions.add(permissionGrant.name);
- grantRuntimePermissionsLPw(pkg, permissions,
- permissionGrant.fixed, userId);
- }
- }
- }
- }
-
- private File[] getDefaultPermissionFiles() {
- ArrayList<File> ret = new ArrayList<File>();
- File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
- dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
- return ret.isEmpty() ? null : ret.toArray(new File[0]);
- }
-
- private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
- readDefaultPermissionExceptionsLPw() {
- File[] files = getDefaultPermissionFiles();
- if (files == null) {
- return new ArrayMap<>(0);
- }
-
- ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
-
- // Iterate over the files in the directory and scan .xml files
- for (File file : files) {
- if (!file.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + file + " in " + file.getParent() + " directory, ignoring");
- continue;
- }
- if (!file.canRead()) {
- Slog.w(TAG, "Default permissions file " + file + " cannot be read");
- continue;
- }
- try (
- InputStream str = new BufferedInputStream(new FileInputStream(file))
- ) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
- parse(parser, grantExceptions);
- } catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "Error reading default permissions file " + file, e);
- }
- }
-
- return grantExceptions;
- }
-
- private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
- outGrantExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_EXCEPTIONS.equals(parser.getName())) {
- parseExceptions(parser, outGrantExceptions);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName());
- }
- }
- }
-
- private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
- outGrantExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_EXCEPTION.equals(parser.getName())) {
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-
- List<DefaultPermissionGrant> packageExceptions =
- outGrantExceptions.get(packageName);
- if (packageExceptions == null) {
- // The package must be on the system image
- PackageParser.Package pkg = getSystemPackageLPr(packageName);
- if (pkg == null) {
- Log.w(TAG, "Unknown package:" + packageName);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- // The package must support runtime permissions
- if (!doesPackageSupportRuntimePermissions(pkg)) {
- Log.w(TAG, "Skipping non supporting runtime permissions package:"
- + packageName);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- packageExceptions = new ArrayList<>();
- outGrantExceptions.put(packageName, packageExceptions);
- }
-
- parsePermission(parser, packageExceptions);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
- }
- }
- }
-
- private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
- outPackageExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (TAG_PERMISSION.contains(parser.getName())) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- if (name == null) {
- Log.w(TAG, "Mandatory name attribute missing for permission tag");
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
-
- DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
- outPackageExceptions.add(exception);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
- }
- }
- }
-
- private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
- return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
- }
-
- private static final class DefaultPermissionGrant {
- final String name;
- final boolean fixed;
-
- public DefaultPermissionGrant(String name, boolean fixed) {
- this.name = name;
- this.fixed = fixed;
- }
- }
-}
diff --git a/com/android/server/pm/DumpState.java b/com/android/server/pm/DumpState.java
new file mode 100644
index 00000000..7ebef83a
--- /dev/null
+++ b/com/android/server/pm/DumpState.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 com.android.server.pm;
+
+public final class DumpState {
+ public static final int DUMP_LIBS = 1 << 0;
+ public static final int DUMP_FEATURES = 1 << 1;
+ public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
+ public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
+ public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
+ public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
+ public static final int DUMP_PERMISSIONS = 1 << 6;
+ public static final int DUMP_PACKAGES = 1 << 7;
+ public static final int DUMP_SHARED_USERS = 1 << 8;
+ public static final int DUMP_MESSAGES = 1 << 9;
+ public static final int DUMP_PROVIDERS = 1 << 10;
+ public static final int DUMP_VERIFIERS = 1 << 11;
+ public static final int DUMP_PREFERRED = 1 << 12;
+ public static final int DUMP_PREFERRED_XML = 1 << 13;
+ public static final int DUMP_KEYSETS = 1 << 14;
+ public static final int DUMP_VERSION = 1 << 15;
+ public static final int DUMP_INSTALLS = 1 << 16;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
+ public static final int DUMP_FROZEN = 1 << 19;
+ public static final int DUMP_DEXOPT = 1 << 20;
+ public static final int DUMP_COMPILER_STATS = 1 << 21;
+ public static final int DUMP_CHANGES = 1 << 22;
+ public static final int DUMP_VOLUMES = 1 << 23;
+
+ public static final int OPTION_SHOW_FILTERS = 1 << 0;
+
+ private int mTypes;
+
+ private int mOptions;
+
+ private boolean mTitlePrinted;
+
+ private SharedUserSetting mSharedUser;
+
+ public boolean isDumping(int type) {
+ if (mTypes == 0 && type != DUMP_PREFERRED_XML) {
+ return true;
+ }
+
+ return (mTypes & type) != 0;
+ }
+
+ public void setDump(int type) {
+ mTypes |= type;
+ }
+
+ public boolean isOptionEnabled(int option) {
+ return (mOptions & option) != 0;
+ }
+
+ public void setOptionEnabled(int option) {
+ mOptions |= option;
+ }
+
+ public boolean onTitlePrinted() {
+ final boolean printed = mTitlePrinted;
+ mTitlePrinted = true;
+ return printed;
+ }
+
+ public boolean getTitlePrinted() {
+ return mTitlePrinted;
+ }
+
+ public void setTitlePrinted(boolean enabled) {
+ mTitlePrinted = enabled;
+ }
+
+ public SharedUserSetting getSharedUser() {
+ return mSharedUser;
+ }
+
+ public void setSharedUser(SharedUserSetting user) {
+ mSharedUser = user;
+ }
+} \ No newline at end of file
diff --git a/com/android/server/pm/InstantAppRegistry.java b/com/android/server/pm/InstantAppRegistry.java
index e1e5b355..c964f912 100644
--- a/com/android/server/pm/InstantAppRegistry.java
+++ b/com/android/server/pm/InstantAppRegistry.java
@@ -49,6 +49,8 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.permission.BasePermission;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -878,8 +880,9 @@ class InstantAppRegistry {
final long identity = Binder.clearCallingIdentity();
try {
for (String grantedPermission : appInfo.getGrantedPermissions()) {
- BasePermission bp = mService.mSettings.mPermissions.get(grantedPermission);
- if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()) {
+ final boolean propagatePermission =
+ mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
+ if (propagatePermission) {
mService.grantRuntimePermission(packageName, grantedPermission, userId);
}
}
diff --git a/com/android/server/pm/KeySetManagerService.java b/com/android/server/pm/KeySetManagerService.java
index 49d3c8bc..35744662 100644
--- a/com/android/server/pm/KeySetManagerService.java
+++ b/com/android/server/pm/KeySetManagerService.java
@@ -565,7 +565,7 @@ public class KeySetManagerService {
}
public void dumpLPr(PrintWriter pw, String packageName,
- PackageManagerService.DumpState dumpState) {
+ DumpState dumpState) {
boolean printedHeader = false;
for (ArrayMap.Entry<String, PackageSetting> e : mPackages.entrySet()) {
String keySetPackage = e.getKey();
diff --git a/com/android/server/pm/PackageDexOptimizer.java b/com/android/server/pm/PackageDexOptimizer.java
index 0f580d81..8ebeeae2 100644
--- a/com/android/server/pm/PackageDexOptimizer.java
+++ b/com/android/server/pm/PackageDexOptimizer.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageParser;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
@@ -103,7 +104,17 @@ public class PackageDexOptimizer {
}
static boolean canOptimizePackage(PackageParser.Package pkg) {
- return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ // We do not dexopt a package with no code.
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
+ return false;
+ }
+
+ // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+ if (pkg.isPrivilegedApp()) {
+ return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+ }
+
+ return true;
}
/**
@@ -354,18 +365,13 @@ public class PackageDexOptimizer {
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- String classLoaderContext;
- if (dexUseInfo.isUnknownClassLoaderContext() ||
- dexUseInfo.isUnsupportedClassLoaderContext() ||
- dexUseInfo.isVariableClassLoaderContext()) {
- // If we have an unknown (not yet set), unsupported (custom class loaders), or a
- // variable class loader chain, compile without a context and mark the oat file with
- // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation.
- // TODO(calin): We should just extract in this case.
- classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
- } else {
- classLoaderContext = dexUseInfo.getClassLoaderContext();
- }
+ // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
+ // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
+ // in isolation (and avoid to extract/verify the main apk if it's in the class path).
+ // Note this trades correctness for performance since the resulting slow down is
+ // unacceptable in some cases until b/64530081 is fixed.
+ String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -425,7 +431,7 @@ public class PackageDexOptimizer {
}
if (useInfo.isUsedByOtherApps(path)) {
- pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+ pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
}
Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
@@ -438,19 +444,10 @@ public class PackageDexOptimizer {
PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
pw.println(dex);
pw.increaseIndent();
- for (String isa : dexUseInfo.getLoaderIsas()) {
- String status = null;
- try {
- status = DexFile.getDexFileStatus(path, isa);
- } catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
- }
- pw.println(isa + ": " + status);
- }
-
+ // TODO(calin): get the status of the oat file (needs installd call)
pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
if (dexUseInfo.isUsedByOtherApps()) {
- pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+ pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
}
pw.decreaseIndent();
}
@@ -474,8 +471,9 @@ public class PackageDexOptimizer {
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
- // If the dex files is used by other apps, we cannot use profile-guided compilation.
- return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If the dex files is used by other apps, apply the shared filter.
+ return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_SHARED);
}
return targetCompilerFilter;
diff --git a/com/android/server/pm/PackageInstallerService.java b/com/android/server/pm/PackageInstallerService.java
index 1fa37b91..09f9cb8c 100644
--- a/com/android/server/pm/PackageInstallerService.java
+++ b/com/android/server/pm/PackageInstallerService.java
@@ -80,6 +80,8 @@ import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.permission.PermissionManagerInternal;
import libcore.io.IoUtils;
@@ -122,6 +124,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private final Context mContext;
private final PackageManagerService mPm;
+ private final PermissionManagerInternal mPermissionManager;
private AppOpsManager mAppOps;
@@ -177,6 +180,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public PackageInstallerService(Context context, PackageManagerService pm) {
mContext = context;
mPm = pm;
+ mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
mInstallThread = new HandlerThread(TAG);
mInstallThread.start();
@@ -243,35 +247,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- public void onSecureContainersAvailable() {
- synchronized (mSessions) {
- final ArraySet<String> unclaimed = new ArraySet<>();
- for (String cid : PackageHelper.getSecureContainerList()) {
- if (isStageName(cid)) {
- unclaimed.add(cid);
- }
- }
-
- // Ignore stages claimed by active sessions
- for (int i = 0; i < mSessions.size(); i++) {
- final PackageInstallerSession session = mSessions.valueAt(i);
- final String cid = session.stageCid;
-
- if (unclaimed.remove(cid)) {
- // Claimed by active session, mount it
- PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID);
- }
- }
-
- // Clean up orphaned staging containers
- for (String cid : unclaimed) {
- Slog.w(TAG, "Deleting orphan container " + cid);
- PackageHelper.destroySdDir(cid);
- }
- }
- }
-
public static boolean isStageName(String name) {
final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
@@ -426,7 +401,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
+ mPermissionManager.enforceCrossUserPermission(
+ callingUid, userId, true, true, "createSession");
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
@@ -671,13 +647,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return "smdl" + sessionId + ".tmp";
}
- static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
- if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID, true) == null) {
- throw new IOException("Failed to create session cid: " + stageCid);
- }
- }
-
@Override
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
@@ -688,7 +657,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "getAllSessions");
final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
@@ -704,7 +674,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "getMySessions");
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
final List<SessionInfo> result = new ArrayList<>();
@@ -726,7 +697,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) throws RemoteException {
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
@@ -775,7 +746,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "registerCallback");
mCallbacks.register(callback, userId);
}
diff --git a/com/android/server/pm/PackageInstallerSession.java b/com/android/server/pm/PackageInstallerSession.java
index ff6e5b3b..d62f0934 100644
--- a/com/android/server/pm/PackageInstallerSession.java
+++ b/com/android/server/pm/PackageInstallerSession.java
@@ -36,7 +36,6 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.internal.util.XmlUtils.writeUriAttribute;
-import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
@@ -481,12 +480,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageDir != null) {
mResolvedStageDir = stageDir;
} else {
- final String path = PackageHelper.getSdDir(stageCid);
- if (path != null) {
- mResolvedStageDir = new File(path);
- } else {
- throw new IOException("Failed to resolve path to container " + stageCid);
- }
+ throw new IOException("Missing stageDir");
}
}
return mResolvedStageDir;
@@ -880,14 +874,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- if (stageCid != null) {
- // Figure out the final installed size and resize the container once
- // and for all. Internally the parser handles straddling between two
- // locations when inheriting.
- final long finalSize = calculateInstalledSize();
- resizeContainer(stageCid, finalSize);
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -924,11 +910,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
- // Container is ready to go, let's seal it up!
- if (stageCid != null) {
- finalizeAndFixContainer(stageCid);
- }
-
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@@ -953,7 +934,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
mRelinquished = true;
- mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
+ mPm.installStage(mPackageName, stageDir, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
@@ -1212,11 +1193,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
splitPaths.toArray(new String[splitPaths.size()]), null);
- final boolean isForwardLocked =
- (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
try {
- return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
+ return PackageHelper.calculateInstalledSize(pkg, params.abiOverride);
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to calculate install size", e);
@@ -1345,52 +1324,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private static void resizeContainer(String cid, long targetSize)
- throws PackageManagerException {
- String path = PackageHelper.getSdDir(cid);
- if (path == null) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to find mounted " + cid);
- }
-
- final long currentSize = new File(path).getTotalSpace();
- if (currentSize > targetSize) {
- Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
- + targetSize + "; skipping resize");
- return;
- }
-
- if (!PackageHelper.unMountSdDir(cid)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to unmount " + cid + " before resize");
- }
-
- if (!PackageHelper.resizeSdDir(targetSize, cid,
- PackageManagerService.getEncryptKey())) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to resize " + cid + " to " + targetSize + " bytes");
- }
-
- path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID, false);
- if (path == null) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to mount " + cid + " after resize");
- }
- }
-
- private void finalizeAndFixContainer(String cid) throws PackageManagerException {
- if (!PackageHelper.finalizeSdDir(cid)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to finalize container " + cid);
- }
-
- if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to fix permissions on container " + cid);
- }
- }
-
void setPermissionsResult(boolean accepted) {
if (!mSealed) {
throw new SecurityException("Must be sealed to accept permissions");
@@ -1419,20 +1352,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!mPrepared) {
if (stageDir != null) {
prepareStageDir(stageDir);
- } else if (stageCid != null) {
- final long identity = Binder.clearCallingIdentity();
- try {
- prepareExternalStageCid(stageCid, params.sizeBytes);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // TODO: deliver more granular progress for ASEC allocation
- mInternalProgress = 0.25f;
- computeProgressLocked(true);
} else {
- throw new IllegalArgumentException(
- "Exactly one of stageDir or stageCid stage must be set");
+ throw new IllegalArgumentException("stageDir must be set");
}
mPrepared = true;
@@ -1534,9 +1455,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (InstallerException ignored) {
}
}
- if (stageCid != null) {
- PackageHelper.destroySdDir(stageCid);
- }
}
void dump(IndentingPrintWriter pw) {
diff --git a/com/android/server/pm/PackageManagerService.java b/com/android/server/pm/PackageManagerService.java
index ff52e0eb..7d1a6470 100644
--- a/com/android/server/pm/PackageManagerService.java
+++ b/com/android/server/pm/PackageManagerService.java
@@ -55,7 +55,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
@@ -103,10 +102,9 @@ import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
import android.Manifest;
@@ -160,10 +158,11 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageStats;
@@ -230,7 +229,6 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
-import android.util.TimingsTraceLog;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
@@ -244,6 +242,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
@@ -283,12 +282,19 @@ import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
+import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
@@ -338,6 +344,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -385,7 +392,7 @@ import java.util.zip.GZIPInputStream;
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
static final String TAG = "PackageManager";
- static final boolean DEBUG_SETTINGS = false;
+ public static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
@@ -396,7 +403,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
- private static final boolean DEBUG_PACKAGE_SCANNING = false;
+ public static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_FILTERS = false;
private static final boolean DEBUG_PERMISSIONS = false;
@@ -427,9 +434,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
- // Cap the size of permission trees that 3rd party apps can define
- private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
-
// Suffix used during package installation when copying/moving
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
@@ -578,8 +582,9 @@ public class PackageManagerService extends IPackageManager.Stub
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
+ public static final int REASON_SHARED = 6;
- public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
+ public static final int REASON_LAST = REASON_SHARED;
/** All dangerous permission names in the same order as the events in MetricsEvent */
private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -654,9 +659,6 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mPackages")
private boolean mDexOptDialogShown;
- /** The location for ASEC container files on internal storage. */
- final String mAsecInternalPath;
-
// Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
@GuardedBy("mInstallLock")
@@ -862,7 +864,7 @@ public class PackageManagerService extends IPackageManager.Stub
String targetPath) {
return getStaticOverlayPaths(targetPackageName, targetPath);
}
- };
+ }
class ParallelPackageParserCallback extends PackageParserCallback {
List<PackageParser.Package> mOverlayPackages = null;
@@ -1004,7 +1006,9 @@ public class PackageManagerService extends IPackageManager.Stub
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
+ // TODO remove this and go through mPermissonManager directly
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
+ private final PermissionManagerInternal mPermissionManager;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -1315,7 +1319,6 @@ public class PackageManagerService extends IPackageManager.Stub
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
- static final int UPDATED_MEDIA_STATUS = 12;
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
@@ -1714,32 +1717,6 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
- case UPDATED_MEDIA_STATUS: {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
- boolean reportStatus = msg.arg1 == 1;
- boolean doGc = msg.arg2 == 1;
- if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc);
- if (doGc) {
- // Force a gc to clear up stale containers.
- Runtime.getRuntime().gc();
- }
- if (msg.obj != null) {
- @SuppressWarnings("unchecked")
- Set<AsecInstallArgs> args = (Set<AsecInstallArgs>) msg.obj;
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
- // Unload containers
- unloadAllContainers(args);
- }
- if (reportStatus) {
- try {
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Invoking StorageManagerService call back");
- PackageHelper.getStorageManager().finishMediaUpdate();
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService not running?");
- }
- }
- } break;
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
@@ -1910,6 +1887,69 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ private PermissionCallback mPermissionCallback = new PermissionCallback() {
+ @Override
+ public void onGidsChanged(int appId, int userId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
+ }
+ });
+ }
+ @Override
+ public void onPermissionGranted(int uid, int userId) {
+ mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+ // Not critical; if this is lost, the application has to request again.
+ synchronized (mPackages) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+ }
+ @Override
+ public void onInstallPermissionGranted() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionRevoked(int uid, int userId) {
+ mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+ synchronized (mPackages) {
+ // Critical; after this call the application should never have the permission
+ mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+ }
+
+ final int appId = UserHandle.getAppId(uid);
+ killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+ }
+ @Override
+ public void onInstallPermissionRevoked() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionUpdated(int userId) {
+ synchronized (mPackages) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+ }
+ @Override
+ public void onInstallPermissionUpdated() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionRemoved() {
+ synchronized (mPackages) {
+ mSettings.writeLPr();
+ }
+ }
+ };
+
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload, String[] grantedPermissions,
boolean launchedForRestore, String installerPackage,
@@ -1926,7 +1966,10 @@ public class PackageManagerService extends IPackageManager.Stub
// review flag which is used to emulate runtime permissions for
// legacy apps.
if (grantPermissions) {
- grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.grantRequestedRuntimePermissions(
+ res.pkg, res.newUsers, grantedPermissions, callingUid,
+ mPermissionCallback);
}
final boolean update = res.removedInfo != null
@@ -1942,9 +1985,9 @@ public class PackageManagerService extends IPackageManager.Stub
// app that had no children, we grant requested runtime permissions to the new
// children if the parent on the system image had them already granted.
if (res.pkg.parentPackage != null) {
- synchronized (mPackages) {
- grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
- }
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(
+ res.pkg, callingUid, mPermissionCallback);
}
synchronized (mPackages) {
@@ -2109,39 +2152,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(
- PackageParser.Package pkg) {
- if (pkg.parentPackage == null) {
- return;
- }
- if (pkg.requestedPermissions == null) {
- return;
- }
- final PackageSetting disabledSysParentPs = mSettings
- .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
- if (disabledSysParentPs == null || disabledSysParentPs.pkg == null
- || !disabledSysParentPs.isPrivileged()
- || (disabledSysParentPs.childPackageNames != null
- && !disabledSysParentPs.childPackageNames.isEmpty())) {
- return;
- }
- final int[] allUserIds = sUserManager.getUserIds();
- final int permCount = pkg.requestedPermissions.size();
- for (int i = 0; i < permCount; i++) {
- String permission = pkg.requestedPermissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
- continue;
- }
- for (int userId : allUserIds) {
- if (disabledSysParentPs.getPermissionsState().hasRuntimePermission(
- permission, userId)) {
- grantRuntimePermission(pkg.packageName, permission, userId);
- }
- }
- }
- }
-
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -2164,14 +2174,6 @@ public class PackageManagerService extends IPackageManager.Stub
unloadPrivatePackages(vol);
}
}
-
- if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isPrimary()) {
- if (vol.state == VolumeInfo.STATE_MOUNTED) {
- updateExternalMediaStatus(true, false);
- } else if (vol.state == VolumeInfo.STATE_EJECTING) {
- updateExternalMediaStatus(false, false);
- }
- }
}
@Override
@@ -2202,58 +2204,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
};
- private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
- String[] grantedPermissions) {
- for (int userId : userIds) {
- grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
- }
- }
-
- private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
- String[] grantedPermissions) {
- PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
- }
-
- PermissionsState permissionsState = ps.getPermissionsState();
-
- final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
- | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-
- final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.M;
-
- final boolean instantApp = isInstantApp(pkg.packageName, userId);
-
- for (String permission : pkg.requestedPermissions) {
- final BasePermission bp;
- synchronized (mPackages) {
- bp = mSettings.mPermissions.get(permission);
- }
- if (bp != null && (bp.isRuntime() || bp.isDevelopment())
- && (!instantApp || bp.isInstant())
- && (supportsRuntimePermissions || !bp.isRuntimeOnly())
- && (grantedPermissions == null
- || ArrayUtils.contains(grantedPermissions, permission))) {
- final int flags = permissionsState.getPermissionFlags(permission, userId);
- if (supportsRuntimePermissions) {
- // Installer cannot change immutable permissions.
- if ((flags & immutableFlags) == 0) {
- grantRuntimePermission(pkg.packageName, permission, userId);
- }
- } else if (mPermissionReviewRequired) {
- // In permission review mode we clear the review flag when we
- // are asked to install the app with all permissions granted.
- if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- updatePermissionFlags(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
- }
- }
- }
- }
- }
-
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.returnCode) {
@@ -2422,7 +2372,29 @@ public class PackageManagerService extends IPackageManager.Stub
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
- mSettings = new Settings(mPackages);
+ mInstaller = installer;
+
+ // Create sub-components that provide services / data. Order here is important.
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ // Expose private service for system components to use.
+ LocalServices.addService(
+ PackageManagerInternal.class, new PackageManagerInternalImpl());
+ sUserManager = new UserManagerService(context, this,
+ new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
+ mPermissionManager = PermissionManagerService.create(context,
+ new DefaultPermissionGrantedCallback() {
+ @Override
+ public void onDefaultRuntimePermissionsGranted(int userId) {
+ synchronized(mPackages) {
+ mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
+ }
+ }
+ }, mPackages /*externalLock*/);
+ mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
+ mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
+ }
+ }
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -2453,7 +2425,6 @@ public class PackageManagerService extends IPackageManager.Stub
mSeparateProcesses = null;
}
- mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
@@ -2482,32 +2453,12 @@ public class PackageManagerService extends IPackageManager.Stub
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
-
- mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
mInstantAppRegistry = new InstantAppRegistry(this);
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
- mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = new UserManagerService(context, this,
- new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
-
- // Propagate permission configuration in to package manager.
- ArrayMap<String, SystemConfig.PermissionEntry> permConfig
- = systemConfig.getPermissions();
- for (int i=0; i<permConfig.size(); i++) {
- SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
- BasePermission bp = mSettings.mPermissions.get(perm.name);
- if (bp == null) {
- bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
- mSettings.mPermissions.put(perm.name, bp);
- }
- if (perm.gids != null) {
- bp.setGids(perm.gids, perm.perUser);
- }
- }
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
@@ -3110,8 +3061,6 @@ public class PackageManagerService extends IPackageManager.Stub
// once we have a booted system.
mInstaller.setWarnIfHeld(mPackages);
- // Expose private service for system components to use.
- LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3314,6 +3263,24 @@ public class PackageManagerService extends IPackageManager.Stub
removeCodePathLI(dstCodePath);
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;
}
@@ -3909,7 +3876,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isPackageAvailable(String packageName, int userId) {
if (!sUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "is package available");
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -3952,7 +3919,7 @@ public class PackageManagerService extends IPackageManager.Stub
int flags, int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
@@ -4214,7 +4181,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
// reader
@@ -4244,7 +4211,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
@@ -4271,116 +4238,23 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
- static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
- if (bp.perm != null) {
- return PackageParser.generatePermissionInfo(bp.perm, flags);
- }
- PermissionInfo pi = new PermissionInfo();
- pi.name = bp.name;
- pi.packageName = bp.sourcePackage;
- pi.nonLocalizedLabel = bp.name;
- pi.protectionLevel = bp.protectionLevel;
- return pi;
- }
-
@Override
public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return null;
- }
- // reader
- synchronized (mPackages) {
- final BasePermission p = mSettings.mPermissions.get(name);
- if (p == null) {
- return null;
- }
- // If the caller is an app that targets pre 26 SDK drop protection flags.
- PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
- if (permissionInfo != null) {
- final int protectionLevel = adjustPermissionProtectionFlagsLPr(
- permissionInfo.protectionLevel, packageName, callingUid);
- if (permissionInfo.protectionLevel != protectionLevel) {
- // If we return different protection level, don't use the cached info
- if (p.perm != null && p.perm.info == permissionInfo) {
- permissionInfo = new PermissionInfo(permissionInfo);
- }
- permissionInfo.protectionLevel = protectionLevel;
- }
- }
- return permissionInfo;
- }
- }
-
- private int adjustPermissionProtectionFlagsLPr(int protectionLevel,
- String packageName, int uid) {
- // Signature permission flags area always reported
- final int protectionLevelMasked = protectionLevel
- & (PermissionInfo.PROTECTION_NORMAL
- | PermissionInfo.PROTECTION_DANGEROUS
- | PermissionInfo.PROTECTION_SIGNATURE);
- if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
- return protectionLevel;
- }
-
- // System sees all flags.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
- || appId == Process.SHELL_UID) {
- return protectionLevel;
- }
-
- // Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName,
- PackageManager.VERSION_CODE_HIGHEST);
-
- // Apps that target O see flags for all protection levels.
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- return protectionLevel;
- }
- if (ps.appId != appId) {
- return protectionLevel;
- }
-
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- return protectionLevel;
- }
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
- return protectionLevelMasked;
- }
-
- return protectionLevel;
+ return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
}
@Override
- public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String group,
+ public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
int flags) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- // reader
+ // TODO Move this to PermissionManager when mPermissionGroups is moved there
synchronized (mPackages) {
- if (group != null && !mPermissionGroups.containsKey(group)) {
+ if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
// This is thrown as NameNotFoundException
return null;
}
-
- ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
- for (BasePermission p : mSettings.mPermissions.values()) {
- if (group == null) {
- if (p.perm == null || p.perm.info.group == null) {
- out.add(generatePermissionInfo(p, flags));
- }
- } else {
- if (p.perm != null && group.equals(p.perm.info.group)) {
- out.add(PackageParser.generatePermissionInfo(p.perm, flags));
- }
- }
- }
- return new ParceledListSlice<>(out);
}
+ return new ParceledListSlice<>(
+ mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()));
}
@Override
@@ -4455,7 +4329,7 @@ public class PackageManagerService extends IPackageManager.Stub
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId, packageName);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get application info");
// writer
@@ -4764,7 +4638,8 @@ public class PackageManagerService extends IPackageManager.Stub
triaged = false;
}
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, false, false,
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
+ Debug.getCallers(5));
} else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
@@ -4893,7 +4768,7 @@ public class PackageManagerService extends IPackageManager.Stub
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -4952,7 +4827,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
@@ -5089,7 +4964,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
@@ -5113,7 +4988,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
@@ -5276,39 +5151,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int checkPermission(String permName, String pkgName, int userId) {
- if (!sUserManager.exists(userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
- final int callingUid = Binder.getCallingUid();
-
- synchronized (mPackages) {
- final PackageParser.Package p = mPackages.get(pkgName);
- if (p != null && p.mExtras != null) {
- final PackageSetting ps = (PackageSetting) p.mExtras;
- if (filterAppAccessLPr(ps, callingUid, userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
- final boolean instantApp = ps.getInstantApp(userId);
- final PermissionsState permissionsState = ps.getPermissionsState();
- if (permissionsState.hasPermission(permName, userId)) {
- if (instantApp) {
- BasePermission bp = mSettings.mPermissions.get(permName);
- if (bp != null && bp.isInstant()) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
- .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- }
-
- return PackageManager.PERMISSION_DENIED;
+ return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
}
@Override
@@ -5339,8 +5182,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
- BasePermission bp = mSettings.mPermissions.get(permName);
- if (bp != null && bp.isInstant()) {
+ if (mPermissionManager.isPermissionInstant(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -5409,448 +5251,49 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /**
- * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
- * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
- * @param checkShell whether to prevent shell from access if there's a debugging restriction
- * @param message the message to log on security exception
- */
- void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
- boolean checkShell, String message) {
- if (userId < 0) {
- throw new IllegalArgumentException("Invalid userId " + userId);
- }
- if (checkShell) {
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- if (userId == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
- if (requireFullPermission) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } else {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } catch (SecurityException se) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, message);
- }
- }
- }
- }
-
- void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
- if (callingUid == Process.SHELL_UID) {
- if (userHandle >= 0
- && sUserManager.hasUserRestriction(restriction, userHandle)) {
- throw new SecurityException("Shell does not have permission to access user "
- + userHandle);
- } else if (userHandle < 0) {
- Slog.e(TAG, "Unable to check shell permission for user " + userHandle + "\n\t"
- + Debug.getCallers(3));
- }
- }
- }
-
- private BasePermission findPermissionTreeLP(String permName) {
- for(BasePermission bp : mSettings.mPermissionTrees.values()) {
- if (permName.startsWith(bp.name) &&
- permName.length() > bp.name.length() &&
- permName.charAt(bp.name.length()) == '.') {
- return bp;
- }
- }
- return null;
- }
-
- private BasePermission checkPermissionTreeLP(String permName) {
- if (permName != null) {
- BasePermission bp = findPermissionTreeLP(permName);
- if (bp != null) {
- if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
- return bp;
- }
- throw new SecurityException("Calling uid "
- + Binder.getCallingUid()
- + " is not allowed to add to permission tree "
- + bp.name + " owned by uid " + bp.uid);
- }
- }
- throw new SecurityException("No permission tree found for " + permName);
- }
-
- static boolean compareStrings(CharSequence s1, CharSequence s2) {
- if (s1 == null) {
- return s2 == null;
- }
- if (s2 == null) {
- return false;
- }
- if (s1.getClass() != s2.getClass()) {
- return false;
- }
- return s1.equals(s2);
- }
-
- static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
- if (pi1.icon != pi2.icon) return false;
- if (pi1.logo != pi2.logo) return false;
- if (pi1.protectionLevel != pi2.protectionLevel) return false;
- if (!compareStrings(pi1.name, pi2.name)) return false;
- if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
- // We'll take care of setting this one.
- if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
- // These are not currently stored in settings.
- //if (!compareStrings(pi1.group, pi2.group)) return false;
- //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
- //if (pi1.labelRes != pi2.labelRes) return false;
- //if (pi1.descriptionRes != pi2.descriptionRes) return false;
- return true;
- }
-
- int permissionInfoFootprint(PermissionInfo info) {
- int size = info.name.length();
- if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
- if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
- return size;
- }
-
- int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
- int size = 0;
- for (BasePermission perm : mSettings.mPermissions.values()) {
- if (perm.uid == tree.uid) {
- size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
- }
- }
- return size;
- }
-
- void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
- // We calculate the max size of permissions defined by this uid and throw
- // if that plus the size of 'info' would exceed our stated maximum.
- if (tree.uid != Process.SYSTEM_UID) {
- final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
- if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
- throw new SecurityException("Permission tree size cap exceeded");
- }
- }
- }
-
- boolean addPermissionLocked(PermissionInfo info, boolean async) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- throw new SecurityException("Instant apps can't add permissions");
- }
- if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
- throw new SecurityException("Label must be specified in permission");
- }
- BasePermission tree = checkPermissionTreeLP(info.name);
- BasePermission bp = mSettings.mPermissions.get(info.name);
- boolean added = bp == null;
- boolean changed = true;
- int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
- if (added) {
- enforcePermissionCapLocked(info, tree);
- bp = new BasePermission(info.name, tree.sourcePackage,
- BasePermission.TYPE_DYNAMIC);
- } else if (bp.type != BasePermission.TYPE_DYNAMIC) {
- throw new SecurityException(
- "Not allowed to modify non-dynamic permission "
- + info.name);
- } else {
- if (bp.protectionLevel == fixedLevel
- && bp.perm.owner.equals(tree.perm.owner)
- && bp.uid == tree.uid
- && comparePermissionInfos(bp.perm.info, info)) {
- changed = false;
- }
- }
- bp.protectionLevel = fixedLevel;
- info = new PermissionInfo(info);
- info.protectionLevel = fixedLevel;
- bp.perm = new PackageParser.Permission(tree.perm.owner, info);
- bp.perm.info.packageName = tree.perm.info.packageName;
- bp.uid = tree.uid;
- if (added) {
- mSettings.mPermissions.put(info.name, bp);
- }
- if (changed) {
- if (!async) {
- mSettings.writeLPr();
- } else {
- scheduleWriteSettingsLocked();
- }
- }
- return added;
+ boolean addPermission(PermissionInfo info, final boolean async) {
+ return mPermissionManager.addPermission(
+ info, async, getCallingUid(), new PermissionCallback() {
+ @Override
+ public void onPermissionChanged() {
+ if (!async) {
+ mSettings.writeLPr();
+ } else {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ });
}
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
- return addPermissionLocked(info, false);
+ return addPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
- return addPermissionLocked(info, true);
+ return addPermission(info, true);
}
}
@Override
- public void removePermission(String name) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- throw new SecurityException("Instant applications don't have access to this method");
- }
- synchronized (mPackages) {
- checkPermissionTreeLP(name);
- BasePermission bp = mSettings.mPermissions.get(name);
- if (bp != null) {
- if (bp.type != BasePermission.TYPE_DYNAMIC) {
- throw new SecurityException(
- "Not allowed to modify non-dynamic permission "
- + name);
- }
- mSettings.mPermissions.remove(name);
- mSettings.writeLPr();
- }
- }
- }
-
- private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(
- PackageParser.Package pkg, BasePermission bp) {
- int index = pkg.requestedPermissions.indexOf(bp.name);
- if (index == -1) {
- throw new SecurityException("Package " + pkg.packageName
- + " has not requested permission " + bp.name);
- }
- if (!bp.isRuntime() && !bp.isDevelopment()) {
- throw new SecurityException("Permission " + bp.name
- + " is not a changeable permission type");
- }
+ public void removePermission(String permName) {
+ mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
- public void grantRuntimePermission(String packageName, String name, final int userId) {
- grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
- }
-
- private void grantRuntimePermission(String packageName, String name, final int userId,
- boolean overridePolicy) {
- if (!sUserManager.exists(userId)) {
- Log.e(TAG, "No such user:" + userId);
- return;
- }
- final int callingUid = Binder.getCallingUid();
-
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
- "grantRuntimePermission");
-
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "grantRuntimePermission");
-
- final int uid;
- final PackageSetting ps;
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
- ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
-
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
- && bp.isRuntime()) {
- return;
- }
-
- uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
-
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- final int flags = permissionsState.getPermissionFlags(name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot grant system fixed permission "
- + name + " for package " + packageName);
- }
- if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- throw new SecurityException("Cannot grant policy fixed permission "
- + name + " for package " + packageName);
- }
-
- if (bp.isDevelopment()) {
- // Development permissions must be handled specially, since they are not
- // normal runtime permissions. For now they apply to all users.
- if (permissionsState.grantInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- scheduleWriteSettingsLocked();
- }
- return;
- }
-
- if (ps.getInstantApp(userId) && !bp.isInstant()) {
- throw new SecurityException("Cannot grant non-ephemeral permission"
- + name + " for package " + packageName);
- }
-
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
- return;
- }
-
- final int result = permissionsState.grantRuntimePermission(bp, userId);
- switch (result) {
- case PermissionsState.PERMISSION_OPERATION_FAILURE: {
- return;
- }
-
- case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
- final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
- }
- });
- }
- break;
- }
-
- if (bp.isRuntime()) {
- logPermissionGranted(mContext, name, packageName);
- }
-
- mOnPermissionChangeListeners.onPermissionsChanged(uid);
-
- // Not critical if that is lost - app has to request again.
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
- }
-
- // Only need to do this if user is initialized. Otherwise it's a new user
- // and there are no processes running as the user yet and there's no need
- // to make an expensive call to remount processes for the changed permissions.
- if (READ_EXTERNAL_STORAGE.equals(name)
- || WRITE_EXTERNAL_STORAGE.equals(name)) {
- final long token = Binder.clearCallingIdentity();
- try {
- if (sUserManager.isInitialized(userId)) {
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
+ public void grantRuntimePermission(String packageName, String permName, final int userId) {
+ mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
+ getCallingUid(), userId, mPermissionCallback);
}
@Override
- public void revokeRuntimePermission(String packageName, String name, int userId) {
- revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
- }
-
- private void revokeRuntimePermission(String packageName, String name, int userId,
- boolean overridePolicy) {
- if (!sUserManager.exists(userId)) {
- Log.e(TAG, "No such user:" + userId);
- return;
- }
-
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
- "revokeRuntimePermission");
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, true /* checkShell */,
- "revokeRuntimePermission");
-
- final int appId;
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
-
- enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
-
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
- && bp.isRuntime()) {
- return;
- }
-
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- final int flags = permissionsState.getPermissionFlags(name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot revoke system fixed permission "
- + name + " for package " + packageName);
- }
- if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- throw new SecurityException("Cannot revoke policy fixed permission "
- + name + " for package " + packageName);
- }
-
- if (bp.isDevelopment()) {
- // Development permissions must be handled specially, since they are not
- // normal runtime permissions. For now they apply to all users.
- if (permissionsState.revokeInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- scheduleWriteSettingsLocked();
- }
- return;
- }
-
- if (permissionsState.revokeRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- return;
- }
-
- if (bp.isRuntime()) {
- logPermissionRevoked(mContext, name, packageName);
- }
-
- mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
-
- // Critical, after this call app should never have the permission.
- mSettings.writeRuntimePermissionsForUserLPr(userId, true);
-
- appId = UserHandle.getAppId(pkg.applicationInfo.uid);
- }
-
- killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+ public void revokeRuntimePermission(String packageName, String permName, int userId) {
+ mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/,
+ getCallingUid(), userId, mPermissionCallback);
}
/**
@@ -5943,91 +5386,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public int getPermissionFlags(String name, String packageName, int userId) {
- if (!sUserManager.exists(userId)) {
- return 0;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
-
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getPermissionFlags");
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- return 0;
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- return 0;
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- return 0;
- }
- PermissionsState permissionsState = ps.getPermissionsState();
- return permissionsState.getPermissionFlags(name, userId);
- }
+ public int getPermissionFlags(String permName, String packageName, int userId) {
+ return mPermissionManager.getPermissionFlags(permName, packageName, getCallingUid(), userId);
}
@Override
- public void updatePermissionFlags(String name, String packageName, int flagMask,
+ public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int userId) {
- if (!sUserManager.exists(userId)) {
- return;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
-
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "updatePermissionFlags");
-
- // Only the system can change these flags and nothing else.
- if (getCallingUid() != Process.SYSTEM_UID) {
- flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- }
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
-
- PermissionsState permissionsState = ps.getPermissionsState();
-
- boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
-
- if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
- // Install and runtime permissions are stored in different places,
- // so figure out what permission changed and persist the change.
- if (permissionsState.getInstallPermissionState(name) != null) {
- scheduleWriteSettingsLocked();
- } else if (permissionsState.getRuntimePermissionState(name, userId) != null
- || hadState) {
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
- }
- }
- }
+ mPermissionManager.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, getCallingUid(), userId,
+ mPermissionCallback);
}
/**
@@ -6036,52 +5404,16 @@ public class PackageManagerService extends IPackageManager.Stub
*/
@Override
public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) {
- if (!sUserManager.exists(userId)) {
- return;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps");
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, true /* checkShell */,
- "updatePermissionFlagsForAllApps");
-
- // Only the system can change system fixed flags.
- if (getCallingUid() != Process.SYSTEM_UID) {
- flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- }
-
synchronized (mPackages) {
- boolean changed = false;
- final int packageCount = mPackages.size();
- for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) {
- final PackageParser.Package pkg = mPackages.valueAt(pkgIndex);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- continue;
- }
- PermissionsState permissionsState = ps.getPermissionsState();
- changed |= permissionsState.updatePermissionFlagsForAllPermissions(
- userId, flagMask, flagValues);
- }
+ final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps(
+ flagMask, flagValues, getCallingUid(), userId, mPackages.values(),
+ mPermissionCallback);
if (changed) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
- private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
- != PackageManager.PERMISSION_GRANTED
- && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(message + " requires "
- + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
- + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
- }
- }
-
@Override
public boolean shouldShowRequestPermissionRationale(String permissionName,
String packageName, int userId) {
@@ -6273,7 +5605,7 @@ public class PackageManagerService extends IPackageManager.Stub
* <br />
* {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
*/
- static int compareSignatures(Signature[] s1, Signature[] s2) {
+ public static int compareSignatures(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -6627,9 +5959,14 @@ public class PackageManagerService extends IPackageManager.Stub
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(
- intent, resolvedType, flags, userId, false /*includeInstantApps*/);
+ intent, resolvedType, flags, userId, false /*resolveForStart*/);
}
+ /**
+ * Normally instant apps can only be resolved when they're visible to the caller.
+ * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+ * since we need to allow the system to start any installed application.
+ */
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
int flags, int userId, boolean resolveForStart) {
try {
@@ -6638,7 +5975,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
@@ -7219,7 +6556,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean resolveForStart, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
@@ -7974,7 +7311,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
final String resultsAction = intent.getAction();
@@ -8155,7 +7492,7 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8267,7 +7604,7 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8507,7 +7844,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get installed packages");
@@ -8594,7 +7931,7 @@ public class PackageManagerService extends IPackageManager.Stub
String[] permissions, int flags, int userId) {
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, permissions);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
@@ -8699,7 +8036,7 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getEphemeralApplications");
synchronized (mPackages) {
@@ -8714,7 +8051,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isInstantApp(String packageName, int userId) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
@@ -8747,7 +8084,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8765,7 +8102,7 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"setInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8787,7 +8124,7 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppIcon");
@@ -8847,6 +8184,10 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ return resolveContentProviderInternal(name, flags, userId);
+ }
+
+ private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, name);
final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
@@ -9105,7 +8446,7 @@ public class PackageManagerService extends IPackageManager.Stub
return fname;
}
- static void reportSettingsProblem(int priority, String msg) {
+ public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
@@ -9738,7 +9079,7 @@ public class PackageManagerService extends IPackageManager.Stub
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
- String compilerFilter, boolean bootComplete) {
+ final String compilerFilter, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
@@ -9749,6 +9090,8 @@ public class PackageManagerService extends IPackageManager.Stub
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
+ boolean useProfileForDexopt = false;
+
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
@@ -9762,11 +9105,30 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.applicationInfo.uid, pkg.packageName)) {
Log.e(TAG, "Installer failed to copy system profile!");
+ } else {
+ // Disabled as this causes speed-profile compilation during first boot
+ // even if things are already compiled.
+ // useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
+ } else {
+ PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+ // Handle compressed APKs in this path. Only do this for stubs with profiles to
+ // minimize the number off apps being speed-profile compiled during first boot.
+ // The other paths will not change the filter.
+ if (disabledPs != null && disabledPs.pkg.isStub) {
+ // The package is the stub one, remove the stub suffix to get the normal
+ // package and APK names.
+ String systemProfilePath =
+ getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
+ 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();
+ }
}
}
@@ -9795,17 +9157,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // If the OTA updates a system app which was previously preopted to a non-preopted state
- // the app might end up being verified at runtime. That's because by default the apps
- // are verify-profile but for preopted apps there's no profile.
- // Do a hacky check to ensure that if we have no profiles (a reasonable indication
- // that before the OTA the app was preopted) the app gets compiled with a non-profile
- // filter (by default 'quicken').
- // Note that at this stage unused apps are already filtered.
- if (isSystemApp(pkg) &&
- DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
- !Environment.getReferenceProfile(pkg.packageName).exists()) {
- compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+ String pkgCompilerFilter = compilerFilter;
+ if (useProfileForDexopt) {
+ // Use background dexopt mode to try and use the profile. Note that this does not
+ // guarantee usage of the profile.
+ pkgCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
}
// checkProfiles is false to avoid merging profiles during boot which
@@ -9816,22 +9174,9 @@ public class PackageManagerService extends IPackageManager.Stub
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
- compilerFilter,
+ pkgCompilerFilter,
dexoptFlags));
- if (pkg.isSystemApp()) {
- // Only dexopt shared secondary dex files belonging to system apps to not slow down
- // too much boot after an OTA.
- int secondaryDexoptFlags = dexoptFlags |
- DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
- DexoptOptions.DEXOPT_ONLY_SHARED_DEX;
- mDexManager.dexoptSecondaryDex(new DexoptOptions(
- pkg.packageName,
- compilerFilter,
- secondaryDexoptFlags));
- }
-
- // TODO(shubhamajmera): Record secondary dexopt stats.
switch (primaryDexOptStaus) {
case PackageDexOptimizer.DEX_OPT_PERFORMED:
numberOfPackagesOptimized++;
@@ -10388,7 +9733,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+ private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
+ SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
usesLibraryFiles.add(file.path);
@@ -10417,7 +9763,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkg == null) {
return;
}
- ArraySet<String> usesLibraryFiles = null;
+ // The collection used here must maintain the order of addition (so
+ // that libraries are searched in the correct order) and must have no
+ // duplicates.
+ Set<String> usesLibraryFiles = null;
if (pkg.usesLibraries != null) {
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
null, null, pkg.packageName, changingLib, true,
@@ -10441,10 +9790,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+ private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
@Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, @Nullable PackageParser.Package changingLib,
- boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+ boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
@@ -10510,7 +9859,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (outUsedLibraries == null) {
- outUsedLibraries = new ArraySet<>();
+ // Use LinkedHashSet to preserve the order of files added to
+ // usesLibraryFiles while eliminating duplicates.
+ outUsedLibraries = new LinkedHashSet<>();
}
addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
}
@@ -10703,6 +10054,12 @@ public class PackageManagerService extends IPackageManager.Stub
assertPackageIsValid(pkg, policyFlags, scanFlags);
+ if (Build.IS_DEBUGGABLE &&
+ pkg.isPrivilegedApp() &&
+ !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
+ }
+
// Initialize package source and resource directories
final File scanFile = new File(pkg.codePath);
final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
@@ -11866,86 +11223,27 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- ArrayMap<String, BasePermission> permissionMap =
- p.tree ? mSettings.mPermissionTrees
- : mSettings.mPermissions;
- BasePermission bp = permissionMap.get(p.info.name);
-
- // Allow system apps to redefine non-system permissions
- if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {
- final boolean currentOwnerIsSystem = (bp.perm != null
- && isSystemApp(bp.perm.owner));
- if (isSystemApp(p.owner)) {
- if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
- // It's a built-in permission and no owner, take ownership now
- bp.packageSetting = pkgSetting;
- bp.perm = p;
- bp.uid = pkg.applicationInfo.uid;
- bp.sourcePackage = p.info.packageName;
- p.info.flags |= PermissionInfo.FLAG_INSTALLED;
- } else if (!currentOwnerIsSystem) {
- String msg = "New decl " + p.owner + " of permission "
- + p.info.name + " is system; overriding " + bp.sourcePackage;
- reportSettingsProblem(Log.WARN, msg);
- bp = null;
- }
- }
- }
-
- if (bp == null) {
- bp = new BasePermission(p.info.name, p.info.packageName,
- BasePermission.TYPE_NORMAL);
+ // 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);
- }
-
- if (bp.perm == null) {
- if (bp.sourcePackage == null
- || bp.sourcePackage.equals(p.info.packageName)) {
- BasePermission tree = findPermissionTreeLP(p.info.name);
- if (tree == null
- || tree.sourcePackage.equals(p.info.packageName)) {
- bp.packageSetting = pkgSetting;
- bp.perm = p;
- bp.uid = pkg.applicationInfo.uid;
- bp.sourcePackage = p.info.packageName;
- p.info.flags |= PermissionInfo.FLAG_INSTALLED;
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
- } else {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " ignored: base tree "
- + tree.name + " is from package "
- + tree.sourcePackage);
- }
- } else {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " ignored: original from "
- + bp.sourcePackage);
- }
- } else if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append("DUP:");
- r.append(p.info.name);
- }
- if (bp.perm == p) {
- bp.protectionLevel = p.info.protectionLevel;
+ } else {
+ final BasePermission bp = BasePermission.createOrUpdate(
+ (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
+ p, pkg, mSettings.mPermissionTrees, chatty);
+ mPermissionManager.putPermissionTEMP(p.info.name, bp);
}
}
- if (r != null) {
- if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r);
- }
-
N = pkg.instrumentation.size();
r = null;
for (i=0; i<N; i++) {
@@ -12673,12 +11971,12 @@ public class PackageManagerService extends IPackageManager.Stub
r = null;
for (i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(p.info.name);
+ BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
if (bp == null) {
bp = mSettings.mPermissionTrees.get(p.info.name);
}
- if (bp != null && bp.perm == p) {
- bp.perm = null;
+ if (bp != null && bp.isPermission(p)) {
+ bp.setPermission(null);
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -12703,8 +12001,7 @@ public class PackageManagerService extends IPackageManager.Stub
r = null;
for (i=0; i<N; i++) {
String perm = pkg.requestedPermissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(perm);
- if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ if (mPermissionManager.isPermissionAppOp(perm)) {
ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
if (appOpPkgs != null) {
appOpPkgs.remove(pkg.packageName);
@@ -12809,23 +12106,32 @@ public class PackageManagerService extends IPackageManager.Stub
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
+ // have package settings, we should make note of it elsewhere [map between
+ // source package name and BasePermission] and cycle through that here. Then we
+ // define a single method on BasePermission that takes a PackageSetting, changing
+ // package name and a package.
+ // NOTE: With this approach, we also don't need to tree trees differently than
+ // normal permissions. Today, we need two separate loops because these BasePermission
+ // objects are stored separately.
// Make sure there are no dangling permission trees.
Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
final BasePermission bp = it.next();
- if (bp.packageSetting == null) {
+ if (bp.getSourcePackageSetting() == null) {
// We may not yet have parsed the package, so just see if
// we still know about its settings.
- bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+ bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
}
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
+ 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.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
+ } 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();
}
@@ -12834,40 +12140,28 @@ public class PackageManagerService extends IPackageManager.Stub
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
- it = mSettings.mPermissions.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
- + bp.name + " pkg=" + bp.sourcePackage
- + " info=" + bp.pendingInfo);
- if (bp.packageSetting == null && bp.pendingInfo != null) {
- final BasePermission tree = findPermissionTreeLP(bp.name);
- if (tree != null && tree.perm != null) {
- bp.packageSetting = tree.packageSetting;
- bp.perm = new PackageParser.Permission(tree.perm.owner,
- new PermissionInfo(bp.pendingInfo));
- bp.perm.info.packageName = tree.perm.info.packageName;
- bp.perm.info.name = bp.name;
- bp.uid = tree.uid;
- }
- }
- }
- if (bp.packageSetting == null) {
+ 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.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
- }
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission: " + bp.name
- + " from package " + bp.sourcePackage);
- it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission: " + bp.name
- + " from package " + bp.sourcePackage);
+ 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;
- it.remove();
+ permissionIter.remove();
}
}
}
@@ -12936,8 +12230,9 @@ public class PackageManagerService extends IPackageManager.Stub
// the runtime ones are written only if changed. The only cases of
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
- changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw(
- ps.sharedUser, UserManagerService.getInstance().getUserIds());
+ changedRuntimePermissionUserIds =
+ mPermissionManager.revokeUnusedSharedUserPermissions(
+ ps.sharedUser, UserManagerService.getInstance().getUserIds());
if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
runtimePermissionsRevoked = true;
}
@@ -12949,7 +12244,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
- final BasePermission bp = mSettings.mPermissions.get(name);
+ final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(name);
final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
@@ -12957,7 +12252,7 @@ public class PackageManagerService extends IPackageManager.Stub
Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
- if (bp == null || bp.packageSetting == null) {
+ if (bp == null || bp.getSourcePackageSetting() == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + name
@@ -12971,7 +12266,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
+ Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() + " for package "
+ pkg.packageName);
}
continue;
@@ -12979,64 +12274,57 @@ public class PackageManagerService extends IPackageManager.Stub
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName() + " for package "
+ pkg.packageName);
}
continue;
}
- final String perm = bp.name;
+ final String perm = bp.getName();
boolean allowedSig = false;
int grant = GRANT_DENIED;
// Keep track of app op permissions.
- if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
+ if (bp.isAppOp()) {
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
if (pkgs == null) {
pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(bp.name, pkgs);
+ mAppOpPermissionPackages.put(perm, pkgs);
}
pkgs.add(pkg.packageName);
}
- final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- switch (level) {
- case PermissionInfo.PROTECTION_NORMAL: {
- // For all apps normal permissions are install time ones.
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
+ // For legacy apps dangerous permissions are install time ones.
grant = GRANT_INSTALL;
- } break;
-
- case PermissionInfo.PROTECTION_DANGEROUS: {
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
- // For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (origPermissions.hasInstallPermission(bp.name)) {
- // For legacy apps that became modern, install becomes runtime.
- grant = GRANT_UPGRADE;
- } else if (mPromoteSystemApps
- && isSystemApp(ps)
- && mExistingSystemPackages.contains(ps.name)) {
- // For legacy system apps, install becomes runtime.
- // We cannot check hasInstallPermission() for system apps since those
- // permissions were granted implicitly and not persisted pre-M.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
- }
- } break;
-
- case PermissionInfo.PROTECTION_SIGNATURE: {
- // For all apps signature permissions are install time ones.
- allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowedSig) {
- grant = GRANT_INSTALL;
- }
- } break;
+ } else if (origPermissions.hasInstallPermission(bp.getName())) {
+ // For legacy apps that became modern, install becomes runtime.
+ grant = GRANT_UPGRADE;
+ } else if (mPromoteSystemApps
+ && isSystemApp(ps)
+ && mExistingSystemPackages.contains(ps.name)) {
+ // For legacy system apps, install becomes runtime.
+ // We cannot check hasInstallPermission() for system apps since those
+ // permissions were granted implicitly and not persisted pre-M.
+ grant = GRANT_UPGRADE;
+ } else {
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
}
if (DEBUG_PERMISSIONS) {
@@ -13065,7 +12353,7 @@ public class PackageManagerService extends IPackageManager.Stub
// for legacy apps
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (origPermissions.getRuntimePermissionState(
- bp.name, userId) != null) {
+ perm, userId) != null) {
// Revoke the runtime permission and clear the flags.
origPermissions.revokeRuntimePermission(bp, userId);
origPermissions.updatePermissionFlags(bp, userId,
@@ -13086,10 +12374,10 @@ public class PackageManagerService extends IPackageManager.Stub
// Grant previously granted runtime permissions.
for (int userId : UserManagerService.getInstance().getUserIds()) {
PermissionState permissionState = origPermissions
- .getRuntimePermissionState(bp.name, userId);
+ .getRuntimePermissionState(perm, userId);
int flags = permissionState != null
? permissionState.getFlags() : 0;
- if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+ if (origPermissions.hasRuntimePermission(perm, userId)) {
// Don't propagate the permission in a permission review mode if
// the former was revoked, i.e. marked to not propagate on upgrade.
// Note that in a permission review mode install permissions are
@@ -13132,7 +12420,7 @@ public class PackageManagerService extends IPackageManager.Stub
// permissions as these are the only ones the platform knows
// how to disable the API to simulate revocation as legacy
// apps don't expect to run with revoked permissions.
- if (PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage)) {
+ if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
// We changed the flags, hence have to write.
@@ -13155,7 +12443,7 @@ public class PackageManagerService extends IPackageManager.Stub
case GRANT_UPGRADE: {
// Grant runtime permissions for a previously held install permission.
PermissionState permissionState = origPermissions
- .getInstallPermissionState(bp.name);
+ .getInstallPermissionState(perm);
final int flags = permissionState != null ? permissionState.getFlags() : 0;
if (origPermissions.revokeInstallPermission(bp)
@@ -13203,10 +12491,10 @@ public class PackageManagerService extends IPackageManager.Stub
changedInstallPermission = true;
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
- } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
+ } else if (bp.isAppOp()) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
if (DEBUG_PERMISSIONS
@@ -13214,7 +12502,7 @@ public class PackageManagerService extends IPackageManager.Stub
|| packageOfInterest.equals(pkg.packageName))) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
}
@@ -13274,13 +12562,11 @@ public class PackageManagerService extends IPackageManager.Stub
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
BasePermission bp, PermissionsState origPermissions) {
- boolean oemPermission = (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
- boolean privilegedPermission = (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ boolean oemPermission = bp.isOEM();
+ boolean privilegedPermission = bp.isPrivileged();
boolean privappPermissionsDisable =
RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
- boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage);
+ boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivilegedApp()
&& !platformPackage && platformPermission) {
@@ -13309,7 +12595,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
boolean allowed = (compareSignatures(
- bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
+ bp.getSourcePackageSetting().signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
@@ -13386,39 +12672,37 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (!allowed) {
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PRE23) != 0
+ if (!allowed
+ && bp.isPre23()
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
// If this was a previously normal/dangerous permission that got moved
// to a system permission as part of the runtime permission redesign, then
// we still want to blindly grant it to old apps.
allowed = true;
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0
+ if (!allowed && bp.isInstaller()
&& pkg.packageName.equals(mRequiredInstallerPackage)) {
// If this permission is to be granted to the system installer and
// this app is an installer, then it gets the permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0
+ if (!allowed && bp.isVerifier()
&& pkg.packageName.equals(mRequiredVerifierPackage)) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0
+ if (!allowed && bp.isPreInstalled()
&& isSystemApp(pkg)) {
// Any pre-installed system app is allowed to get this permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+ if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
allowed = origPermissions.hasInstallPermission(perm);
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0
+ if (!allowed && bp.isSetup()
&& pkg.packageName.equals(mSetupWizardPackage)) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
@@ -14719,7 +14003,7 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -14847,7 +14131,7 @@ public class PackageManagerService extends IPackageManager.Stub
return installReason;
}
- void installStage(String packageName, File stagedDir, String stagedCid,
+ void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
@@ -14860,12 +14144,7 @@ public class PackageManagerService extends IPackageManager.Stub
sessionParams.originatingUri, sessionParams.referrerUri,
sessionParams.originatingUid, installerUid);
- final OriginInfo origin;
- if (stagedDir != null) {
- origin = OriginInfo.fromStagedFile(stagedDir);
- } else {
- origin = OriginInfo.fromStagedContainer(stagedCid);
- }
+ final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
@@ -14963,7 +14242,7 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setApplicationHiddenSetting for user " + userId);
@@ -15065,7 +14344,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"getApplicationHidden for user " + userId);
PackageSetting ps;
@@ -15097,7 +14376,7 @@ public class PackageManagerService extends IPackageManager.Stub
null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"installExistingPackage for user " + userId);
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -15202,7 +14481,7 @@ public class PackageManagerService extends IPackageManager.Stub
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setPackagesSuspended for user " + userId);
@@ -15263,7 +14542,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isPackageSuspendedForUser for user " + userId);
synchronized (mPackages) {
@@ -15710,7 +14989,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mPackages) {
boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
if (packageName != null) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowserLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(
packageName, userId);
}
return result;
@@ -16065,7 +15344,6 @@ public class PackageManagerService extends IPackageManager.Stub
* file, or a cluster directory. This location may be untrusted.
*/
final File file;
- final String cid;
/**
* Flag indicating that {@link #file} or {@link #cid} has already been
@@ -16084,35 +15362,27 @@ public class PackageManagerService extends IPackageManager.Stub
final File resolvedFile;
static OriginInfo fromNothing() {
- return new OriginInfo(null, null, false, false);
+ return new OriginInfo(null, false, false);
}
static OriginInfo fromUntrustedFile(File file) {
- return new OriginInfo(file, null, false, false);
+ return new OriginInfo(file, false, false);
}
static OriginInfo fromExistingFile(File file) {
- return new OriginInfo(file, null, false, true);
+ return new OriginInfo(file, false, true);
}
static OriginInfo fromStagedFile(File file) {
- return new OriginInfo(file, null, true, false);
+ return new OriginInfo(file, true, false);
}
- static OriginInfo fromStagedContainer(String cid) {
- return new OriginInfo(null, cid, true, false);
- }
-
- private OriginInfo(File file, String cid, boolean staged, boolean existing) {
+ private OriginInfo(File file, boolean staged, boolean existing) {
this.file = file;
- this.cid = cid;
this.staged = staged;
this.existing = existing;
- if (cid != null) {
- resolvedPath = PackageHelper.getSdDir(cid);
- resolvedFile = new File(resolvedPath);
- } else if (file != null) {
+ if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
@@ -16205,7 +15475,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String toString() {
return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
- + " file=" + origin.file + " cid=" + origin.cid + "}";
+ + " file=" + origin.file + "}";
}
private int installLocationPolicy(PackageInfoLite pkgLite) {
@@ -16316,9 +15586,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
- } else if (origin.cid != null) {
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
@@ -16356,7 +15623,7 @@ public class PackageManagerService extends IPackageManager.Stub
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
- origin.resolvedPath, isForwardLocked(), packageAbiOverride);
+ origin.resolvedPath, packageAbiOverride);
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
@@ -16593,43 +15860,11 @@ public class PackageManagerService extends IPackageManager.Stub
mArgs = createInstallArgs(this);
mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
-
- public boolean isForwardLocked() {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- }
- }
-
- /**
- * Used during creation of InstallArgs
- *
- * @param installFlags package installation flags
- * @return true if should be installed on external storage
- */
- private static boolean installOnExternalAsec(int installFlags) {
- if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- return false;
- }
- if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- return true;
- }
- return false;
- }
-
- /**
- * Used during creation of InstallArgs
- *
- * @param installFlags package installation flags
- * @return true if should be installed as forward locked
- */
- private static boolean installForwardLocked(int installFlags) {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
}
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
- } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
- return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
@@ -16641,27 +15876,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
String resourcePath, String[] instructionSets) {
- final boolean isInAsec;
- if (installOnExternalAsec(installFlags)) {
- /* Apps on SD card are always in ASEC containers. */
- isInAsec = true;
- } else if (installForwardLocked(installFlags)
- && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {
- /*
- * Forward-locked apps are only in ASEC containers if they're the
- * new style
- */
- isInAsec = true;
- } else {
- isInAsec = false;
- }
-
- if (isInAsec) {
- return new AsecInstallArgs(codePath, instructionSets,
- installOnExternalAsec(installFlags), installForwardLocked(installFlags));
- } else {
- return new FileInstallArgs(codePath, resourcePath, instructionSets);
- }
+ return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
static abstract class InstallArgs {
@@ -16995,11 +16210,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private boolean isAsecExternal(String cid) {
- final String asecPath = PackageHelper.getSdFilesystem(cid);
- return !asecPath.startsWith(mAsecInternalPath);
- }
-
private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
PackageManagerException {
if (copyRet < 0) {
@@ -17022,308 +16232,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
- * Logic to handle installation of ASEC applications, including copying and
- * renaming logic.
- */
- class AsecInstallArgs extends InstallArgs {
- static final String RES_FILE_NAME = "pkg.apk";
- static final String PUBLIC_RES_FILE_NAME = "res.zip";
-
- String cid;
- String packagePath;
- String resourcePath;
-
- /** New install */
- AsecInstallArgs(InstallParams params) {
- super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid,
- params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates,
- params.installReason);
- }
-
- /** Existing install */
- AsecInstallArgs(String fullCodePath, String[] instructionSets,
- boolean isExternal, boolean isForwardLocked) {
- super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
- // Hackily pretend we're still looking at a full code path
- if (!fullCodePath.endsWith(RES_FILE_NAME)) {
- fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
- }
-
- // Extract cid from fullCodePath
- int eidx = fullCodePath.lastIndexOf("/");
- String subStr1 = fullCodePath.substring(0, eidx);
- int sidx = subStr1.lastIndexOf("/");
- cid = subStr1.substring(sidx+1, eidx);
- setMountPath(subStr1);
- }
-
- AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
- super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
- this.cid = cid;
- setMountPath(PackageHelper.getSdDir(cid));
- }
-
- void createCopyFile() {
- cid = mInstallerService.allocateExternalStageCidLegacy();
- }
-
- int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
- if (origin.staged && origin.cid != null) {
- if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
- cid = origin.cid;
- setMountPath(PackageHelper.getSdDir(cid));
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- if (temp) {
- createCopyFile();
- } else {
- /*
- * Pre-emptively destroy the container since it's destroyed if
- * copying fails due to it existing anyway.
- */
- PackageHelper.destroySdDir(cid);
- }
-
- final String newMountPath = imcs.copyPackageToContainer(
- origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternalAsec(),
- isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
-
- if (newMountPath != null) {
- setMountPath(newMountPath);
- return PackageManager.INSTALL_SUCCEEDED;
- } else {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- @Override
- String getCodePath() {
- return packagePath;
- }
-
- @Override
- String getResourcePath() {
- return resourcePath;
- }
-
- int doPreInstall(int status) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- // Destroy container
- PackageHelper.destroySdDir(cid);
- } else {
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (!mounted) {
- String newMountPath = PackageHelper.mountSdDir(cid, getEncryptKey(),
- Process.SYSTEM_UID);
- if (newMountPath != null) {
- setMountPath(newMountPath);
- } else {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
- }
- return status;
- }
-
- boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
- String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME);
- String newMountPath = null;
- if (PackageHelper.isContainerMounted(cid)) {
- // Unmount the container
- if (!PackageHelper.unMountSdDir(cid)) {
- Slog.i(TAG, "Failed to unmount " + cid + " before renaming");
- return false;
- }
- }
- if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId +
- " which might be stale. Will try to clean up.");
- // Clean up the stale container and proceed to recreate.
- if (!PackageHelper.destroySdDir(newCacheId)) {
- Slog.e(TAG, "Very strange. Cannot clean up stale container " + newCacheId);
- return false;
- }
- // Successfully cleaned up stale container. Try to rename again.
- if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId
- + " inspite of cleaning it up.");
- return false;
- }
- }
- if (!PackageHelper.isContainerMounted(newCacheId)) {
- Slog.w(TAG, "Mounting container " + newCacheId);
- newMountPath = PackageHelper.mountSdDir(newCacheId,
- getEncryptKey(), Process.SYSTEM_UID);
- } else {
- newMountPath = PackageHelper.getSdDir(newCacheId);
- }
- if (newMountPath == null) {
- Slog.w(TAG, "Failed to get cache path for " + newCacheId);
- return false;
- }
- Log.i(TAG, "Succesfully renamed " + cid +
- " to " + newCacheId +
- " at new path: " + newMountPath);
- cid = newCacheId;
-
- final File beforeCodeFile = new File(packagePath);
- setMountPath(newMountPath);
- final File afterCodeFile = new File(packagePath);
-
- // Reflect the rename in scanned details
- pkg.setCodePath(afterCodeFile.getAbsolutePath());
- pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, pkg.baseCodePath));
- pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, pkg.splitCodePaths));
-
- // Reflect the rename in app info
- pkg.setApplicationVolumeUuid(pkg.volumeUuid);
- pkg.setApplicationInfoCodePath(pkg.codePath);
- pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
- pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
- pkg.setApplicationInfoResourcePath(pkg.codePath);
- pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
- pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
-
- return true;
- }
-
- private void setMountPath(String mountPath) {
- final File mountFile = new File(mountPath);
-
- final File monolithicFile = new File(mountFile, RES_FILE_NAME);
- if (monolithicFile.exists()) {
- packagePath = monolithicFile.getAbsolutePath();
- if (isFwdLocked()) {
- resourcePath = new File(mountFile, PUBLIC_RES_FILE_NAME).getAbsolutePath();
- } else {
- resourcePath = packagePath;
- }
- } else {
- packagePath = mountFile.getAbsolutePath();
- resourcePath = packagePath;
- }
- }
-
- int doPostInstall(int status, int uid) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp();
- } else {
- final int groupOwner;
- final String protectedFile;
- if (isFwdLocked()) {
- groupOwner = UserHandle.getSharedAppGid(uid);
- protectedFile = RES_FILE_NAME;
- } else {
- groupOwner = -1;
- protectedFile = null;
- }
-
- if (uid < Process.FIRST_APPLICATION_UID
- || !PackageHelper.fixSdPermissions(cid, groupOwner, protectedFile)) {
- Slog.e(TAG, "Failed to finalize " + cid);
- PackageHelper.destroySdDir(cid);
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
-
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (!mounted) {
- PackageHelper.mountSdDir(cid, getEncryptKey(), Process.myUid());
- }
- }
- return status;
- }
-
- private void cleanUp() {
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "cleanUp");
-
- // Destroy secure container
- PackageHelper.destroySdDir(cid);
- }
-
- private List<String> getAllCodePaths() {
- final File codeFile = new File(getCodePath());
- if (codeFile != null && codeFile.exists()) {
- try {
- final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
- return pkg.getAllCodePaths();
- } catch (PackageParserException e) {
- // Ignored; we tried our best
- }
- }
- return Collections.EMPTY_LIST;
- }
-
- void cleanUpResourcesLI() {
- // Enumerate all code paths before deleting
- cleanUpResourcesLI(getAllCodePaths());
- }
-
- private void cleanUpResourcesLI(List<String> allCodePaths) {
- cleanUp();
- removeDexFiles(allCodePaths, instructionSets);
- }
-
- String getPackageName() {
- return getAsecPackageName(cid);
- }
-
- boolean doPostDeleteLI(boolean delete) {
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete);
- final List<String> allCodePaths = getAllCodePaths();
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (mounted) {
- // Unmount first
- if (PackageHelper.unMountSdDir(cid)) {
- mounted = false;
- }
- }
- if (!mounted && delete) {
- cleanUpResourcesLI(allCodePaths);
- }
- return !mounted;
- }
-
- @Override
- int doPreCopy() {
- if (isFwdLocked()) {
- if (!PackageHelper.fixSdPermissions(cid, getPackageUid(DEFAULT_CONTAINER_PACKAGE,
- MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM), RES_FILE_NAME)) {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- @Override
- int doPostCopy(int uid) {
- if (isFwdLocked()) {
- if (uid < Process.FIRST_APPLICATION_UID
- || !PackageHelper.fixSdPermissions(cid, UserHandle.getSharedAppGid(uid),
- RES_FILE_NAME)) {
- Slog.e(TAG, "Failed to finalize " + cid);
- PackageHelper.destroySdDir(cid);
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
- }
-
- /**
* Logic to handle movement of existing installed applications.
*/
class MoveInstallArgs extends InstallArgs {
@@ -17627,9 +16535,9 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
+ private boolean shouldCheckUpgradeKeySetLP(PackageSettingBase oldPs, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
- if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null
+ if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()
|| !oldPs.keySetData.isUsingUpgradeKeySets()) {
return false;
}
@@ -17649,7 +16557,7 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
+ private boolean checkUpgradeKeySetLP(PackageSettingBase oldPS, PackageParser.Package newPkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
@@ -18225,66 +17133,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
- // Collect all used permissions in the UID
- ArraySet<String> usedPermissions = new ArraySet<>();
- final int packageCount = su.packages.size();
- for (int i = 0; i < packageCount; i++) {
- PackageSetting ps = su.packages.valueAt(i);
- if (ps.pkg == null) {
- continue;
- }
- final int requestedPermCount = ps.pkg.requestedPermissions.size();
- for (int j = 0; j < requestedPermCount; j++) {
- String permission = ps.pkg.requestedPermissions.get(j);
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp != null) {
- usedPermissions.add(permission);
- }
- }
- }
-
- PermissionsState permissionsState = su.getPermissionsState();
- // Prune install permissions
- List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
- final int installPermCount = installPermStates.size();
- for (int i = installPermCount - 1; i >= 0; i--) {
- PermissionState permissionState = installPermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- }
- }
- }
-
- int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
- // Prune runtime permissions
- for (int userId : allUserIds) {
- List<PermissionState> runtimePermStates = permissionsState
- .getRuntimePermissionStates(userId);
- final int runtimePermCount = runtimePermStates.size();
- for (int i = runtimePermCount - 1; i >= 0; i--) {
- PermissionState permissionState = runtimePermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- runtimePermissionChangedUserIds = ArrayUtils.appendInt(
- runtimePermissionChangedUserIds, userId);
- }
- }
- }
- }
-
- return runtimePermissionChangedUserIds;
- }
-
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
// Update the parent package setting
@@ -18685,8 +17533,9 @@ public class PackageManagerService extends IPackageManager.Stub
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
- PackageParser.Permission perm = pkg.permissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(perm.info.name);
+ final PackageParser.Permission perm = pkg.permissions.get(i);
+ final BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
// Don't allow anyone but the system to define ephemeral permissions.
if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
@@ -18703,25 +17552,26 @@ public class PackageManagerService extends IPackageManager.Stub
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
- if (bp.sourcePackage.equals(pkg.packageName)
- && (bp.packageSetting instanceof PackageSetting)
- && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
+ final String sourcePackageName = bp.getSourcePackageName();
+ final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
+ if (sourcePackageName.equals(pkg.packageName)
+ && (shouldCheckUpgradeKeySetLP(sourcePackageSetting,
scanFlags))) {
- sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
+ sigsOk = checkUpgradeKeySetLP(sourcePackageSetting, pkg);
} else {
- sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
+ sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
- if (!bp.sourcePackage.equals("android")) {
+ if (!sourcePackageName.equals("android")) {
res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ pkg.packageName + " attempting to redeclare permission "
- + perm.info.name + " already owned by " + bp.sourcePackage);
+ + perm.info.name + " already owned by " + sourcePackageName);
res.origPermission = perm.info.name;
- res.origPackage = bp.sourcePackage;
+ res.origPackage = sourcePackageName;
return;
} else {
Slog.w(TAG, "Package " + pkg.packageName
@@ -18740,7 +17590,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
+ "non-runtime permission " + perm.info.name
+ " to runtime; keeping old protection level");
- perm.info.protectionLevel = bp.protectionLevel;
+ perm.info.protectionLevel = bp.getProtectionLevel();
}
}
}
@@ -18855,7 +17705,13 @@ public class PackageManagerService extends IPackageManager.Stub
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
- startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ if (!instantApp) {
+ startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ } else {
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
+ }
+ }
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
@@ -20440,7 +19296,7 @@ public class PackageManagerService extends IPackageManager.Stub
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -20579,9 +19435,9 @@ public class PackageManagerService extends IPackageManager.Stub
final int permissionCount = ps.pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
- String permission = ps.pkg.requestedPermissions.get(i);
-
- BasePermission bp = mSettings.mPermissions.get(permission);
+ final String permName = ps.pkg.requestedPermissions.get(i);
+ final BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(permName);
if (bp == null) {
continue;
}
@@ -20593,7 +19449,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int j = 0; j < packageCount; j++) {
PackageSetting pkg = ps.sharedUser.packages.valueAt(j);
if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName)
- && pkg.pkg.requestedPermissions.contains(permission)) {
+ && pkg.pkg.requestedPermissions.contains(permName)) {
used = true;
break;
}
@@ -20603,13 +19459,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = ps.getPermissionsState();
- final int oldFlags = permissionsState.getPermissionFlags(bp.name, userId);
+ final int oldFlags = permissionsState.getPermissionFlags(permName, userId);
// Always clear the user settable flags.
- final boolean hasInstallState = permissionsState.getInstallPermissionState(
- bp.name) != null;
+ final boolean hasInstallState =
+ permissionsState.getInstallPermissionState(permName) != null;
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
@@ -20709,7 +19565,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
/* requireFullPermission= */ true, /* checkShell= */ false,
"delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
@@ -20829,7 +19685,7 @@ public class PackageManagerService extends IPackageManager.Stub
String opname) {
// writer
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
@@ -20894,7 +19750,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"replace preferred activity");
synchronized (mPackages) {
@@ -21571,8 +20427,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
if (DEBUG_BACKUP) {
- Slog.v(TAG, " + Restoring grant: pkg=" + pkgName + " perm=" + permName
- + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
+ Slog.v(TAG, " + Restoring grant:"
+ + " pkg=" + pkgName
+ + " perm=" + permName
+ + " granted=" + isGranted
+ + " bits=0x" + Integer.toHexString(newFlagSet));
}
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
@@ -21581,13 +20440,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
Slog.v(TAG, " + already installed; applying");
}
PermissionsState perms = ps.getPermissionsState();
- BasePermission bp = mSettings.mPermissions.get(permName);
+ BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(permName);
if (bp != null) {
if (isGranted) {
perms.grantRuntimePermission(bp, userId);
}
if (newFlagSet != 0) {
- perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
+ perms.updatePermissionFlags(
+ bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
}
}
} else {
@@ -21616,7 +20477,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
if (intentFilter.countActions() == 0) {
Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
return;
@@ -21647,7 +20509,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
final int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
synchronized (mPackages) {
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
@@ -21877,7 +20740,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
}
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, true /* checkShell */, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
@@ -22166,7 +21029,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
if (!sUserManager.exists(userId)) {
return;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mPackages) {
mSettings.writePackageRestrictionsLPr(userId);
@@ -22208,7 +21071,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "stop package");
// writer
synchronized (mPackages) {
@@ -22248,7 +21111,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get enabled");
// reader
synchronized (mPackages) {
@@ -22263,7 +21126,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public int getComponentEnabledSetting(ComponentName component, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
synchronized (mPackages) {
if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
@@ -22361,7 +21224,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
- mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+ mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
}
if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
@@ -22465,85 +21328,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
return buf.toString();
}
- static class DumpState {
- public static final int DUMP_LIBS = 1 << 0;
- public static final int DUMP_FEATURES = 1 << 1;
- public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
- public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
- public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
- public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
- public static final int DUMP_PERMISSIONS = 1 << 6;
- public static final int DUMP_PACKAGES = 1 << 7;
- public static final int DUMP_SHARED_USERS = 1 << 8;
- public static final int DUMP_MESSAGES = 1 << 9;
- public static final int DUMP_PROVIDERS = 1 << 10;
- public static final int DUMP_VERIFIERS = 1 << 11;
- public static final int DUMP_PREFERRED = 1 << 12;
- public static final int DUMP_PREFERRED_XML = 1 << 13;
- public static final int DUMP_KEYSETS = 1 << 14;
- public static final int DUMP_VERSION = 1 << 15;
- public static final int DUMP_INSTALLS = 1 << 16;
- public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
- public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
- public static final int DUMP_FROZEN = 1 << 19;
- public static final int DUMP_DEXOPT = 1 << 20;
- public static final int DUMP_COMPILER_STATS = 1 << 21;
- public static final int DUMP_CHANGES = 1 << 22;
- public static final int DUMP_VOLUMES = 1 << 23;
-
- public static final int OPTION_SHOW_FILTERS = 1 << 0;
-
- private int mTypes;
-
- private int mOptions;
-
- private boolean mTitlePrinted;
-
- private SharedUserSetting mSharedUser;
-
- public boolean isDumping(int type) {
- if (mTypes == 0 && type != DUMP_PREFERRED_XML) {
- return true;
- }
-
- return (mTypes & type) != 0;
- }
-
- public void setDump(int type) {
- mTypes |= type;
- }
-
- public boolean isOptionEnabled(int option) {
- return (mOptions & option) != 0;
- }
-
- public void setOptionEnabled(int option) {
- mOptions |= option;
- }
-
- public boolean onTitlePrinted() {
- final boolean printed = mTitlePrinted;
- mTitlePrinted = true;
- return printed;
- }
-
- public boolean getTitlePrinted() {
- return mTitlePrinted;
- }
-
- public void setTitlePrinted(boolean enabled) {
- mTitlePrinted = enabled;
- }
-
- public SharedUserSetting getSharedUser() {
- return mSharedUser;
- }
-
- public void setSharedUser(SharedUserSetting user) {
- mSharedUser = user;
- }
- }
-
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -23401,135 +22185,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
}
- /*
- * Update media status on PackageManager.
- */
- @Override
- public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- enforceSystemOrRoot("Media status can only be updated by the system");
- // reader; this apparently protects mMediaMounted, but should probably
- // be a different lock in that case.
- synchronized (mPackages) {
- Log.i(TAG, "Updating external media status from "
- + (mMediaMounted ? "mounted" : "unmounted") + " to "
- + (mediaStatus ? "mounted" : "unmounted"));
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
- + ", mMediaMounted=" + mMediaMounted);
- if (mediaStatus == mMediaMounted) {
- final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
- : 0, -1);
- mHandler.sendMessage(msg);
- return;
- }
- mMediaMounted = mediaStatus;
- }
- // Queue up an async operation since the package installation may take a
- // little while.
- mHandler.post(new Runnable() {
- public void run() {
- updateExternalMediaStatusInner(mediaStatus, reportStatus, true);
- }
- });
- }
-
- /**
- * Called by StorageManagerService when the initial ASECs to scan are available.
- * Should block until all the ASEC containers are finished being scanned.
- */
- public void scanAvailableAsecs() {
- updateExternalMediaStatusInner(true, false, false);
- }
-
- /*
- * Collect information of applications on external media, map them against
- * existing containers and update information based on current mount status.
- * Please note that we always have to report status if reportStatus has been
- * set to true especially when unloading packages.
- */
- private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus,
- boolean externalStorage) {
- ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>();
- int[] uidArr = EmptyArray.INT;
-
- final String[] list = PackageHelper.getSecureContainerList();
- if (ArrayUtils.isEmpty(list)) {
- Log.i(TAG, "No secure containers found");
- } else {
- // Process list of secure containers and categorize them
- // as active or stale based on their package internal state.
-
- // reader
- synchronized (mPackages) {
- for (String cid : list) {
- // Leave stages untouched for now; installer service owns them
- if (PackageInstallerService.isStageName(cid)) continue;
-
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Processing container " + cid);
- String pkgName = getAsecPackageName(cid);
- if (pkgName == null) {
- Slog.i(TAG, "Found stale container " + cid + " with no package name");
- continue;
- }
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Looking for pkg : " + pkgName);
-
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps == null) {
- Slog.i(TAG, "Found stale container " + cid + " with no matching settings");
- continue;
- }
-
- /*
- * Skip packages that are not external if we're unmounting
- * external storage.
- */
- if (externalStorage && !isMounted && !isExternal(ps)) {
- continue;
- }
-
- final AsecInstallArgs args = new AsecInstallArgs(cid,
- getAppDexInstructionSets(ps), ps.isForwardLocked());
- // The package status is changed only if the code path
- // matches between settings and the container id.
- if (ps.codePathString != null
- && ps.codePathString.startsWith(args.getCodePath())) {
- if (DEBUG_SD_INSTALL) {
- Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName
- + " at code path: " + ps.codePathString);
- }
-
- // We do have a valid package installed on sdcard
- processCids.put(args, ps.codePathString);
- final int uid = ps.appId;
- if (uid != -1) {
- uidArr = ArrayUtils.appendInt(uidArr, uid);
- }
- } else {
- Slog.i(TAG, "Found stale container " + cid + ": expected codePath="
- + ps.codePathString);
- }
- }
- }
-
- Arrays.sort(uidArr);
- }
-
- // Process packages with valid entries.
- if (isMounted) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, externalStorage);
- startCleaningPackages();
- mInstallerService.onSecureContainersAvailable();
- } else {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, reportStatus);
- }
- }
-
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
final int size = infos.size();
@@ -23569,193 +22224,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
}
- /*
- * Look at potentially valid container ids from processCids If package
- * information doesn't match the one on record or package scanning fails,
- * the cid is added to list of removeCids. We currently don't delete stale
- * containers.
- */
- private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
- boolean externalStorage) {
- ArrayList<String> pkgList = new ArrayList<String>();
- Set<AsecInstallArgs> keys = processCids.keySet();
-
- for (AsecInstallArgs args : keys) {
- String codePath = processCids.get(args);
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Loading container : " + args.cid);
- int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- try {
- // Make sure there are no container errors first.
- if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to mount cid : " + args.cid
- + " when installing from sdcard");
- continue;
- }
- // Check code path here.
- if (codePath == null || !codePath.startsWith(args.getCodePath())) {
- Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()
- + " does not match one in settings " + codePath);
- continue;
- }
- // Parse package
- int parseFlags = mDefParseFlags;
- if (args.isExternalAsec()) {
- parseFlags |= PackageParser.PARSE_EXTERNAL_STORAGE;
- }
- if (args.isFwdLocked()) {
- parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
- }
-
- synchronized (mInstallLock) {
- PackageParser.Package pkg = null;
- try {
- // Sadly we don't know the package name yet to freeze it
- pkg = scanPackageTracedLI(new File(codePath), parseFlags,
- SCAN_IGNORE_FROZEN, 0, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
- }
- // Scan the package
- if (pkg != null) {
- /*
- * TODO why is the lock being held? doPostInstall is
- * called in other places without the lock. This needs
- * to be straightened out.
- */
- // writer
- synchronized (mPackages) {
- retCode = PackageManager.INSTALL_SUCCEEDED;
- pkgList.add(pkg.packageName);
- // Post process args
- args.doPostInstall(PackageManager.INSTALL_SUCCEEDED,
- pkg.applicationInfo.uid);
- }
- } else {
- Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard");
- }
- }
-
- } finally {
- if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode);
- }
- }
- }
- // writer
- synchronized (mPackages) {
- // If the platform SDK has changed since the last time we booted,
- // we need to re-grant app permission to catch any new ones that
- // appear. This is really a hack, and means that apps can in some
- // cases get permissions that the user didn't initially explicitly
- // allow... it would be nice to have some better way to handle
- // this situation.
- final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
- : mSettings.getInternalVersion();
- final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
- : StorageManager.UUID_PRIVATE_INTERNAL;
-
- int updateFlags = UPDATE_PERMISSIONS_ALL;
- if (ver.sdkVersion != mSdkVersion) {
- logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
- + mSdkVersion + "; regranting permissions for external");
- updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
- }
- updatePermissionsLPw(null, null, volumeUuid, updateFlags);
-
- // Yay, everything is now upgraded
- ver.forceCurrent();
-
- // can downgrade to reader
- // Persist settings
- mSettings.writeLPr();
- }
- // Send a broadcast to let everyone know we are done processing
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
- }
- }
-
- /*
- * Utility method to unload a list of specified containers
- */
- private void unloadAllContainers(Set<AsecInstallArgs> cidArgs) {
- // Just unmount all valid containers.
- for (AsecInstallArgs arg : cidArgs) {
- synchronized (mInstallLock) {
- arg.doPostDeleteLI(false);
- }
- }
- }
-
- /*
- * Unload packages mounted on external media. This involves deleting package
- * data from internal structures, sending broadcasts about disabled packages,
- * gc'ing to free up references, unmounting all secure containers
- * corresponding to packages on external media, and posting a
- * UPDATED_MEDIA_STATUS message if status has been requested. Please note
- * that we always have to post this message if status has been requested no
- * matter what.
- */
- private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[],
- final boolean reportStatus) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "unloading media packages");
- ArrayList<String> pkgList = new ArrayList<String>();
- ArrayList<AsecInstallArgs> failedList = new ArrayList<AsecInstallArgs>();
- final Set<AsecInstallArgs> keys = processCids.keySet();
- for (AsecInstallArgs args : keys) {
- String pkgName = args.getPackageName();
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Trying to unload pkg : " + pkgName);
- // Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
- synchronized (mInstallLock) {
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final boolean res;
- try (PackageFreezer freezer = freezePackageForDelete(pkgName, deleteFlags,
- "unloadMediaPackages")) {
- res = deletePackageLIF(pkgName, null, false, null, deleteFlags, outInfo, false,
- null);
- }
- if (res) {
- pkgList.add(pkgName);
- } else {
- Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
- failedList.add(args);
- }
- }
- }
-
- // reader
- synchronized (mPackages) {
- // We didn't update the settings after removing each package;
- // write them now for all packages.
- mSettings.writeLPr();
- }
-
- // We have to absolutely send UPDATED_MEDIA_STATUS only
- // after confirming that all the receivers processed the ordered
- // broadcast when packages get disabled, force a gc to clean things up.
- // and unload all the containers.
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(false, false, pkgList, uidArr,
- new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky,
- int sendingUser) throws RemoteException {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, 1, keys);
- mHandler.sendMessage(msg);
- }
- });
- } else {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1,
- keys);
- mHandler.sendMessage(msg);
- }
- }
-
private void loadPrivatePackages(final VolumeInfo vol) {
mHandler.post(new Runnable() {
@Override
@@ -24841,7 +23309,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
void onNewUserCreated(final int userId) {
- mDefaultPermissionPolicy.grantDefaultPermissions(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.
@@ -24873,7 +23343,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
synchronized (mPackages) {
if (mSettings.mReadExternalStorageEnforced == null
|| mSettings.mReadExternalStorageEnforced != enforced) {
- mSettings.mReadExternalStorageEnforced = enforced;
+ mSettings.mReadExternalStorageEnforced =
+ enforced ? Boolean.TRUE : Boolean.FALSE;
mSettings.writeLPr();
}
}
@@ -25245,74 +23716,164 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
return results;
}
+
+ // NB: this differentiates between preloads and sideloads
+ @Override
+ public String getInstallerForPackage(String packageName) throws RemoteException {
+ final String installerName = getInstallerPackageName(packageName);
+ if (!TextUtils.isEmpty(installerName)) {
+ return installerName;
+ }
+ // differentiate between preload and sideload
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ ApplicationInfo appInfo = getApplicationInfo(packageName,
+ /*flags*/ 0,
+ /*userId*/ callingUser);
+ if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return "preload";
+ }
+ return "";
+ }
+
+ @Override
+ public int getVersionCodeForPackage(String packageName) throws RemoteException {
+ try {
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
+ if (pInfo != null) {
+ return pInfo.versionCode;
+ }
+ } catch (Exception e) {
+ }
+ return 0;
+ }
}
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
- public void setLocationPackagesProvider(PackagesProvider provider) {
+ public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask,
+ int flagValues, int userId) {
+ PackageManagerService.this.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, userId);
+ }
+
+ @Override
+ public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
+ return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
+ }
+
+ @Override
+ public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider);
+ return BasePermission.enforcePermissionTreeLP(
+ mSettings.mPermissionTrees, permName, callingUid);
}
}
+ @Override
+ public boolean isInstantApp(String packageName, int userId) {
+ return PackageManagerService.this.isInstantApp(packageName, userId);
+ }
@Override
- public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+ public String getInstantAppPackageName(int uid) {
+ return PackageManagerService.this.getInstantAppPackageName(uid);
+ }
+
+ @Override
+ public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setVoiceInteractionPackagesProviderLPw(provider);
+ return PackageManagerService.this.filterAppAccessLPr(
+ (PackageSetting) pkg.mExtras, callingUid, userId);
}
}
@Override
- public void setSmsAppPackagesProvider(PackagesProvider provider) {
+ public PackageParser.Package getPackage(String packageName) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setSmsAppPackagesProviderLPw(provider);
+ packageName = resolveInternalPackageNameLPr(
+ packageName, PackageManager.VERSION_CODE_HIGHEST);
+ return mPackages.get(packageName);
}
}
@Override
- public void setDialerAppPackagesProvider(PackagesProvider provider) {
+ public PackageParser.Package getDisabledPackage(String packageName) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setDialerAppPackagesProviderLPw(provider);
+ final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ return (ps != null) ? ps.pkg : null;
}
}
@Override
- public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setSimCallManagerPackagesProviderLPw(provider);
+ public String getKnownPackageName(int knownPackage, int userId) {
+ switch(knownPackage) {
+ case PackageManagerInternal.PACKAGE_BROWSER:
+ return getDefaultBrowserPackageName(userId);
+ case PackageManagerInternal.PACKAGE_INSTALLER:
+ return mRequiredInstallerPackage;
+ case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
+ return mSetupWizardPackage;
+ case PackageManagerInternal.PACKAGE_SYSTEM:
+ return "android";
+ case PackageManagerInternal.PACKAGE_VERIFIER:
+ return mRequiredVerifierPackage;
}
+ return null;
+ }
+
+ @Override
+ public boolean isResolveActivityComponent(ComponentInfo component) {
+ return mResolveActivity.packageName.equals(component.packageName)
+ && mResolveActivity.name.equals(component.name);
+ }
+
+ @Override
+ public void setLocationPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setLocationPackagesProvider(provider);
+ }
+
+ @Override
+ public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
+ }
+
+ @Override
+ public void setSmsAppPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider);
+ }
+
+ @Override
+ public void setDialerAppPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider);
+ }
+
+ @Override
+ public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider);
}
@Override
public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setSyncAdapterPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider);
}
@Override
public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsAppLPr(
- packageName, userId);
- }
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
synchronized (mPackages) {
mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerAppLPr(
- packageName, userId);
}
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManagerLPr(
- packageName, userId);
- }
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+ packageName, userId);
}
@Override
@@ -25399,6 +23960,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
+ public List<ResolveInfo> queryIntentServices(
+ Intent intent, int flags, int callingUid, int userId) {
+ final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
+ return PackageManagerService.this
+ .queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid,
+ false);
+ }
+
+ @Override
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
@@ -25433,17 +24003,19 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
- public void grantRuntimePermission(String packageName, String name, int userId,
+ public void grantRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
- PackageManagerService.this.grantRuntimePermission(packageName, name, userId,
- overridePolicy);
+ PackageManagerService.this.mPermissionManager.grantRuntimePermission(
+ permName, packageName, overridePolicy, getCallingUid(), userId,
+ mPermissionCallback);
}
@Override
- public void revokeRuntimePermission(String packageName, String name, int userId,
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
- PackageManagerService.this.revokeRuntimePermission(packageName, name, userId,
- overridePolicy);
+ mPermissionManager.revokeRuntimePermission(
+ permName, packageName, overridePolicy, getCallingUid(), userId,
+ mPermissionCallback);
}
@Override
@@ -25565,9 +24137,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId) {
+ int flags, int userId, boolean resolveForStart) {
return resolveIntentInternal(
- intent, resolvedType, flags, userId, true /*resolveForStart*/);
+ intent, resolvedType, flags, userId, resolveForStart);
}
@Override
@@ -25577,6 +24149,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
+ public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ return PackageManagerService.this.resolveContentProviderInternal(
+ name, flags, userId);
+ }
+
+ @Override
public void addIsolatedUid(int isolatedUid, int ownerUid) {
synchronized (mPackages) {
mIsolatedOwners.put(isolatedUid, ownerUid);
@@ -25623,7 +24201,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
- mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierAppsLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -25637,7 +24215,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
- mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -25712,7 +24290,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
@Override
public int getInstallReason(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get install reason");
synchronized (mPackages) {
@@ -25790,7 +24368,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public String getInstantAppAndroidId(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppAndroidId");
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppAndroidId");
// Make sure the target is an Instant App.
diff --git a/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 1a97a72c..19b0d9bc 100644
--- a/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,14 +26,19 @@ import dalvik.system.DexFile;
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
- "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
+ "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
};
+ static final int REASON_SHARED_INDEX = 6;
+
// Static block to ensure the strings array is of the right length.
static {
if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
throw new IllegalStateException("REASON_STRINGS not correct");
}
+ if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
+ throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
+ }
}
private static String getSystemPropertyName(int reason) {
@@ -52,11 +57,18 @@ public class PackageManagerServiceCompilerMapping {
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
+ } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
+ + "(reason " + REASON_STRINGS[reason] + ")");
}
return sysPropValue;
}
+ private static boolean isFilterAllowedForReason(int reason, String filter) {
+ return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
+ }
+
// Check that the properties are set and valid.
// Note: this is done in a separate method so this class can be statically initialized.
static void checkProperties() {
diff --git a/com/android/server/pm/PackageManagerServiceUtils.java b/com/android/server/pm/PackageManagerServiceUtils.java
index 25fef0a0..8f7971e1 100644
--- a/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/com/android/server/pm/PackageManagerServiceUtils.java
@@ -22,17 +22,23 @@ import com.android.server.pm.dex.PackageDexUsage;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.TAG;
+import com.android.internal.util.ArrayUtils;
+
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.os.Debug;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
+import android.util.jar.StrictJarFile;
import dalvik.system.VMRuntime;
import libcore.io.Libcore;
@@ -41,9 +47,11 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
+import java.util.zip.ZipEntry;
/**
* Class containing helper methods for the PackageManagerService.
@@ -253,4 +261,73 @@ public class PackageManagerServiceUtils {
}
return false;
}
+
+ /**
+ * Checks that the archive located at {@code fileName} has uncompressed dex file and so
+ * files that can be direclty mapped.
+ */
+ public static void logApkHasUncompressedCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().endsWith(".dex")) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " +
+ entry.getName());
+ } else if ((entry.getDataOffset() & 0x3) != 0) {
+ Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " +
+ entry.getName());
+ }
+ } else if (entry.getName().endsWith(".so")) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Slog.wtf(TAG, "APK " + fileName + " has compressed native code " +
+ entry.getName());
+ } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
+ Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " +
+ entry.getName());
+ }
+ }
+ }
+ } catch (IOException ignore) {
+ Slog.wtf(TAG, "Error when parsing APK " + fileName);
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {}
+ }
+ return;
+ }
+
+ /**
+ * Checks that the APKs in the given package have uncompressed dex file and so
+ * files that can be direclty mapped.
+ */
+ public static void logPackageHasUncompressedCode(PackageParser.Package pkg) {
+ logApkHasUncompressedCode(pkg.baseCodePath);
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+ logApkHasUncompressedCode(pkg.splitCodePaths[i]);
+ }
+ }
+ }
+
+ public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
+ if (callingUid == Process.SHELL_UID) {
+ if (userHandle >= 0
+ && PackageManagerService.sUserManager.hasUserRestriction(
+ restriction, userHandle)) {
+ throw new SecurityException("Shell does not have permission to access user "
+ + userHandle);
+ } else if (userHandle < 0) {
+ Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
+ + userHandle + "\n\t" + Debug.getCallers(3));
+ }
+ }
+ }
}
diff --git a/com/android/server/pm/PackageManagerShellCommand.java b/com/android/server/pm/PackageManagerShellCommand.java
index 930e4f09..1fea003a 100644
--- a/com/android/server/pm/PackageManagerShellCommand.java
+++ b/com/android/server/pm/PackageManagerShellCommand.java
@@ -183,7 +183,7 @@ class PackageManagerShellCommand extends ShellCommand {
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
null, null);
params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
- pkgLite, false, params.sessionParams.abiOverride));
+ pkgLite, params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
pw.println("Error: Failed to parse APK file: " + file);
throw new IllegalArgumentException(
@@ -1441,7 +1441,7 @@ class PackageManagerShellCommand extends ShellCommand {
out = session.openWrite(splitName, 0, sizeBytes);
int total = 0;
- byte[] buffer = new byte[65536];
+ byte[] buffer = new byte[1024 * 1024];
int c;
while ((c = in.read(buffer)) != -1) {
total += c;
diff --git a/com/android/server/pm/PackageSetting.java b/com/android/server/pm/PackageSetting.java
index 52bf6410..83cb2db2 100644
--- a/com/android/server/pm/PackageSetting.java
+++ b/com/android/server/pm/PackageSetting.java
@@ -23,13 +23,15 @@ import android.content.pm.UserInfo;
import android.service.pm.PackageProto;
import android.util.proto.ProtoOutputStream;
+import com.android.server.pm.permission.PermissionsState;
+
import java.io.File;
import java.util.List;
/**
* Settings data for a particular package we know about.
*/
-final class PackageSetting extends PackageSettingBase {
+public final class PackageSetting extends PackageSettingBase {
int appId;
PackageParser.Package pkg;
/**
@@ -103,12 +105,21 @@ final class PackageSetting extends PackageSettingBase {
sharedUserId = orig.sharedUserId;
}
+ @Override
public PermissionsState getPermissionsState() {
return (sharedUser != null)
? sharedUser.getPermissionsState()
: super.getPermissionsState();
}
+ public PackageParser.Package getPackage() {
+ return pkg;
+ }
+
+ public int getAppId() {
+ return appId;
+ }
+
public boolean isPrivileged() {
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
@@ -125,6 +136,7 @@ final class PackageSetting extends PackageSettingBase {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ @Override
public boolean isSharedUser() {
return sharedUser != null;
}
@@ -136,6 +148,10 @@ final class PackageSetting extends PackageSettingBase {
return true;
}
+ public boolean hasChildPackages() {
+ return childPackageNames != null && !childPackageNames.isEmpty();
+ }
+
public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
final long packageToken = proto.start(fieldId);
proto.write(PackageProto.NAME, (realName != null ? realName : name));
diff --git a/com/android/server/pm/PackageSettingBase.java b/com/android/server/pm/PackageSettingBase.java
index d3ca1fda..e19e83fc 100644
--- a/com/android/server/pm/PackageSettingBase.java
+++ b/com/android/server/pm/PackageSettingBase.java
@@ -24,14 +24,12 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
-import android.os.storage.VolumeInfo;
import android.service.pm.PackageProto;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.google.android.collect.Lists;
import java.io.File;
import java.util.ArrayList;
@@ -42,7 +40,7 @@ import java.util.Set;
/**
* Settings base class for pending and resolved classes.
*/
-abstract class PackageSettingBase extends SettingBase {
+public abstract class PackageSettingBase extends SettingBase {
private static final int[] EMPTY_INT_ARRAY = new int[0];
@@ -230,6 +228,9 @@ abstract class PackageSettingBase extends SettingBase {
return updateAvailable;
}
+ public boolean isSharedUser() {
+ return false;
+ }
/**
* Makes a shallow copy of the given package settings.
*
@@ -412,7 +413,7 @@ abstract class PackageSettingBase extends SettingBase {
modifyUserState(userId).suspended = suspended;
}
- boolean getInstantApp(int userId) {
+ public boolean getInstantApp(int userId) {
return readUserState(userId).instantApp;
}
diff --git a/com/android/server/pm/SettingBase.java b/com/android/server/pm/SettingBase.java
index e17cec02..c97f5e54 100644
--- a/com/android/server/pm/SettingBase.java
+++ b/com/android/server/pm/SettingBase.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import android.content.pm.ApplicationInfo;
+import com.android.server.pm.permission.PermissionsState;
+
abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
diff --git a/com/android/server/pm/Settings.java b/com/android/server/pm/Settings.java
index 51d3e102..00844114 100644
--- a/com/android/server/pm/Settings.java
+++ b/com/android/server/pm/Settings.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -64,7 +63,6 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -87,10 +85,11 @@ import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
-import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PackageManagerService.DumpState;
-import com.android.server.pm.PermissionsState.PermissionState;
+import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.PermissionSettings;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
import libcore.io.IoUtils;
@@ -127,7 +126,7 @@ import java.util.Set;
/**
* Holds information about dynamic settings.
*/
-final class Settings {
+public final class Settings {
private static final String TAG = "PackageSettings";
/**
@@ -176,7 +175,7 @@ final class Settings {
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
- private static final String TAG_ITEM = "item";
+ public static final String TAG_ITEM = "item";
private static final String TAG_DISABLED_COMPONENTS = "disabled-components";
private static final String TAG_ENABLED_COMPONENTS = "enabled-components";
private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions";
@@ -201,7 +200,8 @@ final class Settings {
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
- private static final String ATTR_NAME = "name";
+ public static final String ATTR_NAME = "name";
+ public static final String ATTR_PACKAGE = "package";
private static final String ATTR_USER = "user";
private static final String ATTR_CODE = "code";
private static final String ATTR_GRANTED = "granted";
@@ -233,7 +233,6 @@ final class Settings {
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_SDK_VERSION = "sdkVersion";
private static final String ATTR_DATABASE_VERSION = "databaseVersion";
- private static final String ATTR_DONE = "done";
// Bookkeeping for restored permission grants
private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
@@ -379,10 +378,6 @@ final class Settings {
private final ArrayMap<Long, Integer> mKeySetRefs =
new ArrayMap<Long, Integer>();
- // Mapping from permission names to info about them.
- final ArrayMap<String, BasePermission> mPermissions =
- new ArrayMap<String, BasePermission>();
-
// Mapping from permission tree names to info about them.
final ArrayMap<String, BasePermission> mPermissionTrees =
new ArrayMap<String, BasePermission>();
@@ -420,14 +415,16 @@ final class Settings {
private final File mSystemDir;
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+ /** Settings and other information about permissions */
+ private final PermissionSettings mPermissions;
- Settings(Object lock) {
- this(Environment.getDataDirectory(), lock);
+ Settings(PermissionSettings permissions, Object lock) {
+ this(Environment.getDataDirectory(), permissions, lock);
}
- Settings(File dataDir, Object lock) {
+ Settings(File dataDir, PermissionSettings permission, Object lock) {
mLock = lock;
-
+ mPermissions = permission;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
mSystemDir = new File(dataDir, "system");
@@ -490,7 +487,7 @@ final class Settings {
final PermissionsState perms = ps.getPermissionsState();
for (RestoredPermissionGrant grant : grants) {
- BasePermission bp = mPermissions.get(grant.permissionName);
+ BasePermission bp = mPermissions.getPermission(grant.permissionName);
if (bp != null) {
if (grant.granted) {
perms.grantRuntimePermission(bp, userId);
@@ -507,6 +504,10 @@ final class Settings {
writeRuntimePermissionsForUserLPr(userId, false);
}
+ public boolean canPropagatePermissionToInstantApp(String permName) {
+ return mPermissions.canPropagatePermissionToInstantApp(permName);
+ }
+
void setInstallerPackageName(String pkgName, String installerPkgName) {
PackageSetting p = mPackages.get(pkgName);
if (p != null) {
@@ -664,29 +665,11 @@ final class Settings {
}
}
- // Transfer ownership of permissions from one package to another.
- void transferPermissionsLPw(String origPkg, String newPkg) {
- // Transfer ownership of permissions to the new package.
- for (int i=0; i<2; i++) {
- ArrayMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
- for (BasePermission bp : permissions.values()) {
- if (origPkg.equals(bp.sourcePackage)) {
- if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG,
- "Moving permission " + bp.name
- + " from pkg " + bp.sourcePackage
- + " to " + newPkg);
- bp.sourcePackage = newPkg;
- bp.packageSetting = null;
- bp.perm = null;
- if (bp.pendingInfo != null) {
- bp.pendingInfo.packageName = newPkg;
- }
- bp.uid = 0;
- bp.setGids(null, false);
- }
- }
- }
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ void transferPermissionsLPw(String origPackageName, String newPackageName) {
+ mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
}
/**
@@ -1074,7 +1057,7 @@ final class Settings {
// Update permissions
for (String eachPerm : deletedPs.pkg.requestedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
+ BasePermission bp = mPermissions.getPermission(eachPerm);
if (bp == null) {
continue;
}
@@ -2022,8 +2005,7 @@ final class Settings {
// Specifically for backup/restore
public void processRestoredPermissionGrantLPr(String pkgName, String permission,
- boolean isGranted, int restoredFlagSet, int userId)
- throws IOException, XmlPullParserException {
+ boolean isGranted, int restoredFlagSet, int userId) {
mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
pkgName, permission, isGranted, restoredFlagSet, userId);
}
@@ -2225,7 +2207,7 @@ final class Settings {
if (tagName.equals(TAG_ITEM)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
- BasePermission bp = mPermissions.get(name);
+ BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
XmlUtils.skipCurrentTag(parser);
@@ -2520,9 +2502,7 @@ final class Settings {
serializer.endTag(null, "permission-trees");
serializer.startTag(null, "permissions");
- for (BasePermission bp : mPermissions.values()) {
- writePermissionLPr(serializer, bp);
- }
+ mPermissions.writePermissions(serializer);
serializer.endTag(null, "permissions");
for (final PackageSetting pkg : mPackages.values()) {
@@ -2605,9 +2585,6 @@ final class Settings {
writeAllRuntimePermissionsLPr();
return;
- } catch(XmlPullParserException e) {
- Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
} catch(java.io.IOException e) {
Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ "current changes will be lost at reboot", e);
@@ -2951,7 +2928,6 @@ final class Settings {
void writeUpgradeKeySetsLPr(XmlSerializer serializer,
PackageKeySetData data) throws IOException {
- long properSigning = data.getProperSigningKeySet();
if (data.isUsingUpgradeKeySets()) {
for (long id : data.getUpgradeKeySets()) {
serializer.startTag(null, "upgrade-keyset");
@@ -2971,32 +2947,8 @@ final class Settings {
}
}
- void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
- throws XmlPullParserException, java.io.IOException {
- if (bp.sourcePackage != null) {
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, bp.name);
- serializer.attribute(null, "package", bp.sourcePackage);
- if (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel));
- }
- if (PackageManagerService.DEBUG_SETTINGS)
- Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type="
- + bp.type);
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- final PermissionInfo pi = bp.perm != null ? bp.perm.info : bp.pendingInfo;
- if (pi != null) {
- serializer.attribute(null, "type", "dynamic");
- if (pi.icon != 0) {
- serializer.attribute(null, "icon", Integer.toString(pi.icon));
- }
- if (pi.nonLocalizedLabel != null) {
- serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
- }
- }
- }
- serializer.endTag(null, TAG_ITEM);
- }
+ void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException {
+ bp.writeLPr(serializer);
}
ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() {
@@ -3088,9 +3040,9 @@ final class Settings {
if (tagName.equals("package")) {
readPackageLPw(parser);
} else if (tagName.equals("permissions")) {
- readPermissionsLPw(mPermissions, parser);
+ mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
- readPermissionsLPw(mPermissionTrees, parser);
+ PermissionSettings.readPermissions(mPermissionTrees, parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if (tagName.equals("preferred-packages")) {
@@ -3169,7 +3121,8 @@ final class Settings {
}
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
- mReadExternalStorageEnforced = "1".equals(enforcement);
+ mReadExternalStorageEnforced =
+ "1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE;
} else if (tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
} else if (TAG_VERSION.equals(tagName)) {
@@ -3593,72 +3546,6 @@ final class Settings {
}
}
- private int readInt(XmlPullParser parser, String ns, String name, int defValue) {
- String v = parser.getAttributeValue(ns, name);
- try {
- if (v == null) {
- return defValue;
- }
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: attribute " + name
- + " has bad integer value " + v + " at "
- + parser.getPositionDescription());
- }
- return defValue;
- }
-
- private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- final String tagName = parser.getName();
- if (tagName.equals(TAG_ITEM)) {
- final String name = parser.getAttributeValue(null, ATTR_NAME);
- final String sourcePackage = parser.getAttributeValue(null, "package");
- final String ptype = parser.getAttributeValue(null, "type");
- if (name != null && sourcePackage != null) {
- final boolean dynamic = "dynamic".equals(ptype);
- BasePermission bp = out.get(name);
- // If the permission is builtin, do not clobber it.
- if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) {
- bp = new BasePermission(name.intern(), sourcePackage,
- dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
- }
- bp.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_NORMAL);
- bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
- if (dynamic) {
- PermissionInfo pi = new PermissionInfo();
- pi.packageName = sourcePackage.intern();
- pi.name = name.intern();
- pi.icon = readInt(parser, null, "icon", 0);
- pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
- pi.protectionLevel = bp.protectionLevel;
- bp.pendingInfo = pi;
- }
- out.put(bp.name, bp);
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: permissions has" + " no name at "
- + parser.getPositionDescription());
- }
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element reading permissions: " + parser.getName() + " at "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
IOException {
String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -4385,10 +4272,6 @@ final class Settings {
return ps;
}
- private String compToString(ArraySet<String> cmp) {
- return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
- }
-
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
@@ -5001,45 +4884,8 @@ final class Settings {
void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
- boolean printedSomething = false;
- for (BasePermission p : mPermissions.values()) {
- if (packageName != null && !packageName.equals(p.sourcePackage)) {
- continue;
- }
- if (permissionNames != null && !permissionNames.contains(p.name)) {
- continue;
- }
- if (!printedSomething) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Permissions:");
- printedSomething = true;
- }
- pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(p)));
- pw.println("):");
- pw.print(" sourcePackage="); pw.println(p.sourcePackage);
- pw.print(" uid="); pw.print(p.uid);
- pw.print(" gids="); pw.print(Arrays.toString(
- p.computeGids(UserHandle.USER_SYSTEM)));
- pw.print(" type="); pw.print(p.type);
- pw.print(" prot=");
- pw.println(PermissionInfo.protectionToString(p.protectionLevel));
- if (p.perm != null) {
- pw.print(" perm="); pw.println(p.perm);
- if ((p.perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
- || (p.perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
- pw.print(" flags=0x"); pw.println(Integer.toHexString(p.perm.info.flags));
- }
- }
- if (p.packageSetting != null) {
- pw.print(" packageSetting="); pw.println(p.packageSetting);
- }
- if (READ_EXTERNAL_STORAGE.equals(p.name)) {
- pw.print(" enforced=");
- pw.println(mReadExternalStorageEnforced);
- }
- }
+ mPermissions.dumpPermissions(pw, packageName, permissionNames,
+ (mReadExternalStorageEnforced == Boolean.TRUE), dumpState);
}
void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
@@ -5248,7 +5094,7 @@ final class Settings {
private final Handler mHandler = new MyHandler();
- private final Object mLock;
+ private final Object mPersistenceLock;
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5265,8 +5111,8 @@ final class Settings {
// The mapping keys are user ids.
private final SparseBooleanArray mDefaultPermissionsGranted = new SparseBooleanArray();
- public RuntimePermissionPersistence(Object lock) {
- mLock = lock;
+ public RuntimePermissionPersistence(Object persistenceLock) {
+ mPersistenceLock = persistenceLock;
}
public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) {
@@ -5321,7 +5167,7 @@ final class Settings {
ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>();
ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>();
- synchronized (mLock) {
+ synchronized (mPersistenceLock) {
mWriteScheduled.delete(userId);
final int packageCount = mPackages.size();
@@ -5470,7 +5316,7 @@ final class Settings {
PermissionsState permissionsState = sb.getPermissionsState();
for (PermissionState permissionState
: permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mPermissions.get(permissionState.getName());
+ BasePermission bp = mPermissions.getPermission(permissionState.getName());
if (bp != null) {
permissionsState.revokeRuntimePermission(bp, userId);
permissionsState.updatePermissionFlags(bp, userId,
@@ -5631,7 +5477,7 @@ final class Settings {
switch (parser.getName()) {
case TAG_ITEM: {
String name = parser.getAttributeValue(null, ATTR_NAME);
- BasePermission bp = mPermissions.get(name);
+ BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
XmlUtils.skipCurrentTag(parser);
diff --git a/com/android/server/pm/SharedUserSetting.java b/com/android/server/pm/SharedUserSetting.java
index 06e020a0..a0dadae3 100644
--- a/com/android/server/pm/SharedUserSetting.java
+++ b/com/android/server/pm/SharedUserSetting.java
@@ -16,12 +16,18 @@
package com.android.server.pm;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
import android.util.ArraySet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
/**
* Settings data for a particular shared user ID we know about.
*/
-final class SharedUserSetting extends SettingBase {
+public final class SharedUserSetting extends SettingBase {
final String name;
int userId;
@@ -73,4 +79,18 @@ final class SharedUserSetting extends SettingBase {
setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
}
}
+
+ public @Nullable List<PackageParser.Package> getPackages() {
+ if (packages == null || packages.size() == 0) {
+ return null;
+ }
+ final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size());
+ for (PackageSetting ps : packages) {
+ if (ps == null) {
+ continue;
+ }
+ pkgList.add(ps.pkg);
+ }
+ return pkgList;
+ }
}
diff --git a/com/android/server/pm/ShortcutLauncher.java b/com/android/server/pm/ShortcutLauncher.java
index 30608403..f922ad19 100644
--- a/com/android/server/pm/ShortcutLauncher.java
+++ b/com/android/server/pm/ShortcutLauncher.java
@@ -25,6 +25,7 @@ import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.json.JSONException;
@@ -293,7 +294,7 @@ class ShortcutLauncher extends ShortcutPackageItem {
return ret;
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
pw.println();
pw.print(prefix);
diff --git a/com/android/server/pm/ShortcutPackage.java b/com/android/server/pm/ShortcutPackage.java
index 6f70f4c8..6fc1e738 100644
--- a/com/android/server/pm/ShortcutPackage.java
+++ b/com/android/server/pm/ShortcutPackage.java
@@ -33,6 +33,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.Stats;
@@ -1144,7 +1145,7 @@ class ShortcutPackage extends ShortcutPackageItem {
return false;
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
pw.println();
pw.print(prefix);
@@ -1186,9 +1187,7 @@ class ShortcutPackage extends ShortcutPackageItem {
final int size = shortcuts.size();
for (int i = 0; i < size; i++) {
final ShortcutInfo si = shortcuts.valueAt(i);
- pw.print(prefix);
- pw.print(" ");
- pw.println(si.toInsecureString());
+ pw.println(si.toDumpString(prefix + " "));
if (si.getBitmapPath() != null) {
final long len = new File(si.getBitmapPath()).length();
pw.print(prefix);
diff --git a/com/android/server/pm/ShortcutService.java b/com/android/server/pm/ShortcutService.java
index 0e572d82..27560c5f 100644
--- a/com/android/server/pm/ShortcutService.java
+++ b/com/android/server/pm/ShortcutService.java
@@ -40,8 +40,8 @@ import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
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;
@@ -134,6 +134,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.regex.Pattern;
/**
* TODO:
@@ -484,12 +485,13 @@ public class ShortcutService extends IShortcutService.Stub {
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
- handleOnUidStateChanged(uid, procState);
+ injectPostToHandler(() -> handleOnUidStateChanged(uid, procState));
}
@Override
public void onUidGone(int uid, boolean disabled) {
- handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+ injectPostToHandler(() ->
+ handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT));
}
@Override
@@ -3454,121 +3456,265 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final DumpFilter filter = parseDumpArgs(args);
- boolean dumpMain = true;
- boolean checkin = false;
- boolean clear = false;
- boolean dumpUid = false;
- boolean dumpFiles = false;
+ if (filter.shouldDumpCheckIn()) {
+ // Other flags are not supported for checkin.
+ dumpCheckin(pw, filter.shouldCheckInClear());
+ } else {
+ if (filter.shouldDumpMain()) {
+ dumpInner(pw, filter);
+ pw.println();
+ }
+ if (filter.shouldDumpUid()) {
+ dumpUid(pw);
+ pw.println();
+ }
+ if (filter.shouldDumpFiles()) {
+ dumpDumpFiles(pw);
+ pw.println();
+ }
+ }
+ }
- if (args != null) {
- for (String arg : args) {
- if ("-c".equals(arg)) {
- checkin = true;
+ private static DumpFilter parseDumpArgs(String[] args) {
+ final DumpFilter filter = new DumpFilter();
+ if (args == null) {
+ return filter;
+ }
- } else if ("--checkin".equals(arg)) {
- checkin = true;
- clear = true;
+ int argIndex = 0;
+ while (argIndex < args.length) {
+ final String arg = args[argIndex++];
- } else if ("-a".equals(arg) || "--all".equals(arg)) {
- dumpUid = true;
- dumpFiles = true;
+ if ("-c".equals(arg)) {
+ filter.setDumpCheckIn(true);
+ continue;
+ }
+ if ("--checkin".equals(arg)) {
+ filter.setDumpCheckIn(true);
+ filter.setCheckInClear(true);
+ continue;
+ }
+ if ("-a".equals(arg) || "--all".equals(arg)) {
+ filter.setDumpUid(true);
+ filter.setDumpFiles(true);
+ continue;
+ }
+ if ("-u".equals(arg) || "--uid".equals(arg)) {
+ filter.setDumpUid(true);
+ continue;
+ }
+ if ("-f".equals(arg) || "--files".equals(arg)) {
+ filter.setDumpFiles(true);
+ continue;
+ }
+ if ("-n".equals(arg) || "--no-main".equals(arg)) {
+ filter.setDumpMain(false);
+ continue;
+ }
+ if ("--user".equals(arg)) {
+ if (argIndex >= args.length) {
+ throw new IllegalArgumentException("Missing user ID for --user");
+ }
+ try {
+ filter.addUser(Integer.parseInt(args[argIndex++]));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid user ID", e);
+ }
+ continue;
+ }
+ if ("-p".equals(arg) || "--package".equals(arg)) {
+ if (argIndex >= args.length) {
+ throw new IllegalArgumentException("Missing package name for --package");
+ }
+ filter.addPackageRegex(args[argIndex++]);
+ filter.setDumpDetails(false);
+ continue;
+ }
+ if (arg.startsWith("-")) {
+ throw new IllegalArgumentException("Unknown option " + arg);
+ }
+ break;
+ }
+ while (argIndex < args.length) {
+ filter.addPackage(args[argIndex++]);
+ }
+ return filter;
+ }
- } else if ("-u".equals(arg) || "--uid".equals(arg)) {
- dumpUid = true;
+ static class DumpFilter {
+ private boolean mDumpCheckIn = false;
+ private boolean mCheckInClear = false;
- } else if ("-f".equals(arg) || "--files".equals(arg)) {
- dumpFiles = true;
+ private boolean mDumpMain = true;
+ private boolean mDumpUid = false;
+ private boolean mDumpFiles = false;
- } else if ("-n".equals(arg) || "--no-main".equals(arg)) {
- dumpMain = false;
+ private boolean mDumpDetails = true;
+ private List<Pattern> mPackagePatterns = new ArrayList<>();
+ private List<Integer> mUsers = new ArrayList<>();
+
+ void addPackageRegex(String regex) {
+ mPackagePatterns.add(Pattern.compile(regex));
+ }
+
+ public void addPackage(String packageName) {
+ addPackageRegex(Pattern.quote(packageName));
+ }
+
+ void addUser(int userId) {
+ mUsers.add(userId);
+ }
+
+ boolean isPackageMatch(String packageName) {
+ if (mPackagePatterns.size() == 0) {
+ return true;
+ }
+ for (int i = 0; i < mPackagePatterns.size(); i++) {
+ if (mPackagePatterns.get(i).matcher(packageName).find()) {
+ return true;
}
}
+ return false;
}
- if (checkin) {
- // Other flags are not supported for checkin.
- dumpCheckin(pw, clear);
- } else {
- if (dumpMain) {
- dumpInner(pw);
- pw.println();
- }
- if (dumpUid) {
- dumpUid(pw);
- pw.println();
+ boolean isUserMatch(int userId) {
+ if (mUsers.size() == 0) {
+ return true;
}
- if (dumpFiles) {
- dumpDumpFiles(pw);
- pw.println();
+ for (int i = 0; i < mUsers.size(); i++) {
+ if (mUsers.get(i) == userId) {
+ return true;
+ }
}
+ return false;
+ }
+
+ public boolean shouldDumpCheckIn() {
+ return mDumpCheckIn;
+ }
+
+ public void setDumpCheckIn(boolean dumpCheckIn) {
+ mDumpCheckIn = dumpCheckIn;
+ }
+
+ public boolean shouldCheckInClear() {
+ return mCheckInClear;
+ }
+
+ public void setCheckInClear(boolean checkInClear) {
+ mCheckInClear = checkInClear;
+ }
+
+ public boolean shouldDumpMain() {
+ return mDumpMain;
+ }
+
+ public void setDumpMain(boolean dumpMain) {
+ mDumpMain = dumpMain;
+ }
+
+ public boolean shouldDumpUid() {
+ return mDumpUid;
+ }
+
+ public void setDumpUid(boolean dumpUid) {
+ mDumpUid = dumpUid;
+ }
+
+ public boolean shouldDumpFiles() {
+ return mDumpFiles;
+ }
+
+ public void setDumpFiles(boolean dumpFiles) {
+ mDumpFiles = dumpFiles;
+ }
+
+ public boolean shouldDumpDetails() {
+ return mDumpDetails;
+ }
+
+ public void setDumpDetails(boolean dumpDetails) {
+ mDumpDetails = dumpDetails;
}
}
private void dumpInner(PrintWriter pw) {
+ dumpInner(pw, new DumpFilter());
+ }
+
+ private void dumpInner(PrintWriter pw, DumpFilter filter) {
synchronized (mLock) {
- final long now = injectCurrentTimeMillis();
- pw.print("Now: [");
- pw.print(now);
- pw.print("] ");
- pw.print(formatTime(now));
-
- pw.print(" Raw last reset: [");
- pw.print(mRawLastResetTime);
- pw.print("] ");
- pw.print(formatTime(mRawLastResetTime));
-
- final long last = getLastResetTimeLocked();
- pw.print(" Last reset: [");
- pw.print(last);
- pw.print("] ");
- pw.print(formatTime(last));
-
- final long next = getNextResetTimeLocked();
- pw.print(" Next reset: [");
- pw.print(next);
- pw.print("] ");
- pw.print(formatTime(next));
-
- pw.print(" Config:");
- pw.print(" Max icon dim: ");
- pw.println(mMaxIconDimension);
- pw.print(" Icon format: ");
- pw.println(mIconPersistFormat);
- pw.print(" Icon quality: ");
- pw.println(mIconPersistQuality);
- pw.print(" saveDelayMillis: ");
- pw.println(mSaveDelayMillis);
- pw.print(" resetInterval: ");
- pw.println(mResetInterval);
- pw.print(" maxUpdatesPerInterval: ");
- pw.println(mMaxUpdatesPerInterval);
- pw.print(" maxShortcutsPerActivity: ");
- pw.println(mMaxShortcuts);
- pw.println();
+ if (filter.shouldDumpDetails()) {
+ final long now = injectCurrentTimeMillis();
+ pw.print("Now: [");
+ pw.print(now);
+ pw.print("] ");
+ pw.print(formatTime(now));
+
+ pw.print(" Raw last reset: [");
+ pw.print(mRawLastResetTime);
+ pw.print("] ");
+ pw.print(formatTime(mRawLastResetTime));
+
+ final long last = getLastResetTimeLocked();
+ pw.print(" Last reset: [");
+ pw.print(last);
+ pw.print("] ");
+ pw.print(formatTime(last));
+
+ final long next = getNextResetTimeLocked();
+ pw.print(" Next reset: [");
+ pw.print(next);
+ pw.print("] ");
+ pw.print(formatTime(next));
+
+ pw.print(" Config:");
+ pw.print(" Max icon dim: ");
+ pw.println(mMaxIconDimension);
+ pw.print(" Icon format: ");
+ pw.println(mIconPersistFormat);
+ pw.print(" Icon quality: ");
+ pw.println(mIconPersistQuality);
+ pw.print(" saveDelayMillis: ");
+ pw.println(mSaveDelayMillis);
+ pw.print(" resetInterval: ");
+ pw.println(mResetInterval);
+ pw.print(" maxUpdatesPerInterval: ");
+ pw.println(mMaxUpdatesPerInterval);
+ pw.print(" maxShortcutsPerActivity: ");
+ pw.println(mMaxShortcuts);
+ pw.println();
- pw.println(" Stats:");
- synchronized (mStatLock) {
- for (int i = 0; i < Stats.COUNT; i++) {
- dumpStatLS(pw, " ", i);
+ pw.println(" Stats:");
+ synchronized (mStatLock) {
+ for (int i = 0; i < Stats.COUNT; i++) {
+ dumpStatLS(pw, " ", i);
+ }
}
- }
- pw.println();
- pw.print(" #Failures: ");
- pw.println(mWtfCount);
+ pw.println();
+ pw.print(" #Failures: ");
+ pw.println(mWtfCount);
- if (mLastWtfStacktrace != null) {
- pw.print(" Last failure stack trace: ");
- pw.println(Log.getStackTraceString(mLastWtfStacktrace));
- }
+ if (mLastWtfStacktrace != null) {
+ pw.print(" Last failure stack trace: ");
+ pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ }
- pw.println();
- mShortcutBitmapSaver.dumpLocked(pw, " ");
+ pw.println();
+ mShortcutBitmapSaver.dumpLocked(pw, " ");
- for (int i = 0; i < mUsers.size(); i++) {
pw.println();
- mUsers.valueAt(i).dump(pw, " ");
+ }
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ final ShortcutUser user = mUsers.valueAt(i);
+ if (filter.isUserMatch(user.getUserId())) {
+ user.dump(pw, " ", filter);
+ pw.println();
+ }
}
}
}
diff --git a/com/android/server/pm/ShortcutUser.java b/com/android/server/pm/ShortcutUser.java
index 2c388c4a..55e6d28a 100644
--- a/com/android/server/pm/ShortcutUser.java
+++ b/com/android/server/pm/ShortcutUser.java
@@ -28,6 +28,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.InvalidFileFormatException;
import libcore.util.Objects;
@@ -531,44 +532,54 @@ class ShortcutUser {
+ " S=" + restoredShortcuts[0]);
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
- pw.print(prefix);
- pw.print("User: ");
- pw.print(mUserId);
- pw.print(" Known locales: ");
- pw.print(mKnownLocales);
- pw.print(" Last app scan: [");
- pw.print(mLastAppScanTime);
- pw.print("] ");
- pw.print(ShortcutService.formatTime(mLastAppScanTime));
- pw.print(" Last app scan FP: ");
- pw.print(mLastAppScanOsFingerprint);
- pw.println();
-
- prefix += prefix + " ";
-
- pw.print(prefix);
- pw.print("Cached launcher: ");
- pw.print(mCachedLauncher);
- pw.println();
-
- pw.print(prefix);
- pw.print("Last known launcher: ");
- pw.print(mLastKnownLauncher);
- pw.println();
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
+ if (filter.shouldDumpDetails()) {
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.print(mUserId);
+ pw.print(" Known locales: ");
+ pw.print(mKnownLocales);
+ pw.print(" Last app scan: [");
+ pw.print(mLastAppScanTime);
+ pw.print("] ");
+ pw.print(ShortcutService.formatTime(mLastAppScanTime));
+ pw.print(" Last app scan FP: ");
+ pw.print(mLastAppScanOsFingerprint);
+ pw.println();
+
+ prefix += prefix + " ";
+
+ pw.print(prefix);
+ pw.print("Cached launcher: ");
+ pw.print(mCachedLauncher);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print("Last known launcher: ");
+ pw.print(mLastKnownLauncher);
+ pw.println();
+ }
for (int i = 0; i < mLaunchers.size(); i++) {
- mLaunchers.valueAt(i).dump(pw, prefix);
+ ShortcutLauncher launcher = mLaunchers.valueAt(i);
+ if (filter.isPackageMatch(launcher.getPackageName())) {
+ launcher.dump(pw, prefix, filter);
+ }
}
for (int i = 0; i < mPackages.size(); i++) {
- mPackages.valueAt(i).dump(pw, prefix);
+ ShortcutPackage pkg = mPackages.valueAt(i);
+ if (filter.isPackageMatch(pkg.getPackageName())) {
+ pkg.dump(pw, prefix, filter);
+ }
}
- pw.println();
- pw.print(prefix);
- pw.println("Bitmap directories: ");
- dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId));
+ if (filter.shouldDumpDetails()) {
+ pw.println();
+ pw.print(prefix);
+ pw.println("Bitmap directories: ");
+ dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId));
+ }
}
private void dumpDirectorySize(@NonNull PrintWriter pw,
diff --git a/com/android/server/pm/UserManagerService.java b/com/android/server/pm/UserManagerService.java
index f2d527b2..1e5245cf 100644
--- a/com/android/server/pm/UserManagerService.java
+++ b/com/android/server/pm/UserManagerService.java
@@ -1055,7 +1055,7 @@ public class UserManagerService extends IUserManager.Stub {
/** Called by PackageManagerService */
public boolean exists(int userId) {
- return getUserInfoNoChecks(userId) != null;
+ return mLocalService.exists(userId);
}
@Override
@@ -3502,8 +3502,8 @@ public class UserManagerService extends IUserManager.Stub {
* @param userId
* @return whether the user has been initialized yet
*/
- boolean isInitialized(int userId) {
- return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+ boolean isUserInitialized(int userId) {
+ return mLocalService.isUserInitialized(userId);
}
private class LocalService extends UserManagerInternal {
@@ -3715,6 +3715,16 @@ public class UserManagerService extends IUserManager.Stub {
}
return state == UserState.STATE_RUNNING_UNLOCKED;
}
+
+ @Override
+ public boolean isUserInitialized(int userId) {
+ return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+ }
+
+ @Override
+ public boolean exists(int userId) {
+ return getUserInfoNoChecks(userId) != null;
+ }
}
/* Remove all the users except of the system one. */
diff --git a/com/android/server/pm/permission/BasePermission.java b/com/android/server/pm/permission/BasePermission.java
new file mode 100644
index 00000000..09a6e9c0
--- /dev/null
+++ b/com/android/server/pm/permission/BasePermission.java
@@ -0,0 +1,564 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_NORMAL;
+import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
+import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
+
+import static com.android.server.pm.Settings.ATTR_NAME;
+import static com.android.server.pm.Settings.ATTR_PACKAGE;
+import static com.android.server.pm.Settings.TAG_ITEM;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Permission;
+import android.content.pm.PermissionInfo;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSettingBase;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public final class BasePermission {
+ static final String TAG = "PackageManager";
+
+ public static final int TYPE_NORMAL = 0;
+ public static final int TYPE_BUILTIN = 1;
+ public static final int TYPE_DYNAMIC = 2;
+ @IntDef(value = {
+ TYPE_NORMAL,
+ TYPE_BUILTIN,
+ TYPE_DYNAMIC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionType {}
+
+ @IntDef(value = {
+ PROTECTION_DANGEROUS,
+ PROTECTION_NORMAL,
+ PROTECTION_SIGNATURE,
+ PROTECTION_SIGNATURE_OR_SYSTEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtectionLevel {}
+
+ final String name;
+
+ @PermissionType final int type;
+
+ String sourcePackageName;
+
+ // TODO: Can we get rid of this? Seems we only use some signature info from the setting
+ PackageSettingBase sourcePackageSetting;
+
+ int protectionLevel;
+
+ PackageParser.Permission perm;
+
+ PermissionInfo pendingPermissionInfo;
+
+ /** UID that owns the definition of this permission */
+ int uid;
+
+ /** Additional GIDs given to apps granted this permission */
+ private int[] gids;
+
+ /**
+ * Flag indicating that {@link #gids} should be adjusted based on the
+ * {@link UserHandle} the granted app is running as.
+ */
+ private boolean perUser;
+
+ public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
+ name = _name;
+ sourcePackageName = _sourcePackageName;
+ type = _type;
+ // Default to most conservative protection level.
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ @Override
+ public String toString() {
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ + "}";
+ }
+
+ public String getName() {
+ return name;
+ }
+ public int getProtectionLevel() {
+ return protectionLevel;
+ }
+ public String getSourcePackageName() {
+ return sourcePackageName;
+ }
+ public PackageSettingBase getSourcePackageSetting() {
+ return sourcePackageSetting;
+ }
+ public int getType() {
+ return type;
+ }
+ public int getUid() {
+ return uid;
+ }
+ public void setGids(int[] gids, boolean perUser) {
+ this.gids = gids;
+ this.perUser = perUser;
+ }
+ public void setPermission(@Nullable Permission perm) {
+ this.perm = perm;
+ }
+ public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) {
+ this.sourcePackageSetting = sourcePackageSetting;
+ }
+
+ public int[] computeGids(int userId) {
+ if (perUser) {
+ final int[] userGids = new int[gids.length];
+ for (int i = 0; i < gids.length; i++) {
+ userGids[i] = UserHandle.getUid(userId, gids[i]);
+ }
+ return userGids;
+ } else {
+ return gids;
+ }
+ }
+
+ public int calculateFootprint(BasePermission perm) {
+ if (uid == perm.uid) {
+ return perm.name.length() + perm.perm.info.calculateFootprint();
+ }
+ return 0;
+ }
+
+ public boolean isPermission(Permission perm) {
+ return this.perm == perm;
+ }
+
+ public boolean isDynamic() {
+ return type == TYPE_DYNAMIC;
+ }
+
+
+ public boolean isNormal() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_NORMAL;
+ }
+ public boolean isRuntime() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+ public boolean isSignature() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
+ PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ public boolean isAppOp() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ }
+ public boolean isDevelopment() {
+ return isSignature()
+ && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
+ }
+ public boolean isInstaller() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
+ }
+ public boolean isInstant() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+ }
+ public boolean isOEM() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
+ }
+ public boolean isPre23() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
+ }
+ public boolean isPreInstalled() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
+ }
+ public boolean isPrivileged() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ }
+ public boolean isRuntimeOnly() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ }
+ public boolean isSetup() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
+ }
+ public boolean isVerifier() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
+ }
+
+ public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
+ if (!origPackageName.equals(sourcePackageName)) {
+ return;
+ }
+ sourcePackageName = newPackageName;
+ sourcePackageSetting = null;
+ perm = null;
+ if (pendingPermissionInfo != null) {
+ pendingPermissionInfo.packageName = newPackageName;
+ }
+ uid = 0;
+ setGids(null, false);
+ }
+
+ public boolean addToTree(@ProtectionLevel int protectionLevel,
+ @NonNull PermissionInfo info, @NonNull BasePermission tree) {
+ final boolean changed =
+ (this.protectionLevel != protectionLevel
+ || perm == null
+ || uid != tree.uid
+ || !perm.owner.equals(tree.perm.owner)
+ || !comparePermissionInfos(perm.info, info));
+ this.protectionLevel = protectionLevel;
+ info = new PermissionInfo(info);
+ info.protectionLevel = protectionLevel;
+ perm = new PackageParser.Permission(tree.perm.owner, info);
+ perm.info.packageName = tree.perm.info.packageName;
+ uid = tree.uid;
+ return changed;
+ }
+
+ 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 = findPermissionTreeLP(permissionTrees, name);
+ if (tree != null && tree.perm != null) {
+ sourcePackageSetting = tree.sourcePackageSetting;
+ perm = new PackageParser.Permission(tree.perm.owner,
+ new PermissionInfo(pendingPermissionInfo));
+ perm.info.packageName = tree.perm.info.packageName;
+ perm.info.name = name;
+ uid = tree.uid;
+ }
+ }
+ }
+
+ 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
+ if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) {
+ final boolean currentOwnerIsSystem = (bp.perm != null
+ && bp.perm.owner.isSystemApp());
+ if (p.owner.isSystemApp()) {
+ if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
+ // It's a built-in permission and no owner, take ownership now
+ bp.sourcePackageSetting = pkgSetting;
+ bp.perm = p;
+ bp.uid = pkg.applicationInfo.uid;
+ bp.sourcePackageName = p.info.packageName;
+ p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+ } else if (!currentOwnerIsSystem) {
+ String msg = "New decl " + p.owner + " of permission "
+ + p.info.name + " is system; overriding " + bp.sourcePackageName;
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ bp = null;
+ }
+ }
+ }
+ if (bp == null) {
+ bp = new BasePermission(p.info.name, p.info.packageName, TYPE_NORMAL);
+ }
+ StringBuilder r = null;
+ if (bp.perm == null) {
+ if (bp.sourcePackageName == null
+ || bp.sourcePackageName.equals(p.info.packageName)) {
+ final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
+ if (tree == null
+ || tree.sourcePackageName.equals(p.info.packageName)) {
+ bp.sourcePackageSetting = pkgSetting;
+ bp.perm = p;
+ bp.uid = pkg.applicationInfo.uid;
+ bp.sourcePackageName = p.info.packageName;
+ p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(p.info.name);
+ }
+ } else {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " ignored: base tree "
+ + tree.name + " is from package "
+ + tree.sourcePackageName);
+ }
+ } else {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " ignored: original from "
+ + bp.sourcePackageName);
+ }
+ } else if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append("DUP:");
+ r.append(p.info.name);
+ }
+ if (bp.perm == p) {
+ bp.protectionLevel = p.info.protectionLevel;
+ }
+ if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
+ Log.d(TAG, " Permissions: " + r);
+ }
+ return bp;
+ }
+
+ public static BasePermission enforcePermissionTreeLP(
+ Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
+ if (permName != null) {
+ BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
+ if (bp != null) {
+ if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
+ return bp;
+ }
+ throw new SecurityException("Calling uid " + callingUid
+ + " is not allowed to add to permission tree "
+ + bp.name + " owned by uid " + bp.uid);
+ }
+ }
+ throw new SecurityException("No permission tree found for " + permName);
+ }
+
+ public void enforceDeclaredUsedAndRuntimeOrDevelopment(PackageParser.Package pkg) {
+ int index = pkg.requestedPermissions.indexOf(name);
+ if (index == -1) {
+ throw new SecurityException("Package " + pkg.packageName
+ + " has not requested permission " + name);
+ }
+ if (!isRuntime() && !isDevelopment()) {
+ throw new SecurityException("Permission " + name
+ + " is not a changeable permission type");
+ }
+ }
+
+ 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()) == '.') {
+ return bp;
+ }
+ }
+ return null;
+ }
+
+ public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
+ if (groupName == null) {
+ if (perm == null || perm.info.group == null) {
+ return generatePermissionInfo(protectionLevel, flags);
+ }
+ } else {
+ if (perm != null && groupName.equals(perm.info.group)) {
+ return PackageParser.generatePermissionInfo(perm, flags);
+ }
+ }
+ return null;
+ }
+
+ public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) {
+ final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
+ // if we return different protection level, don't use the cached info
+ if (perm != null && !protectionLevelChanged) {
+ return PackageParser.generatePermissionInfo(perm, flags);
+ }
+ final PermissionInfo pi = new PermissionInfo();
+ pi.name = name;
+ pi.packageName = sourcePackageName;
+ pi.nonLocalizedLabel = name;
+ pi.protectionLevel = protectionLevelChanged ? adjustedProtectionLevel : protectionLevel;
+ return pi;
+ }
+
+ public static boolean readLPw(@NonNull Map<String, BasePermission> out,
+ @NonNull XmlPullParser parser) {
+ final String tagName = parser.getName();
+ if (!tagName.equals(TAG_ITEM)) {
+ return false;
+ }
+ final String name = parser.getAttributeValue(null, ATTR_NAME);
+ final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE);
+ final String ptype = parser.getAttributeValue(null, "type");
+ if (name == null || sourcePackage == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: permissions has" + " no name at "
+ + parser.getPositionDescription());
+ return false;
+ }
+ final boolean dynamic = "dynamic".equals(ptype);
+ BasePermission bp = out.get(name);
+ // If the permission is builtin, do not clobber it.
+ if (bp == null || bp.type != TYPE_BUILTIN) {
+ bp = new BasePermission(name.intern(), sourcePackage,
+ dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
+ }
+ bp.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_NORMAL);
+ bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ if (dynamic) {
+ final PermissionInfo pi = new PermissionInfo();
+ pi.packageName = sourcePackage.intern();
+ pi.name = name.intern();
+ pi.icon = readInt(parser, null, "icon", 0);
+ pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
+ pi.protectionLevel = bp.protectionLevel;
+ bp.pendingPermissionInfo = pi;
+ }
+ out.put(bp.name, bp);
+ return true;
+ }
+
+ private static int readInt(XmlPullParser parser, String ns, String name, int defValue) {
+ String v = parser.getAttributeValue(ns, name);
+ try {
+ if (v == null) {
+ return defValue;
+ }
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: attribute " + name
+ + " has bad integer value " + v + " at "
+ + parser.getPositionDescription());
+ }
+ return defValue;
+ }
+
+ public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
+ if (sourcePackageName == null) {
+ return;
+ }
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
+ if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection", Integer.toString(protectionLevel));
+ }
+ if (type == BasePermission.TYPE_DYNAMIC) {
+ final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
+ if (pi != null) {
+ serializer.attribute(null, "type", "dynamic");
+ if (pi.icon != 0) {
+ serializer.attribute(null, "icon", Integer.toString(pi.icon));
+ }
+ if (pi.nonLocalizedLabel != null) {
+ serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
+ }
+ }
+ }
+ serializer.endTag(null, TAG_ITEM);
+ }
+
+ private static boolean compareStrings(CharSequence s1, CharSequence s2) {
+ if (s1 == null) {
+ return s2 == null;
+ }
+ if (s2 == null) {
+ return false;
+ }
+ if (s1.getClass() != s2.getClass()) {
+ return false;
+ }
+ return s1.equals(s2);
+ }
+
+ private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
+ if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
+ if (pi1.protectionLevel != pi2.protectionLevel) return false;
+ if (!compareStrings(pi1.name, pi2.name)) return false;
+ if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
+ // We'll take care of setting this one.
+ if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ // These are not currently stored in settings.
+ //if (!compareStrings(pi1.group, pi2.group)) return false;
+ //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
+ //if (pi1.labelRes != pi2.labelRes) return false;
+ //if (pi1.descriptionRes != pi2.descriptionRes) return false;
+ return true;
+ }
+
+ public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
+ @NonNull Set<String> permissionNames, boolean readEnforced,
+ boolean printedSomething, @NonNull DumpState dumpState) {
+ if (packageName != null && !packageName.equals(sourcePackageName)) {
+ return false;
+ }
+ if (permissionNames != null && !permissionNames.contains(name)) {
+ return false;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Permissions:");
+ printedSomething = true;
+ }
+ pw.print(" Permission ["); pw.print(name); pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(this)));
+ pw.println("):");
+ pw.print(" sourcePackage="); pw.println(sourcePackageName);
+ pw.print(" uid="); pw.print(uid);
+ pw.print(" gids="); pw.print(Arrays.toString(
+ computeGids(UserHandle.USER_SYSTEM)));
+ pw.print(" type="); pw.print(type);
+ pw.print(" prot=");
+ pw.println(PermissionInfo.protectionToString(protectionLevel));
+ if (perm != null) {
+ pw.print(" perm="); pw.println(perm);
+ if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.info.flags));
+ }
+ }
+ if (sourcePackageSetting != null) {
+ pw.print(" packageSetting="); pw.println(sourcePackageSetting);
+ }
+ if (READ_EXTERNAL_STORAGE.equals(name)) {
+ pw.print(" enforced=");
+ pw.println(readEnforced);
+ }
+ return true;
+ }
+}
diff --git a/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
new file mode 100644
index 00000000..161efd38
--- /dev/null
+++ b/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -0,0 +1,1296 @@
+/*
+ * 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.server.pm.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.DownloadManager;
+import android.app.admin.DevicePolicyManager;
+import android.companion.CompanionDeviceManager;
+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.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManagerInternal.PackagesProvider;
+import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.print.PrintManager;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.TelephonyManager;
+import android.security.Credentials;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSetting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+/**
+ * This class is the policy for granting runtime permissions to
+ * platform components and default handlers in the system such
+ * that the device is usable out-of-the-box. For example, the
+ * shell UID is a part of the system and the Phone app should
+ * have phone related permission by default.
+ * <p>
+ * NOTE: This class is at the wrong abstraction level. It is a part of the package manager
+ * service but knows about lots of higher level subsystems. The correct way to do this is
+ * to have an interface defined in the package manager but have the impl next to other
+ * policy stuff like PhoneWindowManager
+ */
+public final class DefaultPermissionGrantPolicy {
+ private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
+ private static final boolean DEBUG = false;
+
+ private static final int DEFAULT_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+ private static final String AUDIO_MIME_TYPE = "audio/mpeg";
+
+ private static final String TAG_EXCEPTIONS = "exceptions";
+ private static final String TAG_EXCEPTION = "exception";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FIXED = "fixed";
+
+ private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
+ static {
+ PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
+ PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
+ PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
+ PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
+ PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
+ PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
+ PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
+ }
+
+ private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
+ static {
+ CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
+ CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
+ CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
+ }
+
+ private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
+ static {
+ LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
+ static {
+ CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
+ CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
+ }
+
+ private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
+ static {
+ SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
+ SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
+ }
+
+ private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
+ static {
+ MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
+ }
+
+ private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
+ static {
+ CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
+ }
+
+ private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
+ static {
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ }
+
+ private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
+ static {
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
+
+ private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+
+ private static final String ACTION_TRACK = "com.android.fitness.TRACK";
+
+ private final Handler mHandler;
+
+ private PackagesProvider mLocationPackagesProvider;
+ private PackagesProvider mVoiceInteractionPackagesProvider;
+ private PackagesProvider mSmsAppPackagesProvider;
+ private PackagesProvider mDialerAppPackagesProvider;
+ private PackagesProvider mSimCallManagerPackagesProvider;
+ private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
+
+ private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final PackageManagerInternal mServiceInternal;
+ private final PermissionManagerService mPermissionManager;
+ private final DefaultPermissionGrantedCallback mPermissionGrantedCallback;
+ public interface DefaultPermissionGrantedCallback {
+ /** Callback when permissions have been granted */
+ public void onDefaultRuntimePermissionsGranted(int userId);
+ }
+
+ public DefaultPermissionGrantPolicy(Context context, Looper looper,
+ @Nullable DefaultPermissionGrantedCallback callback,
+ @NonNull PermissionManagerService permissionManager) {
+ mContext = context;
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
+ synchronized (mLock) {
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLocked();
+ }
+ }
+ }
+ }
+ };
+ mPermissionGrantedCallback = callback;
+ mPermissionManager = permissionManager;
+ mServiceInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public void setLocationPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mLocationPackagesProvider = provider;
+ }
+ }
+
+ public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mVoiceInteractionPackagesProvider = provider;
+ }
+ }
+
+ public void setSmsAppPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mSmsAppPackagesProvider = provider;
+ }
+ }
+
+ public void setDialerAppPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mDialerAppPackagesProvider = provider;
+ }
+ }
+
+ public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mSimCallManagerPackagesProvider = provider;
+ }
+ }
+
+ public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
+ synchronized (mLock) {
+ mSyncAdapterPackagesProvider = provider;
+ }
+ }
+
+ public void grantDefaultPermissions(Collection<PackageParser.Package> packages, int userId) {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+ grantAllRuntimePermissions(packages, userId);
+ } else {
+ grantPermissionsToSysComponentsAndPrivApps(packages, userId);
+ grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
+ }
+ }
+
+ private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
+ Set<String> permissions = new ArraySet<>();
+ for (String permission : pkg.requestedPermissions) {
+ final BasePermission bp = mPermissionManager.getPermission(permission);
+ if (bp == null) {
+ continue;
+ }
+ if (bp.isRuntime()) {
+ permissions.add(permission);
+ }
+ }
+ if (!permissions.isEmpty()) {
+ grantRuntimePermissions(pkg, permissions, true, userId);
+ }
+ }
+
+ private void grantAllRuntimePermissions(
+ Collection<PackageParser.Package> packages, int userId) {
+ Log.i(TAG, "Granting all runtime permissions for user " + userId);
+ for (PackageParser.Package pkg : packages) {
+ grantRuntimePermissionsForPackage(userId, pkg);
+ }
+ }
+
+ public void scheduleReadDefaultPermissionExceptions() {
+ mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+ }
+
+ private void grantPermissionsToSysComponentsAndPrivApps(
+ Collection<PackageParser.Package> packages, int userId) {
+ Log.i(TAG, "Granting permissions to platform components for user " + userId);
+ for (PackageParser.Package pkg : packages) {
+ if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
+ || !doesPackageSupportRuntimePermissions(pkg)
+ || pkg.requestedPermissions.isEmpty()) {
+ continue;
+ }
+ grantRuntimePermissionsForPackage(userId, pkg);
+ }
+ }
+
+ private void grantDefaultSystemHandlerPermissions(int userId) {
+ Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
+
+ final PackagesProvider locationPackagesProvider;
+ final PackagesProvider voiceInteractionPackagesProvider;
+ final PackagesProvider smsAppPackagesProvider;
+ final PackagesProvider dialerAppPackagesProvider;
+ final PackagesProvider simCallManagerPackagesProvider;
+ final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
+
+ synchronized (mLock) {
+ locationPackagesProvider = mLocationPackagesProvider;
+ voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
+ smsAppPackagesProvider = mSmsAppPackagesProvider;
+ dialerAppPackagesProvider = mDialerAppPackagesProvider;
+ simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
+ syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
+ }
+
+ String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
+ ? voiceInteractionPackagesProvider.getPackages(userId) : null;
+ String[] locationPackageNames = (locationPackagesProvider != null)
+ ? locationPackagesProvider.getPackages(userId) : null;
+ String[] smsAppPackageNames = (smsAppPackagesProvider != null)
+ ? smsAppPackagesProvider.getPackages(userId) : null;
+ String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
+ ? dialerAppPackagesProvider.getPackages(userId) : null;
+ String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
+ ? simCallManagerPackagesProvider.getPackages(userId) : null;
+ String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+ syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
+ String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+ syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
+
+ // Installer
+ final String installerPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_INSTALLER, userId);
+ PackageParser.Package installerPackage = getSystemPackage(installerPackageName);
+ if (installerPackage != null
+ && doesPackageSupportRuntimePermissions(installerPackage)) {
+ grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Verifier
+ final String verifierPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_VERIFIER, userId);
+ PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName);
+ if (verifierPackage != null
+ && doesPackageSupportRuntimePermissions(verifierPackage)) {
+ grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId);
+ grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId);
+ }
+
+ // SetupWizard
+ final String setupWizardPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId);
+ PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName);
+ if (setupPackage != null
+ && doesPackageSupportRuntimePermissions(setupPackage)) {
+ grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId);
+ }
+
+ // Camera
+ Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage(
+ cameraIntent, userId);
+ if (cameraPackage != null
+ && doesPackageSupportRuntimePermissions(cameraPackage)) {
+ grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId);
+ grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Media provider
+ PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage(
+ MediaStore.AUTHORITY, userId);
+ if (mediaStorePackage != null) {
+ grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Downloads provider
+ PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage(
+ "downloads", userId);
+ if (downloadsPackage != null) {
+ grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Downloads UI
+ Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+ PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage(
+ downloadsUiIntent, userId);
+ if (downloadsUiPackage != null
+ && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
+ grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Storage provider
+ PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage(
+ "com.android.externalstorage.documents", userId);
+ if (storagePackage != null) {
+ grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // CertInstaller
+ Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
+ PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(
+ certInstallerIntent, userId);
+ if (certInstallerPackage != null
+ && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
+ grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Dialer
+ if (dialerAppPackageNames == null) {
+ Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
+ PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage(
+ dialerIntent, userId);
+ if (dialerPackage != null) {
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+ }
+ } else {
+ for (String dialerAppPackageName : dialerAppPackageNames) {
+ PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName);
+ if (dialerPackage != null) {
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+ }
+ }
+ }
+
+ // Sim call manager
+ if (simCallManagerPackageNames != null) {
+ for (String simCallManagerPackageName : simCallManagerPackageNames) {
+ PackageParser.Package simCallManagerPackage =
+ getSystemPackage(simCallManagerPackageName);
+ if (simCallManagerPackage != null) {
+ grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage,
+ userId);
+ }
+ }
+ }
+
+ // SMS
+ if (smsAppPackageNames == null) {
+ Intent smsIntent = new Intent(Intent.ACTION_MAIN);
+ smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
+ PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage(
+ smsIntent, userId);
+ if (smsPackage != null) {
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+ }
+ } else {
+ for (String smsPackageName : smsAppPackageNames) {
+ PackageParser.Package smsPackage = getSystemPackage(smsPackageName);
+ if (smsPackage != null) {
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+ }
+ }
+ }
+
+ // Cell Broadcast Receiver
+ Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+ PackageParser.Package cbrPackage =
+ getDefaultSystemHandlerActivityPackage(cbrIntent, userId);
+ if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
+ grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId);
+ }
+
+ // Carrier Provisioning Service
+ Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
+ PackageParser.Package carrierProvPackage =
+ getDefaultSystemHandlerServicePackage(carrierProvIntent, userId);
+ if (carrierProvPackage != null
+ && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
+ grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId);
+ }
+
+ // Calendar
+ Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
+ calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
+ PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage(
+ calendarIntent, userId);
+ if (calendarPackage != null
+ && doesPackageSupportRuntimePermissions(calendarPackage)) {
+ grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId);
+ }
+
+ // Calendar provider
+ PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage(
+ CalendarContract.AUTHORITY, userId);
+ if (calendarProviderPackage != null) {
+ grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Calendar provider sync adapters
+ List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages(
+ calendarSyncAdapterPackages, userId);
+ final int calendarSyncAdapterCount = calendarSyncAdapters.size();
+ for (int i = 0; i < calendarSyncAdapterCount; i++) {
+ PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
+ if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
+ grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
+ }
+ }
+
+ // Contacts
+ Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
+ contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+ PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage(
+ contactsIntent, userId);
+ if (contactsPackage != null
+ && doesPackageSupportRuntimePermissions(contactsPackage)) {
+ grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId);
+ }
+
+ // Contacts provider sync adapters
+ List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages(
+ contactsSyncAdapterPackages, userId);
+ final int contactsSyncAdapterCount = contactsSyncAdapters.size();
+ for (int i = 0; i < contactsSyncAdapterCount; i++) {
+ PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
+ if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
+ grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
+ }
+ }
+
+ // Contacts provider
+ PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage(
+ ContactsContract.AUTHORITY, userId);
+ if (contactsProviderPackage != null) {
+ grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Device provisioning
+ Intent deviceProvisionIntent = new Intent(
+ DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
+ PackageParser.Package deviceProvisionPackage =
+ getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId);
+ if (deviceProvisionPackage != null
+ && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
+ grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
+ }
+
+ // Maps
+ Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
+ mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
+ PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage(
+ mapsIntent, userId);
+ if (mapsPackage != null
+ && doesPackageSupportRuntimePermissions(mapsPackage)) {
+ grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId);
+ }
+
+ // Gallery
+ Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
+ galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
+ PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage(
+ galleryIntent, userId);
+ if (galleryPackage != null
+ && doesPackageSupportRuntimePermissions(galleryPackage)) {
+ grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Email
+ Intent emailIntent = new Intent(Intent.ACTION_MAIN);
+ emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
+ PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage(
+ emailIntent, userId);
+ if (emailPackage != null
+ && doesPackageSupportRuntimePermissions(emailPackage)) {
+ grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId);
+ }
+
+ // Browser
+ PackageParser.Package browserPackage = null;
+ String defaultBrowserPackage = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_BROWSER, userId);
+ if (defaultBrowserPackage != null) {
+ browserPackage = getPackage(defaultBrowserPackage);
+ }
+ if (browserPackage == null) {
+ Intent browserIntent = new Intent(Intent.ACTION_MAIN);
+ browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
+ browserPackage = getDefaultSystemHandlerActivityPackage(
+ browserIntent, userId);
+ }
+ if (browserPackage != null
+ && doesPackageSupportRuntimePermissions(browserPackage)) {
+ grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);
+ }
+
+ // Voice interaction
+ if (voiceInteractPackageNames != null) {
+ for (String voiceInteractPackageName : voiceInteractPackageNames) {
+ PackageParser.Package voiceInteractPackage = getSystemPackage(
+ voiceInteractPackageName);
+ if (voiceInteractPackage != null
+ && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
+ grantRuntimePermissions(voiceInteractPackage,
+ CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ LOCATION_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ // Allow voice search on low-ram devices
+ Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
+ PackageParser.Package globalSearchPickerPackage =
+ getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId);
+
+ if (globalSearchPickerPackage != null
+ && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
+ grantRuntimePermissions(globalSearchPickerPackage,
+ MICROPHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(globalSearchPickerPackage,
+ LOCATION_PERMISSIONS, true, userId);
+ }
+ }
+
+ // Voice recognition
+ Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
+ voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage(
+ voiceRecoIntent, userId);
+ if (voiceRecoPackage != null
+ && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
+ grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
+ }
+
+ // Location
+ if (locationPackageNames != null) {
+ for (String packageName : locationPackageNames) {
+ PackageParser.Package locationPackage = getSystemPackage(packageName);
+ if (locationPackage != null
+ && doesPackageSupportRuntimePermissions(locationPackage)) {
+ grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ // Music
+ Intent musicIntent = new Intent(Intent.ACTION_VIEW);
+ musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
+ AUDIO_MIME_TYPE);
+ PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage(
+ musicIntent, userId);
+ if (musicPackage != null
+ && doesPackageSupportRuntimePermissions(musicPackage)) {
+ grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Home
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
+ PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage(
+ homeIntent, userId);
+ if (homePackage != null
+ && doesPackageSupportRuntimePermissions(homePackage)) {
+ grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId);
+ }
+
+ // Watches
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
+ // Home application on watches
+ Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
+ wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+ PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage(
+ wearHomeIntent, userId);
+
+ if (wearHomePackage != null
+ && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+ grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false,
+ userId);
+ }
+
+ // Fitness tracking on watches
+ Intent trackIntent = new Intent(ACTION_TRACK);
+ PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage(
+ trackIntent, userId);
+ if (trackPackage != null
+ && doesPackageSupportRuntimePermissions(trackPackage)) {
+ grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId);
+ grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId);
+ }
+ }
+
+ // Print Spooler
+ PackageParser.Package printSpoolerPackage = getSystemPackage(
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
+ if (printSpoolerPackage != null
+ && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
+ grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
+ }
+
+ // EmergencyInfo
+ Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
+ PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage(
+ emergencyInfoIntent, userId);
+ if (emergencyInfoPckg != null
+ && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
+ grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
+ grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
+ }
+
+ // NFC Tag viewer
+ Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
+ nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
+ PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage(
+ nfcTagIntent, userId);
+ if (nfcTagPkg != null
+ && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
+ grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
+ grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
+ }
+
+ // Storage Manager
+ Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+ PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage(
+ storageManagerIntent, userId);
+ if (storageManagerPckg != null
+ && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
+ grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Companion devices
+ PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage(
+ CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
+ if (companionDeviceDiscoveryPackage != null
+ && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
+ grantRuntimePermissions(companionDeviceDiscoveryPackage,
+ LOCATION_PERMISSIONS, true, userId);
+ }
+
+ // Ringtone Picker
+ Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ PackageParser.Package ringtonePickerPackage =
+ getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId);
+ if (ringtonePickerPackage != null
+ && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
+ grantRuntimePermissions(ringtonePickerPackage,
+ STORAGE_PERMISSIONS, true, userId);
+ }
+
+ if (mPermissionGrantedCallback != null) {
+ mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemDialerApp(
+ PackageParser.Package dialerPackage, int userId) {
+ if (doesPackageSupportRuntimePermissions(dialerPackage)) {
+ boolean isPhonePermFixed =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
+ grantRuntimePermissions(
+ dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
+ grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemSmsApp(
+ PackageParser.Package smsPackage, int userId) {
+ if (doesPackageSupportRuntimePermissions(smsPackage)) {
+ grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package smsPackage = getPackage(packageName);
+ if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
+ grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package dialerPackage = getPackage(packageName);
+ if (dialerPackage != null
+ && doesPackageSupportRuntimePermissions(dialerPackage)) {
+ grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSimCallManager(
+ PackageParser.Package simCallManagerPackage, int userId) {
+ Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
+ if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
+ grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package simCallManagerPackage = getPackage(packageName);
+ if (simCallManagerPackage != null) {
+ grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
+ Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
+ if (packageNames == null) {
+ return;
+ }
+ for (String packageName : packageNames) {
+ PackageParser.Package carrierPackage = getSystemPackage(packageName);
+ if (carrierPackage != null
+ && doesPackageSupportRuntimePermissions(carrierPackage)) {
+ grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
+ Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
+ if (packageNames == null) {
+ return;
+ }
+ for (String packageName : packageNames) {
+ PackageParser.Package imsServicePackage = getSystemPackage(packageName);
+ if (imsServicePackage != null
+ && doesPackageSupportRuntimePermissions(imsServicePackage)) {
+ grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default browser for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package browserPackage = getSystemPackage(packageName);
+ if (browserPackage != null
+ && doesPackageSupportRuntimePermissions(browserPackage)) {
+ grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
+ }
+ }
+
+ private PackageParser.Package getDefaultSystemHandlerActivityPackage(
+ Intent intent, int userId) {
+ ResolveInfo handler = mServiceInternal.resolveIntent(intent,
+ intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false);
+ if (handler == null || handler.activityInfo == null) {
+ return null;
+ }
+ if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) {
+ return null;
+ }
+ return getSystemPackage(handler.activityInfo.packageName);
+ }
+
+ private PackageParser.Package getDefaultSystemHandlerServicePackage(
+ Intent intent, int userId) {
+ List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
+ intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId);
+ if (handlers == null) {
+ return null;
+ }
+ final int handlerCount = handlers.size();
+ for (int i = 0; i < handlerCount; i++) {
+ ResolveInfo handler = handlers.get(i);
+ PackageParser.Package handlerPackage = getSystemPackage(
+ handler.serviceInfo.packageName);
+ if (handlerPackage != null) {
+ return handlerPackage;
+ }
+ }
+ return null;
+ }
+
+ private List<PackageParser.Package> getHeadlessSyncAdapterPackages(
+ String[] syncAdapterPackageNames, int userId) {
+ List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
+
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ for (String syncAdapterPackageName : syncAdapterPackageNames) {
+ homeIntent.setPackage(syncAdapterPackageName);
+
+ ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
+ homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
+ userId, false);
+ if (homeActivity != null) {
+ continue;
+ }
+
+ PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName);
+ if (syncAdapterPackage != null) {
+ syncAdapterPackages.add(syncAdapterPackage);
+ }
+ }
+
+ return syncAdapterPackages;
+ }
+
+ private PackageParser.Package getDefaultProviderAuthorityPackage(
+ String authority, int userId) {
+ ProviderInfo provider =
+ mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
+ if (provider != null) {
+ return getSystemPackage(provider.packageName);
+ }
+ return null;
+ }
+
+ private PackageParser.Package getPackage(String packageName) {
+ return mServiceInternal.getPackage(packageName);
+ }
+
+ private PackageParser.Package getSystemPackage(String packageName) {
+ PackageParser.Package pkg = getPackage(packageName);
+ if (pkg != null && pkg.isSystemApp()) {
+ return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
+ }
+ return null;
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ int userId) {
+ grantRuntimePermissions(pkg, permissions, false, false, userId);
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ boolean systemFixed, int userId) {
+ grantRuntimePermissions(pkg, permissions, systemFixed, false, userId);
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
+ if (pkg.requestedPermissions.isEmpty()) {
+ return;
+ }
+
+ List<String> requestedPermissions = pkg.requestedPermissions;
+ Set<String> grantablePermissions = null;
+
+ // If this is the default Phone or SMS app we grant permissions regardless
+ // whether the version on the system image declares the permission as used since
+ // selecting the app as the default Phone or SMS the user makes a deliberate
+ // choice to grant this app the permissions needed to function. For all other
+ // apps, (default grants on first boot and user creation) we don't grant default
+ // permissions if the version on the system image does not declare them.
+ if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
+ final PackageParser.Package disabledPkg =
+ mServiceInternal.getDisabledPackage(pkg.packageName);
+ if (disabledPkg != null) {
+ if (disabledPkg.requestedPermissions.isEmpty()) {
+ return;
+ }
+ if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) {
+ grantablePermissions = new ArraySet<>(requestedPermissions);
+ requestedPermissions = disabledPkg.requestedPermissions;
+ }
+ }
+ }
+
+ final int grantablePermissionCount = requestedPermissions.size();
+ for (int i = 0; i < grantablePermissionCount; i++) {
+ String permission = requestedPermissions.get(i);
+
+ // If there is a disabled system app it may request a permission the updated
+ // version ot the data partition doesn't, In this case skip the permission.
+ if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
+ continue;
+ }
+
+ if (permissions.contains(permission)) {
+ final int flags = mServiceInternal.getPermissionFlagsTEMP(
+ permission, pkg.packageName, userId);
+
+ // If any flags are set to the permission, then it is either set in
+ // its current state by the system or device/profile owner or the user.
+ // In all these cases we do not want to clobber the current state.
+ // Unless the caller wants to override user choices. The override is
+ // to make sure we can grant the needed permission to the default
+ // sms and phone apps after the user chooses this in the UI.
+ if (flags == 0 || isDefaultPhoneOrSms) {
+ // Never clobber policy or system.
+ final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ if ((flags & fixedFlags) != 0) {
+ continue;
+ }
+
+ mServiceInternal.grantRuntimePermission(
+ pkg.packageName, permission, userId, false);
+ if (DEBUG) {
+ Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
+ + permission + " to default handler " + pkg.packageName);
+ }
+
+ int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ if (systemFixed) {
+ newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ }
+
+ mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+ newFlags, newFlags, userId);
+ }
+
+ // If a component gets a permission for being the default handler A
+ // and also default handler B, we grant the weaker grant form.
+ if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
+ && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ && !systemFixed) {
+ if (DEBUG) {
+ Log.i(TAG, "Granted not fixed " + permission + " to default handler "
+ + pkg.packageName);
+ }
+ mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+ }
+ }
+ }
+ }
+
+ private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
+ if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+ return true;
+ }
+ if (!pkg.isPrivilegedApp()) {
+ return false;
+ }
+ final PackageParser.Package disabledPkg =
+ mServiceInternal.getDisabledPackage(pkg.packageName);
+ if (disabledPkg != null) {
+ if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ return false;
+ }
+ } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ return false;
+ }
+ final String systemPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+ final PackageParser.Package systemPackage = getPackage(systemPackageName);
+ return PackageManagerService.compareSignatures(systemPackage.mSignatures,
+ pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+ }
+
+ private void grantDefaultPermissionExceptions(int userId) {
+ mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+
+ synchronized (mLock) {
+ // mGrantExceptions is null only before the first read and then
+ // it serves as a cache of the default grants that should be
+ // performed for every user. If there is an entry then the app
+ // is on the system image and supports runtime permissions.
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLocked();
+ }
+ }
+
+ Set<String> permissions = null;
+ final int exceptionCount = mGrantExceptions.size();
+ for (int i = 0; i < exceptionCount; i++) {
+ String packageName = mGrantExceptions.keyAt(i);
+ PackageParser.Package pkg = getSystemPackage(packageName);
+ List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
+ final int permissionGrantCount = permissionGrants.size();
+ for (int j = 0; j < permissionGrantCount; j++) {
+ DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+ if (permissions == null) {
+ permissions = new ArraySet<>();
+ } else {
+ permissions.clear();
+ }
+ permissions.add(permissionGrant.name);
+ grantRuntimePermissions(pkg, permissions,
+ permissionGrant.fixed, userId);
+ }
+ }
+ }
+
+ private File[] getDefaultPermissionFiles() {
+ ArrayList<File> ret = new ArrayList<File>();
+ File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ return ret.isEmpty() ? null : ret.toArray(new File[0]);
+ }
+
+ private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
+ readDefaultPermissionExceptionsLocked() {
+ File[] files = getDefaultPermissionFiles();
+ if (files == null) {
+ return new ArrayMap<>(0);
+ }
+
+ ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File file : files) {
+ if (!file.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + file
+ + " in " + file.getParent() + " directory, ignoring");
+ continue;
+ }
+ if (!file.canRead()) {
+ Slog.w(TAG, "Default permissions file " + file + " cannot be read");
+ continue;
+ }
+ try (
+ InputStream str = new BufferedInputStream(new FileInputStream(file))
+ ) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+ parse(parser, grantExceptions);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Error reading default permissions file " + file, e);
+ }
+ }
+
+ return grantExceptions;
+ }
+
+ private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTIONS.equals(parser.getName())) {
+ parseExceptions(parser, outGrantExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ }
+
+ private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTION.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+
+ List<DefaultPermissionGrant> packageExceptions =
+ outGrantExceptions.get(packageName);
+ if (packageExceptions == null) {
+ // The package must be on the system image
+ PackageParser.Package pkg = getSystemPackage(packageName);
+ if (pkg == null) {
+ Log.w(TAG, "Unknown package:" + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ // The package must support runtime permissions
+ if (!doesPackageSupportRuntimePermissions(pkg)) {
+ Log.w(TAG, "Skipping non supporting runtime permissions package:"
+ + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ packageExceptions = new ArrayList<>();
+ outGrantExceptions.put(packageName, packageExceptions);
+ }
+
+ parsePermission(parser, packageExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
+ }
+ }
+ }
+
+ private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+ outPackageExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (TAG_PERMISSION.contains(parser.getName())) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ if (name == null) {
+ Log.w(TAG, "Mandatory name attribute missing for permission tag");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
+
+ DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
+ outPackageExceptions.add(exception);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
+ }
+ }
+ }
+
+ private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
+ return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+ }
+
+ private static final class DefaultPermissionGrant {
+ final String name;
+ final boolean fixed;
+
+ public DefaultPermissionGrant(String name, boolean fixed) {
+ this.name = name;
+ this.fixed = fixed;
+ }
+ }
+}
diff --git a/com/android/server/pm/permission/PermissionManagerInternal.java b/com/android/server/pm/permission/PermissionManagerInternal.java
new file mode 100644
index 00000000..3b20b42b
--- /dev/null
+++ b/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -0,0 +1,140 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.content.pm.PackageParser.Permission;
+
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ */
+public abstract class PermissionManagerInternal {
+ /**
+ * Callbacks invoked when interesting actions have been taken on a permission.
+ * <p>
+ * NOTE: The current arguments are merely to support the existing use cases. This
+ * needs to be properly thought out with appropriate arguments for each of the
+ * callback methods.
+ */
+ public static class PermissionCallback {
+ public void onGidsChanged(int appId, int userId) {
+ }
+ public void onPermissionChanged() {
+ }
+ public void onPermissionGranted(int uid, int userId) {
+ }
+ public void onInstallPermissionGranted() {
+ }
+ public void onPermissionRevoked(int uid, int userId) {
+ }
+ public void onInstallPermissionRevoked() {
+ }
+ public void onPermissionUpdated(int userId) {
+ }
+ public void onPermissionRemoved() {
+ }
+ public void onInstallPermissionUpdated() {
+ }
+ }
+
+ public abstract void grantRuntimePermission(
+ @NonNull String permName, @NonNull String packageName, boolean overridePolicy,
+ int callingUid, int userId, @Nullable PermissionCallback callback);
+ public abstract void grantRuntimePermissionsGrantedToDisabledPackage(
+ @NonNull PackageParser.Package pkg, int callingUid,
+ @Nullable PermissionCallback callback);
+ public abstract void grantRequestedRuntimePermissions(
+ @NonNull PackageParser.Package pkg, @NonNull int[] userIds,
+ @NonNull String[] grantedPermissions, int callingUid,
+ @Nullable PermissionCallback callback);
+ public abstract void revokeRuntimePermission(@NonNull String permName,
+ @NonNull String packageName, boolean overridePolicy, int callingUid, int userId,
+ @Nullable PermissionCallback callback);
+ public abstract int[] revokeUnusedSharedUserPermissions(@NonNull SharedUserSetting suSetting,
+ @NonNull int[] allUserIds);
+
+
+ public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+ int callingUid, @Nullable PermissionCallback callback);
+ public abstract void removePermission(@NonNull String permName, int callingUid,
+ @Nullable PermissionCallback callback);
+
+ public abstract int getPermissionFlags(@NonNull String permName,
+ @NonNull String packageName, int callingUid, int userId);
+ /**
+ * Retrieve all of the information we know about a particular permission.
+ */
+ public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName,
+ @NonNull String packageName, @PermissionInfoFlags int flags, int callingUid);
+ /**
+ * Retrieve all of the permissions associated with a particular group.
+ */
+ 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
+ * the specified mask with the provided flag values.
+ */
+ public abstract void updatePermissionFlags(@NonNull String permName,
+ @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId,
+ @Nullable PermissionCallback callback);
+ /**
+ * Updates the flags for all applications by replacing the flags in the specified mask
+ * with the provided flag values.
+ */
+ public abstract boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues,
+ int callingUid, int userId, @NonNull Collection<PackageParser.Package> packages,
+ @Nullable PermissionCallback callback);
+
+ public abstract int checkPermission(@NonNull String permName, @NonNull String packageName,
+ int callingUid, int userId);
+
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller.
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ public abstract void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, @NonNull String message);
+ public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message);
+
+ public abstract @NonNull PermissionSettings getPermissionSettings();
+ public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy();
+
+ /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
+ public abstract Iterator<BasePermission> getPermissionIteratorTEMP();
+ public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
+ public abstract void putPermissionTEMP(@NonNull String permName,
+ @NonNull BasePermission permission);
+} \ No newline at end of file
diff --git a/com/android/server/pm/permission/PermissionManagerService.java b/com/android/server/pm/permission/PermissionManagerService.java
new file mode 100644
index 00000000..6c031a6a
--- /dev/null
+++ b/com/android/server/pm/permission/PermissionManagerService.java
@@ -0,0 +1,1081 @@
+/*
+ * 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.pm.permission;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageParser.Package;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.storage.StorageManagerInternal;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
+import com.android.server.Watchdog;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.ProcessLoggingHandler;
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
+
+import libcore.util.EmptyArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Manages all permissions and handles permissions related tasks.
+ */
+public class PermissionManagerService {
+ private static final String TAG = "PackageManager";
+
+ /** All dangerous permission names in the same order as the events in MetricsEvent */
+ private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
+ Manifest.permission.READ_CALENDAR,
+ Manifest.permission.WRITE_CALENDAR,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.WRITE_CONTACTS,
+ Manifest.permission.GET_ACCOUNTS,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.CALL_PHONE,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.ADD_VOICEMAIL,
+ Manifest.permission.USE_SIP,
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
+ Manifest.permission.READ_CELL_BROADCASTS,
+ Manifest.permission.BODY_SENSORS,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.RECEIVE_SMS,
+ Manifest.permission.READ_SMS,
+ Manifest.permission.RECEIVE_WAP_PUSH,
+ Manifest.permission.RECEIVE_MMS,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.ANSWER_PHONE_CALLS);
+
+ /** Cap the size of permission trees that 3rd party apps can define */
+ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
+
+ /** Lock to protect internal data access */
+ private final Object mLock;
+
+ /** Internal connection to the package manager */
+ private final PackageManagerInternal mPackageManagerInt;
+
+ /** Internal connection to the user manager */
+ private final UserManagerInternal mUserManagerInt;
+
+ /** Default permission policy to provide proper behaviour out-of-the-box */
+ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+
+ /** Internal storage for permissions and related settings */
+ private final PermissionSettings mSettings;
+
+ private final HandlerThread mHandlerThread;
+ private final Handler mHandler;
+ private final Context mContext;
+
+ PermissionManagerService(Context context,
+ @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+ @NonNull Object externalLock) {
+ mContext = context;
+ mLock = externalLock;
+ mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
+ mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
+ mSettings = new PermissionSettings(context, mLock);
+
+ mHandlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ Watchdog.getInstance().addThread(mHandler);
+
+ mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
+ context, mHandlerThread.getLooper(), defaultGrantCallback, this);
+
+ // propagate permission configuration
+ final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
+ SystemConfig.getInstance().getPermissions();
+ synchronized (mLock) {
+ for (int i=0; i<permConfig.size(); i++) {
+ final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+ BasePermission bp = mSettings.getPermissionLocked(perm.name);
+ if (bp == null) {
+ bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
+ mSettings.putPermissionLocked(perm.name, bp);
+ }
+ if (perm.gids != null) {
+ bp.setGids(perm.gids, perm.perUser);
+ }
+ }
+ }
+
+ LocalServices.addService(
+ PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+ }
+
+ /**
+ * Creates and returns an initialized, internal service for use by other components.
+ * <p>
+ * The object returned is identical to the one returned by the LocalServices class using:
+ * {@code LocalServices.getService(PermissionManagerInternal.class);}
+ * <p>
+ * NOTE: The external lock is temporary and should be removed. This needs to be a
+ * lock created by the permission manager itself.
+ */
+ public static PermissionManagerInternal create(Context context,
+ @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+ @NonNull Object externalLock) {
+ final PermissionManagerInternal permMgrInt =
+ LocalServices.getService(PermissionManagerInternal.class);
+ if (permMgrInt != null) {
+ return permMgrInt;
+ }
+ new PermissionManagerService(context, defaultGrantCallback, externalLock);
+ return LocalServices.getService(PermissionManagerInternal.class);
+ }
+
+ @Nullable BasePermission getPermission(String permName) {
+ synchronized (mLock) {
+ return mSettings.getPermissionLocked(permName);
+ }
+ }
+
+ private int checkPermission(String permName, String pkgName, int callingUid, int userId) {
+ if (!mUserManagerInt.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+ if (pkg != null && pkg.mExtras != null) {
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final boolean instantApp = ps.getInstantApp(userId);
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.hasPermission(permName, userId)) {
+ if (instantApp) {
+ synchronized (mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp != null && bp.isInstant()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ } else {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
+ if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
+ .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ private PermissionInfo getPermissionInfo(String name, String packageName, int flags,
+ int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ // reader
+ synchronized (mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(name);
+ if (bp == null) {
+ return null;
+ }
+ final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
+ bp.getProtectionLevel(), packageName, callingUid);
+ return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+ }
+ }
+
+ private List<PermissionInfo> getPermissionInfoByGroup(
+ String groupName, int flags, int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ // reader
+ synchronized (mLock) {
+ // TODO Uncomment when mPermissionGroups moves to this class
+// if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
+// // This is thrown as NameNotFoundException
+// return null;
+// }
+
+ final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
+ for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+ final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
+ if (pi != null) {
+ out.add(pi);
+ }
+ }
+ return out;
+ }
+ }
+
+ private int adjustPermissionProtectionFlagsLocked(
+ int protectionLevel, String packageName, int uid) {
+ // Signature permission flags area always reported
+ final int protectionLevelMasked = protectionLevel
+ & (PermissionInfo.PROTECTION_NORMAL
+ | PermissionInfo.PROTECTION_DANGEROUS
+ | PermissionInfo.PROTECTION_SIGNATURE);
+ if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
+ return protectionLevel;
+ }
+ // System sees all flags.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
+ || appId == Process.SHELL_UID) {
+ return protectionLevel;
+ }
+ // Normalize package name to handle renamed packages and static libs
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null) {
+ return protectionLevel;
+ }
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ return protectionLevelMasked;
+ }
+ // Apps that target O see flags for all protection levels.
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return protectionLevel;
+ }
+ if (ps.getAppId() != appId) {
+ return protectionLevel;
+ }
+ return protectionLevel;
+ }
+
+ private boolean addPermission(
+ PermissionInfo info, int callingUid, PermissionCallback callback) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ throw new SecurityException("Instant apps can't add permissions");
+ }
+ if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
+ throw new SecurityException("Label must be specified in permission");
+ }
+ final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ info.name, callingUid);
+ final boolean added;
+ final boolean changed;
+ synchronized (mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(info.name);
+ added = bp == null;
+ int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+ if (added) {
+ enforcePermissionCapLocked(info, tree);
+ bp = new BasePermission(info.name, tree.getSourcePackageName(),
+ BasePermission.TYPE_DYNAMIC);
+ } else if (bp.isDynamic()) {
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ + info.name);
+ }
+ changed = bp.addToTree(fixedLevel, info, tree);
+ if (added) {
+ mSettings.putPermissionLocked(info.name, bp);
+ }
+ }
+ if (changed && callback != null) {
+ callback.onPermissionChanged();
+ }
+ return added;
+ }
+
+ 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 = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ permName, callingUid);
+ synchronized (mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp == null) {
+ return;
+ }
+ if (bp.isDynamic()) {
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ + permName);
+ }
+ mSettings.removePermissionLocked(permName);
+ if (callback != null) {
+ callback.onPermissionRemoved();
+ }
+ }
+ }
+
+ private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
+ PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
+ if (pkg.parentPackage == null) {
+ return;
+ }
+ if (pkg.requestedPermissions == null) {
+ return;
+ }
+ final PackageParser.Package disabledPkg =
+ mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName);
+ if (disabledPkg == null || disabledPkg.mExtras == null) {
+ return;
+ }
+ final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras;
+ if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) {
+ return;
+ }
+ final int permCount = pkg.requestedPermissions.size();
+ for (int i = 0; i < permCount; i++) {
+ String permission = pkg.requestedPermissions.get(i);
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
+ continue;
+ }
+ for (int userId : mUserManagerInt.getUserIds()) {
+ if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) {
+ grantRuntimePermission(
+ permission, pkg.packageName, false, callingUid, userId, callback);
+ }
+ }
+ }
+ }
+
+ private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ for (int userId : userIds) {
+ grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid,
+ callback);
+ }
+ }
+
+ private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return;
+ }
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+
+ final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M;
+
+ final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId);
+
+ for (String permission : pkg.requestedPermissions) {
+ final BasePermission bp;
+ synchronized (mLock) {
+ bp = mSettings.getPermissionLocked(permission);
+ }
+ if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+ && (!instantApp || bp.isInstant())
+ && (supportsRuntimePermissions || !bp.isRuntimeOnly())
+ && (grantedPermissions == null
+ || ArrayUtils.contains(grantedPermissions, permission))) {
+ final int flags = permissionsState.getPermissionFlags(permission, userId);
+ if (supportsRuntimePermissions) {
+ // Installer cannot change immutable permissions.
+ if ((flags & immutableFlags) == 0) {
+ grantRuntimePermission(permission, pkg.packageName, false, callingUid,
+ userId, callback);
+ }
+ } else if (mSettings.mPermissionReviewRequired) {
+ // In permission review mode we clear the review flag when we
+ // are asked to install the app with all permissions granted.
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ updatePermissionFlags(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
+ userId, callback);
+ }
+ }
+ }
+ }
+ }
+
+ private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
+ int callingUid, final int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ Log.e(TAG, "No such user:" + userId);
+ return;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "grantRuntimePermission");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "grantRuntimePermission");
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ final BasePermission bp;
+ synchronized(mLock) {
+ bp = mSettings.getPermissionLocked(permName);
+ }
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mSettings.mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
+ }
+
+ final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int flags = permissionsState.getPermissionFlags(permName, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot grant system fixed permission "
+ + permName + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot grant policy fixed permission "
+ + permName + " for package " + packageName);
+ }
+
+ if (bp.isDevelopment()) {
+ // Development permissions must be handled specially, since they are not
+ // normal runtime permissions. For now they apply to all users.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ if (callback != null) {
+ callback.onInstallPermissionGranted();
+ }
+ }
+ return;
+ }
+
+ if (ps.getInstantApp(userId) && !bp.isInstant()) {
+ throw new SecurityException("Cannot grant non-ephemeral permission"
+ + permName + " for package " + packageName);
+ }
+
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+ return;
+ }
+
+ final int result = permissionsState.grantRuntimePermission(bp, userId);
+ switch (result) {
+ case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+ return;
+ }
+
+ case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ if (callback != null) {
+ callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
+ }
+ }
+ break;
+ }
+
+ if (bp.isRuntime()) {
+ logPermissionGranted(mContext, permName, packageName);
+ }
+
+ if (callback != null) {
+ callback.onPermissionGranted(uid, userId);
+ }
+
+ // Only need to do this if user is initialized. Otherwise it's a new user
+ // and there are no processes running as the user yet and there's no need
+ // to make an expensive call to remount processes for the changed permissions.
+ if (READ_EXTERNAL_STORAGE.equals(permName)
+ || WRITE_EXTERNAL_STORAGE.equals(permName)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mUserManagerInt.isUserInitialized(userId)) {
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ }
+
+ private void revokeRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ Log.e(TAG, "No such user:" + userId);
+ return;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ "revokeRuntimePermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "revokeRuntimePermission");
+
+ final int appId;
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mSettings.mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
+ }
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int flags = permissionsState.getPermissionFlags(permName, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke system fixed permission "
+ + permName + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke policy fixed permission "
+ + permName + " for package " + packageName);
+ }
+
+ if (bp.isDevelopment()) {
+ // Development permissions must be handled specially, since they are not
+ // normal runtime permissions. For now they apply to all users.
+ if (permissionsState.revokeInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ if (callback != null) {
+ callback.onInstallPermissionRevoked();
+ }
+ }
+ return;
+ }
+
+ if (permissionsState.revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ return;
+ }
+
+ if (bp.isRuntime()) {
+ logPermissionRevoked(mContext, permName, packageName);
+ }
+
+ if (callback != null) {
+ final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+ callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
+ }
+ }
+
+ private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) {
+ // Collect all used permissions in the UID
+ final ArraySet<String> usedPermissions = new ArraySet<>();
+ final List<PackageParser.Package> pkgList = suSetting.getPackages();
+ if (pkgList == null || pkgList.size() == 0) {
+ return EmptyArray.INT;
+ }
+ for (PackageParser.Package pkg : pkgList) {
+ final int requestedPermCount = pkg.requestedPermissions.size();
+ for (int j = 0; j < requestedPermCount; j++) {
+ String permission = pkg.requestedPermissions.get(j);
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp != null) {
+ usedPermissions.add(permission);
+ }
+ }
+ }
+
+ PermissionsState permissionsState = suSetting.getPermissionsState();
+ // Prune install permissions
+ List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
+ final int installPermCount = installPermStates.size();
+ for (int i = installPermCount - 1; i >= 0; i--) {
+ PermissionState permissionState = installPermStates.get(i);
+ if (!usedPermissions.contains(permissionState.getName())) {
+ BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+ if (bp != null) {
+ permissionsState.revokeInstallPermission(bp);
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ }
+ }
+ }
+
+ int[] runtimePermissionChangedUserIds = EmptyArray.INT;
+
+ // Prune runtime permissions
+ for (int userId : allUserIds) {
+ List<PermissionState> runtimePermStates = permissionsState
+ .getRuntimePermissionStates(userId);
+ final int runtimePermCount = runtimePermStates.size();
+ for (int i = runtimePermCount - 1; i >= 0; i--) {
+ PermissionState permissionState = runtimePermStates.get(i);
+ if (!usedPermissions.contains(permissionState.getName())) {
+ BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+ if (bp != null) {
+ permissionsState.revokeRuntimePermission(bp, userId);
+ permissionsState.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ runtimePermissionChangedUserIds = ArrayUtils.appendInt(
+ runtimePermissionChangedUserIds, userId);
+ }
+ }
+ }
+ }
+
+ return runtimePermissionChangedUserIds;
+ }
+
+ private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+ if (!mUserManagerInt.exists(userId)) {
+ return 0;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getPermissionFlags");
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ return 0;
+ }
+ synchronized (mLock) {
+ if (mSettings.getPermissionLocked(permName) == null) {
+ return 0;
+ }
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ return 0;
+ }
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ PermissionsState permissionsState = ps.getPermissionsState();
+ return permissionsState.getPermissionFlags(permName, userId);
+ }
+
+ private void updatePermissionFlags(String permName, String packageName, int flagMask,
+ int flagValues, int callingUid, int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ return;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "updatePermissionFlags");
+
+ // Only the system can change these flags and nothing else.
+ if (callingUid != Process.SYSTEM_UID) {
+ flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+
+ final BasePermission bp;
+ synchronized (mLock) {
+ bp = mSettings.getPermissionLocked(permName);
+ }
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ final boolean hadState =
+ permissionsState.getRuntimePermissionState(permName, userId) != null;
+ final boolean permissionUpdated =
+ permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+ if (permissionUpdated && callback != null) {
+ // Install and runtime permissions are stored in different places,
+ // so figure out what permission changed and persist the change.
+ if (permissionsState.getInstallPermissionState(permName) != null) {
+ callback.onInstallPermissionUpdated();
+ } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
+ || hadState) {
+ callback.onPermissionUpdated(userId);
+ }
+ }
+ }
+
+ private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+ int userId, Collection<Package> packages, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ return false;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions(
+ "updatePermissionFlagsForAllApps");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "updatePermissionFlagsForAllApps");
+
+ // Only the system can change system fixed flags.
+ if (callingUid != Process.SYSTEM_UID) {
+ flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ }
+
+ boolean changed = false;
+ for (PackageParser.Package pkg : packages) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ continue;
+ }
+ PermissionsState permissionsState = ps.getPermissionsState();
+ changed |= permissionsState.updatePermissionFlagsForAllPermissions(
+ userId, flagMask, flagValues);
+ }
+ return changed;
+ }
+
+ private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(message + " requires "
+ + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
+ + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+ }
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ if (userId == UserHandle.getUserId(callingUid)) return;
+ if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (requireFullPermission) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } else {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } catch (SecurityException se) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, message);
+ }
+ }
+ }
+ }
+
+ private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+ int size = 0;
+ for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+ size += tree.calculateFootprint(perm);
+ }
+ return size;
+ }
+
+ private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+ // We calculate the max size of permissions defined by this uid and throw
+ // if that plus the size of 'info' would exceed our stated maximum.
+ if (tree.getUid() != Process.SYSTEM_UID) {
+ final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+ if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) {
+ throw new SecurityException("Permission tree size cap exceeded");
+ }
+ }
+ }
+
+ /**
+ * Get the first event id for the permission.
+ *
+ * <p>There are four events for each permission: <ul>
+ * <li>Request permission: first id + 0</li>
+ * <li>Grant permission: first id + 1</li>
+ * <li>Request for permission denied: first id + 2</li>
+ * <li>Revoke permission: first id + 3</li>
+ * </ul></p>
+ *
+ * @param name name of the permission
+ *
+ * @return The first event id for the permission
+ */
+ private static int getBaseEventId(@NonNull String name) {
+ int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name);
+
+ if (eventIdIndex == -1) {
+ if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE
+ || Build.IS_USER) {
+ Log.i(TAG, "Unknown permission " + name);
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN;
+ } else {
+ // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated.
+ //
+ // Also update
+ // - EventLogger#ALL_DANGEROUS_PERMISSIONS
+ // - metrics_constants.proto
+ throw new IllegalStateException("Unknown permission " + name);
+ }
+ }
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4;
+ }
+
+ /**
+ * Log that a permission was revoked.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionRevoked(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 3, packageName);
+ }
+
+ /**
+ * Log that a permission request was granted.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionGranted(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 1, packageName);
+ }
+
+ private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+ @Override
+ public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
+ PermissionCallback callback) {
+ return PermissionManagerService.this.addPermission(info, callingUid, callback);
+ }
+ @Override
+ public void removePermission(String permName, int callingUid,
+ PermissionCallback callback) {
+ PermissionManagerService.this.removePermission(permName, callingUid, callback);
+ }
+ @Override
+ public void grantRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId,
+ PermissionCallback callback) {
+ PermissionManagerService.this.grantRuntimePermission(
+ permName, packageName, overridePolicy, callingUid, userId, callback);
+ }
+ @Override
+ public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ PermissionManagerService.this.grantRequestedRuntimePermissions(
+ pkg, userIds, grantedPermissions, callingUid, callback);
+ }
+ @Override
+ public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg,
+ int callingUid, PermissionCallback callback) {
+ PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked(
+ pkg, callingUid, callback);
+ }
+ @Override
+ public void revokeRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId,
+ PermissionCallback callback) {
+ PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
+ overridePolicy, callingUid, userId, callback);
+ }
+ @Override
+ public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting,
+ int[] allUserIds) {
+ return PermissionManagerService.this.revokeUnusedSharedUserPermissions(
+ (SharedUserSetting) suSetting, allUserIds);
+ }
+ @Override
+ public int getPermissionFlags(String permName, String packageName, int callingUid,
+ int userId) {
+ return PermissionManagerService.this.getPermissionFlags(permName, packageName,
+ callingUid, userId);
+ }
+ @Override
+ public void updatePermissionFlags(String permName, String packageName, int flagMask,
+ int flagValues, int callingUid, int userId, PermissionCallback callback) {
+ PermissionManagerService.this.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, callingUid, userId, callback);
+ }
+ @Override
+ public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+ int userId, Collection<Package> packages, PermissionCallback callback) {
+ return PermissionManagerService.this.updatePermissionFlagsForAllApps(
+ flagMask, flagValues, callingUid, userId, packages, callback);
+ }
+ @Override
+ public void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
+ requireFullPermission, checkShell, message);
+ }
+ @Override
+ public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+ PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
+ }
+ @Override
+ public int checkPermission(String permName, String packageName, int callingUid,
+ int userId) {
+ return PermissionManagerService.this.checkPermission(
+ permName, packageName, callingUid, userId);
+ }
+ @Override
+ public PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
+ int callingUid) {
+ return PermissionManagerService.this.getPermissionInfo(
+ permName, packageName, flags, callingUid);
+ }
+ @Override
+ public List<PermissionInfo> getPermissionInfoByGroup(String group, int flags,
+ int callingUid) {
+ 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;
+ }
+ @Override
+ public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
+ return mDefaultPermissionGrantPolicy;
+ }
+ @Override
+ public BasePermission getPermissionTEMP(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ return mSettings.getPermissionLocked(permName);
+ }
+ }
+ @Override
+ public void putPermissionTEMP(String permName, BasePermission permission) {
+ synchronized (PermissionManagerService.this.mLock) {
+ mSettings.putPermissionLocked(permName, (BasePermission) permission);
+ }
+ }
+ @Override
+ public Iterator<BasePermission> getPermissionIteratorTEMP() {
+ synchronized (PermissionManagerService.this.mLock) {
+ return mSettings.getAllPermissionsLocked().iterator();
+ }
+ }
+ }
+}
diff --git a/com/android/server/pm/permission/PermissionSettings.java b/com/android/server/pm/permission/PermissionSettings.java
new file mode 100644
index 00000000..7a2e5ecc
--- /dev/null
+++ b/com/android/server/pm/permission/PermissionSettings.java
@@ -0,0 +1,149 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ * Permissions and other related data. This class is not meant for
+ * direct access outside of the permission package with the sole exception
+ * of package settings. Instead, it should be reference either from the
+ * permission manager or package settings.
+ */
+public class PermissionSettings {
+
+ final boolean mPermissionReviewRequired;
+ /**
+ * All of the permissions known to the system. The mapping is from permission
+ * name to permission object.
+ */
+ private final ArrayMap<String, BasePermission> mPermissions =
+ new ArrayMap<String, BasePermission>();
+ private final Object mLock;
+
+ PermissionSettings(@NonNull Context context, @NonNull Object lock) {
+ mPermissionReviewRequired =
+ context.getResources().getBoolean(R.bool.config_permissionReviewRequired);
+ mLock = lock;
+ }
+
+ public @Nullable BasePermission getPermission(@NonNull String permName) {
+ synchronized (mLock) {
+ return getPermissionLocked(permName);
+ }
+ }
+
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ 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 ? permissionTrees : mPermissions;
+ for (BasePermission bp : permissions.values()) {
+ bp.transfer(origPackageName, newPackageName);
+ }
+ }
+ }
+ }
+
+ public boolean canPropagatePermissionToInstantApp(String permName) {
+ synchronized (mLock) {
+ final BasePermission bp = mPermissions.get(permName);
+ return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant());
+ }
+ }
+
+ public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
+ synchronized (mLock) {
+ readPermissions(mPermissions, parser);
+ }
+ }
+
+ public void writePermissions(XmlSerializer serializer) throws IOException {
+ for (BasePermission bp : mPermissions.values()) {
+ bp.writeLPr(serializer);
+ }
+ }
+
+ public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (!BasePermission.readLPw(out, parser)) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element reading permissions: " + parser.getName() + " at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void dumpPermissions(PrintWriter pw, String packageName,
+ ArraySet<String> permissionNames, boolean externalStorageEnforced,
+ DumpState dumpState) {
+ synchronized (mLock) {
+ boolean printedSomething = false;
+ for (BasePermission bp : mPermissions.values()) {
+ printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
+ externalStorageEnforced, printedSomething, dumpState);
+ }
+ }
+ }
+
+ @Nullable BasePermission getPermissionLocked(@NonNull String permName) {
+ return mPermissions.get(permName);
+ }
+
+ void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
+ mPermissions.put(permName, permission);
+ }
+
+ void removePermissionLocked(@NonNull String permName) {
+ mPermissions.remove(permName);
+ }
+
+ Collection<BasePermission> getAllPermissionsLocked() {
+ return mPermissions.values();
+ }
+}
diff --git a/com/android/server/pm/PermissionsState.java b/com/android/server/pm/permission/PermissionsState.java
index f4d2ad2c..11df3804 100644
--- a/com/android/server/pm/PermissionsState.java
+++ b/com/android/server/pm/permission/PermissionsState.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm;
+package com.android.server.pm.permission;
import android.content.pm.PackageManager;
import android.os.UserHandle;
@@ -406,7 +406,7 @@ public final class PermissionsState {
ensurePermissionData(permission);
}
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permission.getName());
if (permissionData == null) {
if (!mayChangeFlags) {
return false;
@@ -557,7 +557,7 @@ public final class PermissionsState {
}
private int grantPermission(BasePermission permission, int userId) {
- if (hasPermission(permission.name, userId)) {
+ if (hasPermission(permission.getName(), userId)) {
return PERMISSION_OPERATION_FAILURE;
}
@@ -581,21 +581,22 @@ public final class PermissionsState {
}
private int revokePermission(BasePermission permission, int userId) {
- if (!hasPermission(permission.name, userId)) {
+ final String permName = permission.getName();
+ if (!hasPermission(permName, userId)) {
return PERMISSION_OPERATION_FAILURE;
}
final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permName);
if (!permissionData.revoke(userId)) {
return PERMISSION_OPERATION_FAILURE;
}
if (permissionData.isDefault()) {
- ensureNoPermissionData(permission.name);
+ ensureNoPermissionData(permName);
}
if (hasGids) {
@@ -625,13 +626,14 @@ public final class PermissionsState {
}
private PermissionData ensurePermissionData(BasePermission permission) {
+ final String permName = permission.getName();
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permName);
if (permissionData == null) {
permissionData = new PermissionData(permission);
- mPermissions.put(permission.name, permissionData);
+ mPermissions.put(permName, permissionData);
}
return permissionData;
}
@@ -692,7 +694,7 @@ public final class PermissionsState {
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new PermissionState(mPerm.name);
+ userState = new PermissionState(mPerm.getName());
mUserStates.put(userId, userState);
}
@@ -760,7 +762,7 @@ public final class PermissionsState {
}
return userState.mFlags != oldFlags;
} else if (newFlags != 0) {
- userState = new PermissionState(mPerm.name);
+ userState = new PermissionState(mPerm.getName());
userState.mFlags = newFlags;
mUserStates.put(userId, userState);
return true;
diff --git a/com/android/server/policy/PhoneWindowManager.java b/com/android/server/policy/PhoneWindowManager.java
index a806af46..db7817ec 100644
--- a/com/android/server/policy/PhoneWindowManager.java
+++ b/com/android/server/policy/PhoneWindowManager.java
@@ -20,9 +20,12 @@ 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.ActivityManager.StackId.HOME_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_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.DISPLAY_SERVICE;
import static android.content.Context.WINDOW_SERVICE;
@@ -198,6 +201,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.MutableBoolean;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -296,13 +300,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
- static final int LONG_PRESS_BACK_NOTHING = 0;
- static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
-
static final int MULTI_PRESS_POWER_NOTHING = 0;
static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
+ static final int LONG_PRESS_BACK_NOTHING = 0;
+ static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
+
// Number of presses needed before we induce panic press behavior on the back button
static final int PANIC_PRESS_BACK_COUNT = 4;
static final int PANIC_PRESS_BACK_NOTHING = 0;
@@ -565,7 +569,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mLongPressOnBackBehavior;
int mPanicPressOnBackBehavior;
int mShortPressOnSleepBehavior;
- int mShortPressWindowBehavior;
+ int mShortPressOnWindowBehavior;
volatile boolean mAwake;
boolean mScreenOnEarly;
boolean mScreenOnFully;
@@ -2180,9 +2184,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
}
- mShortPressWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
+ mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
- mShortPressWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
+ mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
mNavBarOpacityMode = res.getInteger(
@@ -2626,18 +2630,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
- if (ActivityManager.isHighEndGfx()) {
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- }
- final boolean forceWindowDrawsStatusBarBackground =
- (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND)
- != 0;
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || forceWindowDrawsStatusBarBackground
- && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- }
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+ attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ }
+ final boolean forceWindowDrawsStatusBarBackground =
+ (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ || forceWindowDrawsStatusBarBackground
+ && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
+ attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
}
}
@@ -3627,10 +3628,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return -1;
}
- // If the device is in Vr mode, drop the volume keys and don't
- // forward it to the application/dispatch the audio event.
+ // If the device is in VR mode and keys are "internal" (e.g. on the side of the
+ // device), then drop the volume keys and don't forward it to the application/dispatch
+ // the audio event.
if (mPersistentVrModeEnabled) {
- return -1;
+ final InputDevice d = event.getDevice();
+ if (d != null && !d.isExternal()) {
+ return -1;
+ }
}
} else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) {
// Pass through keyboard navigation keys.
@@ -6272,7 +6277,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
case KeyEvent.KEYCODE_WINDOW: {
- if (mShortPressWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
+ if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
if (mPictureInPictureVisible) {
// Consumes the key only if picture-in-picture is visible to show
// picture-in-picture control menu. This gives a chance to the foreground
@@ -7915,8 +7920,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
- mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
- mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
@@ -8319,9 +8326,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
pw.print(" mSystemBooted="); pw.println(mSystemBooted);
- pw.print(prefix); pw.print("mLidState="); pw.print(mLidState);
- pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation);
- pw.print(" mCameraLensCoverState="); pw.print(mCameraLensCoverState);
+ pw.print(prefix); pw.print("mLidState=");
+ pw.print(WindowManagerFuncs.lidStateToString(mLidState));
+ pw.print(" mLidOpenRotation=");
+ pw.println(Surface.rotationToString(mLidOpenRotation));
+ pw.print(prefix); pw.print("mCameraLensCoverState=");
+ pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
|| mForceClearedSystemUiFlags != 0) {
@@ -8339,16 +8349,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
pw.println(mWakeGestureEnabledSetting);
- pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation);
- pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
- pw.print(" mDockMode="); pw.print(mDockMode);
- pw.print(" mEnableCarDockHomeCapture="); pw.print(mEnableCarDockHomeCapture);
- pw.print(" mCarDockRotation="); pw.print(mCarDockRotation);
- pw.print(" mDeskDockRotation="); pw.println(mDeskDockRotation);
- pw.print(prefix); pw.print("mUserRotationMode="); pw.print(mUserRotationMode);
- pw.print(" mUserRotation="); pw.print(mUserRotation);
- pw.print(" mAllowAllRotations="); pw.println(mAllowAllRotations);
- pw.print(prefix); pw.print("mCurrentAppOrientation="); pw.println(mCurrentAppOrientation);
+ pw.print(prefix);
+ pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation);
+ pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled);
+ pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode));
+ pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode));
+ pw.print(prefix); pw.print("mEnableCarDockHomeCapture=");
+ pw.print(mEnableCarDockHomeCapture);
+ pw.print(" mCarDockRotation=");
+ pw.print(Surface.rotationToString(mCarDockRotation));
+ pw.print(" mDeskDockRotation=");
+ pw.println(Surface.rotationToString(mDeskDockRotation));
+ pw.print(prefix); pw.print("mUserRotationMode=");
+ pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
+ pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation));
+ pw.print(" mAllowAllRotations=");
+ pw.println(allowAllRotationsToString(mAllowAllRotations));
+ pw.print(prefix); pw.print("mCurrentAppOrientation=");
+ pw.println(ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
pw.print(prefix); pw.print("mCarDockEnablesAccelerometer=");
pw.print(mCarDockEnablesAccelerometer);
pw.print(" mDeskDockEnablesAccelerometer=");
@@ -8357,23 +8375,54 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(mLidKeyboardAccessibility);
pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
- pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep);
+ pw.print(prefix); pw.print("mLidControlsSleep="); pw.println(mLidControlsSleep);
pw.print(prefix);
- pw.print(" mLongPressOnBackBehavior="); pw.println(mLongPressOnBackBehavior);
+ pw.print("mLongPressOnBackBehavior=");
+ pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior));
pw.print(prefix);
- pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
- pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior);
+ pw.print("mPanicPressOnBackBehavior=");
+ pw.println(panicPressOnBackBehaviorToString(mPanicPressOnBackBehavior));
pw.print(prefix);
- pw.print("mDoublePressOnPowerBehavior="); pw.print(mDoublePressOnPowerBehavior);
- pw.print(" mTriplePressOnPowerBehavior="); pw.println(mTriplePressOnPowerBehavior);
- pw.print(prefix); pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
- pw.print(prefix); pw.print("mAwake="); pw.println(mAwake);
- pw.print(prefix); pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
+ pw.print("mLongPressOnHomeBehavior=");
+ pw.println(longPressOnHomeBehaviorToString(mLongPressOnHomeBehavior));
+ pw.print(prefix);
+ pw.print("mDoubleTapOnHomeBehavior=");
+ pw.println(doubleTapOnHomeBehaviorToString(mDoubleTapOnHomeBehavior));
+ pw.print(prefix);
+ pw.print("mShortPressOnPowerBehavior=");
+ pw.println(shortPressOnPowerBehaviorToString(mShortPressOnPowerBehavior));
+ pw.print(prefix);
+ pw.print("mLongPressOnPowerBehavior=");
+ pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
+ pw.print(prefix);
+ pw.print("mDoublePressOnPowerBehavior=");
+ pw.println(multiPressOnPowerBehaviorToString(mDoublePressOnPowerBehavior));
+ pw.print(prefix);
+ pw.print("mTriplePressOnPowerBehavior=");
+ pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior));
+ pw.print(prefix);
+ pw.print("mShortPressOnSleepBehavior=");
+ pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior));
+ pw.print(prefix);
+ pw.print("mShortPressOnWindowBehavior=");
+ pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior));
+ pw.print(prefix);
+ pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
+ pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed);
+ pw.print(prefix);
+ pw.print("mIncallPowerBehavior=");
+ pw.print(incallPowerBehaviorToString(mIncallPowerBehavior));
+ pw.print(" mIncallBackBehavior=");
+ pw.print(incallBackBehaviorToString(mIncallBackBehavior));
+ pw.print(" mEndcallBehavior=");
+ pw.println(endcallBehaviorToString(mEndcallBehavior));
+ pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
+ pw.print(prefix);
+ pw.print("mAwake="); pw.print(mAwake);
+ pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
- pw.print(prefix); pw.print("mOrientationSensorEnabled=");
- pw.println(mOrientationSensorEnabled);
pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft);
pw.print(","); pw.print(mOverscanScreenTop);
pw.print(") "); pw.print(mOverscanScreenWidth);
@@ -8439,8 +8488,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mLastInputMethodTargetWindow=");
pw.println(mLastInputMethodTargetWindow);
}
- pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed=");
- pw.println(mDismissImeOnBackKeyPressed);
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar=");
pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
@@ -8473,26 +8520,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen);
pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded);
- pw.print(" mKeyguardOccludedChanged="); pw.println(mKeyguardOccludedChanged);
+ pw.print(prefix);
+ pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
pw.print(" mForceStatusBarFromKeyguard=");
pw.println(mForceStatusBarFromKeyguard);
- pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
- pw.print(prefix); pw.print("mEndcallBehavior="); pw.print(mEndcallBehavior);
- pw.print(" mIncallPowerBehavior="); pw.print(mIncallPowerBehavior);
- pw.print(" mIncallBackBehavior="); pw.print(mIncallBackBehavior);
- pw.print(" mLongPressOnHomeBehavior="); pw.println(mLongPressOnHomeBehavior);
- pw.print(prefix); pw.print("mLandscapeRotation="); pw.print(mLandscapeRotation);
- pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation);
- pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation);
- pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation);
- pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
+ pw.print(prefix); pw.print("mLandscapeRotation=");
+ pw.print(Surface.rotationToString(mLandscapeRotation));
+ pw.print(" mSeascapeRotation=");
+ pw.println(Surface.rotationToString(mSeascapeRotation));
+ pw.print(prefix); pw.print("mPortraitRotation=");
+ pw.print(Surface.rotationToString(mPortraitRotation));
+ pw.print(" mUpsideDownRotation=");
+ pw.println(Surface.rotationToString(mUpsideDownRotation));
+ pw.print(prefix); pw.print("mDemoHdmiRotation=");
+ pw.print(Surface.rotationToString(mDemoHdmiRotation));
pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
- pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+ pw.print(prefix); pw.print("mUndockedHdmiRotation=");
+ pw.println(Surface.rotationToString(mUndockedHdmiRotation));
if (mHasFeatureLeanback) {
pw.print(prefix);
pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
@@ -8519,5 +8568,169 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.dump(prefix, pw);
}
+
+ pw.print(prefix); pw.println("Looper state:");
+ mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
+ }
+
+ private static String allowAllRotationsToString(int allowAll) {
+ switch (allowAll) {
+ case -1:
+ return "unknown";
+ case 0:
+ return "false";
+ case 1:
+ return "true";
+ default:
+ return Integer.toString(allowAll);
+ }
+ }
+
+ private static String endcallBehaviorToString(int behavior) {
+ StringBuilder sb = new StringBuilder();
+ if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
+ sb.append("home|");
+ }
+ if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
+ sb.append("sleep|");
+ }
+
+ final int N = sb.length();
+ if (N == 0) {
+ return "<nothing>";
+ } else {
+ // Chop off the trailing '|'
+ return sb.substring(0, N - 1);
+ }
+ }
+
+ private static String incallPowerBehaviorToString(int behavior) {
+ if ((behavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0) {
+ return "hangup";
+ } else {
+ return "sleep";
+ }
+ }
+
+ private static String incallBackBehaviorToString(int behavior) {
+ if ((behavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0) {
+ return "hangup";
+ } else {
+ return "<nothing>";
+ }
+ }
+
+ private static String longPressOnBackBehaviorToString(int behavior) {
+ switch (behavior) {
+ case LONG_PRESS_BACK_NOTHING:
+ return "LONG_PRESS_BACK_NOTHING";
+ case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
+ return "LONG_PRESS_BACK_GO_TO_VOICE_ASSIST";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String panicPressOnBackBehaviorToString(int behavior) {
+ switch (behavior) {
+ case PANIC_PRESS_BACK_NOTHING:
+ return "PANIC_PRESS_BACK_NOTHING";
+ case PANIC_PRESS_BACK_HOME:
+ return "PANIC_PRESS_BACK_HOME";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String longPressOnHomeBehaviorToString(int behavior) {
+ switch (behavior) {
+ case LONG_PRESS_HOME_NOTHING:
+ return "LONG_PRESS_HOME_NOTHING";
+ case LONG_PRESS_HOME_ALL_APPS:
+ return "LONG_PRESS_HOME_ALL_APPS";
+ case LONG_PRESS_HOME_ASSIST:
+ return "LONG_PRESS_HOME_ASSIST";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String doubleTapOnHomeBehaviorToString(int behavior) {
+ switch (behavior) {
+ case DOUBLE_TAP_HOME_NOTHING:
+ return "DOUBLE_TAP_HOME_NOTHING";
+ case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
+ return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String shortPressOnPowerBehaviorToString(int behavior) {
+ switch (behavior) {
+ case SHORT_PRESS_POWER_NOTHING:
+ return "SHORT_PRESS_POWER_NOTHING";
+ case SHORT_PRESS_POWER_GO_TO_SLEEP:
+ return "SHORT_PRESS_POWER_GO_TO_SLEEP";
+ case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
+ return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP";
+ case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
+ return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME";
+ case SHORT_PRESS_POWER_GO_HOME:
+ return "SHORT_PRESS_POWER_GO_HOME";
+ case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME:
+ return "SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String longPressOnPowerBehaviorToString(int behavior) {
+ switch (behavior) {
+ case LONG_PRESS_POWER_NOTHING:
+ return "LONG_PRESS_POWER_NOTHING";
+ case LONG_PRESS_POWER_GLOBAL_ACTIONS:
+ return "LONG_PRESS_POWER_GLOBAL_ACTIONS";
+ case LONG_PRESS_POWER_SHUT_OFF:
+ return "LONG_PRESS_POWER_SHUT_OFF";
+ case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
+ return "LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+ private static String multiPressOnPowerBehaviorToString(int behavior) {
+ switch (behavior) {
+ case MULTI_PRESS_POWER_NOTHING:
+ return "MULTI_PRESS_POWER_NOTHING";
+ case MULTI_PRESS_POWER_THEATER_MODE:
+ return "MULTI_PRESS_POWER_THEATER_MODE";
+ case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
+ return "MULTI_PRESS_POWER_BRIGHTNESS_BOOST";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String shortPressOnSleepBehaviorToString(int behavior) {
+ switch (behavior) {
+ case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
+ return "SHORT_PRESS_SLEEP_GO_TO_SLEEP";
+ case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
+ return "SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
+ private static String shortPressOnWindowBehaviorToString(int behavior) {
+ switch (behavior) {
+ case SHORT_PRESS_WINDOW_NOTHING:
+ return "SHORT_PRESS_WINDOW_NOTHING";
+ case SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE:
+ return "SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE";
+ default:
+ return Integer.toString(behavior);
+ }
}
}
diff --git a/com/android/server/policy/WindowOrientationListener.java b/com/android/server/policy/WindowOrientationListener.java
index 64f64c0d..169fd278 100644
--- a/com/android/server/policy/WindowOrientationListener.java
+++ b/com/android/server/policy/WindowOrientationListener.java
@@ -26,6 +26,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
+import android.view.Surface;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -236,7 +237,7 @@ public abstract class WindowOrientationListener {
pw.println(prefix + TAG);
prefix += " ";
pw.println(prefix + "mEnabled=" + mEnabled);
- pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+ pw.println(prefix + "mCurrentRotation=" + Surface.rotationToString(mCurrentRotation));
pw.println(prefix + "mSensorType=" + mSensorType);
pw.println(prefix + "mSensor=" + mSensor);
pw.println(prefix + "mRate=" + mRate);
@@ -1026,8 +1027,9 @@ public abstract class WindowOrientationListener {
public void dumpLocked(PrintWriter pw, String prefix) {
pw.println(prefix + "OrientationSensorJudge");
prefix += " ";
- pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
- pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+ pw.println(prefix + "mDesiredRotation=" + Surface.rotationToString(mDesiredRotation));
+ pw.println(prefix + "mProposedRotation="
+ + Surface.rotationToString(mProposedRotation));
pw.println(prefix + "mTouching=" + mTouching);
pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
}
diff --git a/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 50e5e7bd..70cd54ff 100644
--- a/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -1,5 +1,7 @@
package com.android.server.policy.keyguard;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -13,6 +15,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.OnKeyguardExitResult;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -201,7 +204,10 @@ public class KeyguardServiceDelegate {
mKeyguardState.reset();
mHandler.post(() -> {
try {
- ActivityManager.getService().setLockScreenShown(true);
+ // There are no longer any keyguard windows on secondary displays, so pass
+ // INVALID_DISPLAY. All that means is that showWhenLocked activities on
+ // secondary displays now get to show.
+ ActivityManager.getService().setLockScreenShown(true, INVALID_DISPLAY);
} catch (RemoteException e) {
// Local call.
}
@@ -412,13 +418,45 @@ public class KeyguardServiceDelegate {
pw.println(prefix + "systemIsReady=" + mKeyguardState.systemIsReady);
pw.println(prefix + "deviceHasKeyguard=" + mKeyguardState.deviceHasKeyguard);
pw.println(prefix + "enabled=" + mKeyguardState.enabled);
- pw.println(prefix + "offReason=" + mKeyguardState.offReason);
+ pw.println(prefix + "offReason=" +
+ WindowManagerPolicy.offReasonToString(mKeyguardState.offReason));
pw.println(prefix + "currentUser=" + mKeyguardState.currentUser);
pw.println(prefix + "bootCompleted=" + mKeyguardState.bootCompleted);
- pw.println(prefix + "screenState=" + mKeyguardState.screenState);
- pw.println(prefix + "interactiveState=" + mKeyguardState.interactiveState);
+ pw.println(prefix + "screenState=" + screenStateToString(mKeyguardState.screenState));
+ pw.println(prefix + "interactiveState=" +
+ interactiveStateToString(mKeyguardState.interactiveState));
if (mKeyguardService != null) {
mKeyguardService.dump(prefix, pw);
}
}
+
+ private static String screenStateToString(int screen) {
+ switch (screen) {
+ case SCREEN_STATE_OFF:
+ return "SCREEN_STATE_OFF";
+ case SCREEN_STATE_TURNING_ON:
+ return "SCREEN_STATE_TURNING_ON";
+ case SCREEN_STATE_ON:
+ return "SCREEN_STATE_ON";
+ case SCREEN_STATE_TURNING_OFF:
+ return "SCREEN_STATE_TURNING_OFF";
+ default:
+ return Integer.toString(screen);
+ }
+ }
+
+ private static String interactiveStateToString(int interactive) {
+ switch (interactive) {
+ case INTERACTIVE_STATE_SLEEP:
+ return "INTERACTIVE_STATE_SLEEP";
+ case INTERACTIVE_STATE_WAKING:
+ return "INTERACTIVE_STATE_WAKING";
+ case INTERACTIVE_STATE_AWAKE:
+ return "INTERACTIVE_STATE_AWAKE";
+ case INTERACTIVE_STATE_GOING_TO_SLEEP:
+ return "INTERACTIVE_STATE_GOING_TO_SLEEP";
+ default:
+ return Integer.toString(interactive);
+ }
+ }
}
diff --git a/com/android/server/power/PowerManagerService.java b/com/android/server/power/PowerManagerService.java
index 3b701302..b917dae2 100644
--- a/com/android/server/power/PowerManagerService.java
+++ b/com/android/server/power/PowerManagerService.java
@@ -206,6 +206,8 @@ public final class PowerManagerService extends SystemService
private static final String REASON_REBOOT = "reboot";
private static final String REASON_USERREQUESTED = "shutdown,userrequested";
private static final String REASON_THERMAL_SHUTDOWN = "shutdown,thermal";
+ private static final String REASON_LOW_BATTERY = "shutdown,battery";
+ private static final String REASON_BATTERY_THERMAL_STATE = "shutdown,thermal,battery";
private static final String TRACE_SCREEN_ON = "Screen turning on";
@@ -1569,12 +1571,15 @@ public final class PowerManagerService extends SystemService
return true;
}
- private void setWakefulnessLocked(int wakefulness, int reason) {
+ @VisibleForTesting
+ void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness != wakefulness) {
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
- mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
+ if (mNotifier != null) {
+ mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
+ }
}
}
@@ -2432,11 +2437,8 @@ public final class PowerManagerService extends SystemService
return value >= -1.0f && value <= 1.0f;
}
- private int getDesiredScreenPolicyLocked() {
- if (mIsVrModeEnabled) {
- return DisplayPowerRequest.POLICY_VR;
- }
-
+ @VisibleForTesting
+ int getDesiredScreenPolicyLocked() {
if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
}
@@ -2452,6 +2454,13 @@ public final class PowerManagerService extends SystemService
// doze after screen off. This causes the screen off transition to be skipped.
}
+ // It is important that POLICY_VR check happens after the wakefulness checks above so
+ // that VR-mode does not prevent displays from transitioning to the correct state when
+ // dozing or sleeping.
+ if (mIsVrModeEnabled) {
+ return DisplayPowerRequest.POLICY_VR;
+ }
+
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted
@@ -3113,6 +3122,11 @@ public final class PowerManagerService extends SystemService
}
}
+ @VisibleForTesting
+ void setVrModeEnabled(boolean enabled) {
+ mIsVrModeEnabled = enabled;
+ }
+
private void powerHintInternal(int hintId, int data) {
nativeSendPowerHint(hintId, data);
}
@@ -3810,7 +3824,7 @@ public final class PowerManagerService extends SystemService
synchronized (mLock) {
if (mIsVrModeEnabled != enabled) {
- mIsVrModeEnabled = enabled;
+ setVrModeEnabled(enabled);
mDirty |= DIRTY_VR_MODE_CHANGED;
updatePowerStateLocked();
}
@@ -4639,6 +4653,10 @@ public final class PowerManagerService extends SystemService
return PowerManager.SHUTDOWN_REASON_USER_REQUESTED;
case REASON_THERMAL_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN;
+ case REASON_LOW_BATTERY:
+ return PowerManager.SHUTDOWN_REASON_LOW_BATTERY;
+ case REASON_BATTERY_THERMAL_STATE:
+ return PowerManager.SHUTDOWN_REASON_BATTERY_THERMAL;
default:
return PowerManager.SHUTDOWN_REASON_UNKNOWN;
}
diff --git a/com/android/server/power/ShutdownThread.java b/com/android/server/power/ShutdownThread.java
index 853e1b26..515fa399 100644
--- a/com/android/server/power/ShutdownThread.java
+++ b/com/android/server/power/ShutdownThread.java
@@ -457,8 +457,7 @@ public final class ShutdownThread extends Thread {
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
diff --git a/com/android/server/stats/StatsCompanionService.java b/com/android/server/stats/StatsCompanionService.java
new file mode 100644
index 00000000..f1fb3e7b
--- /dev/null
+++ b/com/android/server/stats/StatsCompanionService.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+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.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
+import com.android.server.SystemService;
+
+import java.util.Map;
+
+/**
+ * Helper service for statsd (the native stats management service in cmds/statsd/).
+ * Used for registering and receiving alarms on behalf of statsd.
+ * @hide
+ */
+public class StatsCompanionService extends IStatsCompanionService.Stub {
+ static final String TAG = "StatsCompanionService";
+ static final boolean DEBUG = true;
+
+ private final Context mContext;
+ private final AlarmManager mAlarmManager;
+ @GuardedBy("sStatsdLock")
+ private static IStatsManager sStatsd;
+ private static final Object sStatsdLock = new Object();
+
+ private final PendingIntent mAnomalyAlarmIntent;
+ private final PendingIntent mPollingAlarmIntent;
+
+ public StatsCompanionService(Context context) {
+ super();
+ mContext = context;
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+
+ mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(mContext, AnomalyAlarmReceiver.class), 0);
+ mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(mContext, PollingAlarmReceiver.class), 0);
+ }
+
+ public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informAnomalyAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+ }
+ }
+ // AlarmManager releases its own wakelock here.
+ }
+ };
+
+ public final static class PollingAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of polling alarm firing");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of polling alarm firing", e);
+ }
+ }
+ // AlarmManager releases its own wakelock here.
+ }
+ };
+
+ @Override // Binder call
+ public void setAnomalyAlarm(long timestampMs) {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
+ mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelAnomalyAlarm() {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mAnomalyAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void setPollingAlarms(long timestampMs, long intervalMs) {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs
+ + " every " + intervalMs + "ms");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
+ mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs,
+ mPollingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelPollingAlarms() {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Cancelling polling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPollingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ // These values must be kept in sync with cmd/statsd/StatsPullerManager.h.
+ // TODO: pull the constant from stats_events.proto instead
+ private static final int PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
+ @Override // Binder call
+ public String pullData(int pullCode) {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+
+ StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+ switch (pullCode) {
+ case PULL_CODE_KERNEL_WAKELOCKS:
+ final KernelWakelockStats wakelockStats =
+ mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+ s.append("Wakelock ")
+ .append(name)
+ .append(", time=")
+ .append(kws.mTotalTime)
+ .append(", count=")
+ .append(kws.mCount)
+ .append('\n');
+ }
+ break;
+ default:
+ Slog.w(TAG, "No such pollable data as " + pullCode);
+ return null;
+ }
+ return s.toString();
+ }
+
+ @Override // Binder call
+ public void statsdReady() {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "learned that statsdReady");
+ sayHiToStatsd(); // tell statsd that we're ready too and link to it
+ }
+
+ private void enforceCallingPermission() {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
+ }
+
+ // Lifecycle and related code
+
+ /** Fetches the statsd IBinder service */
+ private static IStatsManager fetchStatsdService() {
+ return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+ }
+
+ public static final class Lifecycle extends SystemService {
+ private StatsCompanionService mStatsCompanionService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mStatsCompanionService = new StatsCompanionService(getContext());
+ try {
+ publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
+ if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to publishBinderService", e);
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mStatsCompanionService.systemReady();
+ }
+ }
+ }
+
+ /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
+ private void systemReady() {
+ if (DEBUG) Slog.d(TAG, "Learned that systemReady");
+ sayHiToStatsd();
+ }
+
+ /** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */
+ private void sayHiToStatsd() {
+ synchronized (sStatsdLock) {
+ if (sStatsd != null) {
+ Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+ new IllegalStateException("sStatsd is not null when being fetched"));
+ return;
+ }
+ sStatsd = fetchStatsdService();
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd");
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
+ try {
+ sStatsd.statsCompanionReady();
+ // If the statsCompanionReady two-way binder call returns, link to statsd.
+ try {
+ sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+ forgetEverything();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+ forgetEverything();
+ }
+ }
+ }
+
+ private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
+ forgetEverything();
+ }
+ }
+
+ private void forgetEverything() {
+ synchronized (sStatsdLock) {
+ sStatsd = null;
+ cancelAnomalyAlarm();
+ cancelPollingAlarms();
+ }
+ }
+
+}
diff --git a/com/android/server/statusbar/StatusBarManagerService.java b/com/android/server/statusbar/StatusBarManagerService.java
index 38dc33fa..bdfbe481 100644
--- a/com/android/server/statusbar/StatusBarManagerService.java
+++ b/com/android/server/statusbar/StatusBarManagerService.java
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.service.notification.NotificationStats;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -97,12 +98,58 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
int what2;
IBinder token;
+ public DisableRecord(int userId, IBinder token) {
+ this.userId = userId;
+ this.token = token;
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException re) {
+ // Give up
+ }
+ }
+
+ @Override
public void binderDied() {
Slog.i(TAG, "binder died for pkg=" + pkg);
disableForUser(0, token, pkg, userId);
disable2ForUser(0, token, pkg, userId);
token.unlinkToDeath(this, 0);
}
+
+ public void setFlags(int what, int which, String pkg) {
+ switch (which) {
+ case 1:
+ what1 = what;
+ return;
+ case 2:
+ what2 = what;
+ return;
+ default:
+ Slog.w(TAG, "Can't set unsupported disable flag " + which
+ + ": 0x" + Integer.toHexString(what));
+ }
+ this.pkg = pkg;
+ }
+
+ public int getFlags(int which) {
+ switch (which) {
+ case 1: return what1;
+ case 2: return what2;
+ default:
+ Slog.w(TAG, "Can't get unsupported disable flag " + which);
+ return 0;
+ }
+ }
+
+ public boolean isEmpty() {
+ return what1 == 0 && what2 == 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("userId=%d what1=0x%08X what2=0x%08X pkg=%s token=%s",
+ userId, what1, what2, pkg, token);
+ }
}
/**
@@ -483,7 +530,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
*/
@Override
public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
- enforceStatusBar();
+ enforceStatusBarService();
synchronized (mLock) {
disableLocked(userId, what, token, pkg, 2);
@@ -897,13 +944,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public void onNotificationClear(String pkg, String tag, int id, int userId) {
+ public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
+ mNotificationDelegate.onNotificationClear(
+ callingUid, callingPid, pkg, tag, id, userId, key, dismissalSurface);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -937,6 +986,28 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
+ public void onNotificationDirectReplied(String key) throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationDirectReplied(key);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onNotificationSettingsViewed(String key) throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationSettingsViewed(key);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void onClearAllNotifications(int userId) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
@@ -970,42 +1041,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
Slog.d(TAG, "manageDisableList userId=" + userId
+ " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
}
- // update the list
+
+ // Find matching record, if any
final int N = mDisableRecords.size();
- DisableRecord tok = null;
+ DisableRecord record = null;
int i;
- for (i=0; i<N; i++) {
- DisableRecord t = mDisableRecords.get(i);
- if (t.token == token && t.userId == userId) {
- tok = t;
+ for (i = 0; i < N; i++) {
+ DisableRecord r = mDisableRecords.get(i);
+ if (r.token == token && r.userId == userId) {
+ record = r;
break;
}
}
- if (what == 0 || !token.isBinderAlive()) {
- if (tok != null) {
+
+ // Remove record if binder is already dead
+ if (!token.isBinderAlive()) {
+ if (record != null) {
mDisableRecords.remove(i);
- tok.token.unlinkToDeath(tok, 0);
- }
- } else {
- if (tok == null) {
- tok = new DisableRecord();
- tok.userId = userId;
- try {
- token.linkToDeath(tok, 0);
- }
- catch (RemoteException ex) {
- return; // give up
- }
- mDisableRecords.add(tok);
+ record.token.unlinkToDeath(record, 0);
}
- if (which == 1) {
- tok.what1 = what;
- } else {
- tok.what2 = what;
+ return;
+ }
+
+ // Update existing record
+ if (record != null) {
+ record.setFlags(what, which, pkg);
+ if (record.isEmpty()) {
+ mDisableRecords.remove(i);
+ record.token.unlinkToDeath(record, 0);
}
- tok.token = token;
- tok.pkg = pkg;
+ return;
}
+
+ // Record doesn't exist, so we create a new one
+ record = new DisableRecord(userId, token);
+ record.setFlags(what, which, pkg);
+ mDisableRecords.add(record);
}
// lock on mDisableRecords
@@ -1016,7 +1087,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
for (int i=0; i<N; i++) {
final DisableRecord rec = mDisableRecords.get(i);
if (rec.userId == userId) {
- net |= (which == 1) ? rec.what1 : rec.what2;
+ net |= rec.getFlags(which);
}
}
return net;
@@ -1036,11 +1107,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
pw.println(" mDisableRecords.size=" + N);
for (int i=0; i<N; i++) {
DisableRecord tok = mDisableRecords.get(i);
- pw.println(" [" + i + "] userId=" + tok.userId
- + " what1=0x" + Integer.toHexString(tok.what1)
- + " what2=0x" + Integer.toHexString(tok.what2)
- + " pkg=" + tok.pkg
- + " token=" + tok.token);
+ pw.println(" [" + i + "] " + tok);
}
pw.println(" mCurrentUserId=" + mCurrentUserId);
pw.println(" mIcons=");
diff --git a/com/android/server/storage/AppCollector.java b/com/android/server/storage/AppCollector.java
index 03b754f7..0b51f9cc 100644
--- a/com/android/server/storage/AppCollector.java
+++ b/com/android/server/storage/AppCollector.java
@@ -135,7 +135,7 @@ public class AppCollector {
PackageStats packageStats = new PackageStats(app.packageName,
user.id);
packageStats.cacheSize = storageStats.getCacheBytes();
- packageStats.codeSize = storageStats.getCodeBytes();
+ packageStats.codeSize = storageStats.getAppBytes();
packageStats.dataSize = storageStats.getDataBytes();
stats.add(packageStats);
} catch (NameNotFoundException | IOException e) {
diff --git a/com/android/server/storage/DiskStatsFileLogger.java b/com/android/server/storage/DiskStatsFileLogger.java
index 0094ab55..1db3ec4c 100644
--- a/com/android/server/storage/DiskStatsFileLogger.java
+++ b/com/android/server/storage/DiskStatsFileLogger.java
@@ -56,10 +56,12 @@ public class DiskStatsFileLogger {
public static final String SYSTEM_KEY = "systemSize";
public static final String MISC_KEY = "otherSize";
public static final String APP_SIZE_AGG_KEY = "appSize";
+ public static final String APP_DATA_SIZE_AGG_KEY = "appDataSize";
public static final String APP_CACHE_AGG_KEY = "cacheSize";
public static final String PACKAGE_NAMES_KEY = "packageNames";
public static final String APP_SIZES_KEY = "appSizes";
public static final String APP_CACHES_KEY = "cacheSizes";
+ public static final String APP_DATA_KEY = "appDataSizes";
public static final String LAST_QUERY_TIMESTAMP_KEY = "queryTime";
private MeasurementResult mResult;
@@ -114,31 +116,39 @@ public class DiskStatsFileLogger {
private void addAppsToJson(JSONObject json) throws JSONException {
JSONArray names = new JSONArray();
JSONArray appSizeList = new JSONArray();
+ JSONArray appDataSizeList = new JSONArray();
JSONArray cacheSizeList = new JSONArray();
long appSizeSum = 0L;
+ long appDataSizeSum = 0L;
long cacheSizeSum = 0L;
boolean isExternal = Environment.isExternalStorageEmulated();
for (Map.Entry<String, PackageStats> entry : filterOnlyPrimaryUser().entrySet()) {
PackageStats stat = entry.getValue();
- long appSize = stat.codeSize + stat.dataSize;
+ long appSize = stat.codeSize;
+ long appDataSize = stat.dataSize;
long cacheSize = stat.cacheSize;
if (isExternal) {
- appSize += stat.externalCodeSize + stat.externalDataSize;
+ appSize += stat.externalCodeSize;
+ appDataSize += stat.externalDataSize;
cacheSize += stat.externalCacheSize;
}
appSizeSum += appSize;
+ appDataSizeSum += appDataSize;
cacheSizeSum += cacheSize;
names.put(stat.packageName);
appSizeList.put(appSize);
+ appDataSizeList.put(appDataSize);
cacheSizeList.put(cacheSize);
}
json.put(PACKAGE_NAMES_KEY, names);
json.put(APP_SIZES_KEY, appSizeList);
json.put(APP_CACHES_KEY, cacheSizeList);
+ json.put(APP_DATA_KEY, appDataSizeList);
json.put(APP_SIZE_AGG_KEY, appSizeSum);
json.put(APP_CACHE_AGG_KEY, cacheSizeSum);
+ json.put(APP_DATA_SIZE_AGG_KEY, appDataSizeSum);
}
/**
diff --git a/com/android/server/timezone/IntentHelper.java b/com/android/server/timezone/IntentHelper.java
index 0cb90657..5de54321 100644
--- a/com/android/server/timezone/IntentHelper.java
+++ b/com/android/server/timezone/IntentHelper.java
@@ -23,15 +23,22 @@ package com.android.server.timezone;
*/
interface IntentHelper {
- void initialize(String updateAppPackageName, String dataAppPackageName, Listener listener);
+ void initialize(String updateAppPackageName, String dataAppPackageName,
+ PackageTracker packageTracker);
void sendTriggerUpdateCheck(CheckToken checkToken);
- void enableReliabilityTriggering();
+ /**
+ * Schedule a "reliability trigger" after at least minimumDelayMillis, replacing any existing
+ * scheduled one. A reliability trigger ensures that the {@link PackageTracker} can pick up
+ * reliably if a previous update check did not complete for some reason. It can happen when
+ * the device is idle. The trigger is expected to call
+ * {@link PackageTracker#triggerUpdateIfNeeded(boolean)} with a {@code false} value.
+ */
+ void scheduleReliabilityTrigger(long minimumDelayMillis);
- void disableReliabilityTriggering();
-
- interface Listener {
- void triggerUpdateIfNeeded(boolean packageUpdated);
- }
+ /**
+ * Make sure there is no reliability trigger scheduled. No-op if there wasn't one.
+ */
+ void unscheduleReliabilityTrigger();
}
diff --git a/com/android/server/timezone/IntentHelperImpl.java b/com/android/server/timezone/IntentHelperImpl.java
index 6db70cd8..6e6259d9 100644
--- a/com/android/server/timezone/IntentHelperImpl.java
+++ b/com/android/server/timezone/IntentHelperImpl.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PatternMatcher;
+import android.os.UserHandle;
import android.util.Slog;
/**
@@ -36,16 +37,13 @@ final class IntentHelperImpl implements IntentHelper {
private final Context mContext;
private String mUpdaterAppPackageName;
- private boolean mReliabilityReceiverEnabled;
- private Receiver mReliabilityReceiver;
-
IntentHelperImpl(Context context) {
mContext = context;
}
@Override
- public void initialize(
- String updaterAppPackageName, String dataAppPackageName, Listener listener) {
+ public void initialize(String updaterAppPackageName, String dataAppPackageName,
+ PackageTracker packageTracker) {
mUpdaterAppPackageName = updaterAppPackageName;
// Register for events of interest.
@@ -78,10 +76,10 @@ final class IntentHelperImpl implements IntentHelper {
// We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are
// not expected to need local data.
- Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */);
- mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
-
- mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */);
+ Receiver packageUpdateReceiver = new Receiver(packageTracker);
+ mContext.registerReceiverAsUser(
+ packageUpdateReceiver, UserHandle.SYSTEM, packageIntentFilter,
+ null /* broadcastPermission */, null /* default handler */);
}
/** Sends an intent to trigger an update check. */
@@ -93,39 +91,26 @@ final class IntentHelperImpl implements IntentHelper {
}
@Override
- public synchronized void enableReliabilityTriggering() {
- if (!mReliabilityReceiverEnabled) {
- // The intent filter that exists to make updates reliable in the event of failures /
- // reboots.
- IntentFilter reliabilityIntentFilter = new IntentFilter();
- reliabilityIntentFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
- mContext.registerReceiver(mReliabilityReceiver, reliabilityIntentFilter);
- mReliabilityReceiverEnabled = true;
- }
+ public synchronized void scheduleReliabilityTrigger(long minimumDelayMillis) {
+ TimeZoneUpdateIdler.schedule(mContext, minimumDelayMillis);
}
@Override
- public synchronized void disableReliabilityTriggering() {
- if (mReliabilityReceiverEnabled) {
- mContext.unregisterReceiver(mReliabilityReceiver);
- mReliabilityReceiverEnabled = false;
- }
+ public synchronized void unscheduleReliabilityTrigger() {
+ TimeZoneUpdateIdler.unschedule(mContext);
}
private static class Receiver extends BroadcastReceiver {
- private final Listener mListener;
- private final boolean mPackageUpdated;
+ private final PackageTracker mPackageTracker;
- private Receiver(Listener listener, boolean packageUpdated) {
- mListener = listener;
- mPackageUpdated = packageUpdated;
+ private Receiver(PackageTracker packageTracker) {
+ mPackageTracker = packageTracker;
}
@Override
public void onReceive(Context context, Intent intent) {
Slog.d(TAG, "Received intent: " + intent.toString());
- mListener.triggerUpdateIfNeeded(mPackageUpdated);
+ mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */);
}
}
-
}
diff --git a/com/android/server/timezone/PackageTracker.java b/com/android/server/timezone/PackageTracker.java
index 24e0fe48..f0306b9b 100644
--- a/com/android/server/timezone/PackageTracker.java
+++ b/com/android/server/timezone/PackageTracker.java
@@ -51,7 +51,7 @@ import java.io.PrintWriter;
*/
// Also made non-final so it can be mocked.
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class PackageTracker implements IntentHelper.Listener {
+public class PackageTracker {
private static final String TAG = "timezone.PackageTracker";
private final PackageManagerHelper mPackageManagerHelper;
@@ -72,6 +72,13 @@ public class PackageTracker implements IntentHelper.Listener {
// The number of failed checks in a row before reliability checks should stop happening.
private long mFailedCheckRetryCount;
+ /*
+ * The minimum delay between a successive reliability triggers / other operations. Should to be
+ * larger than mCheckTimeAllowedMillis to avoid reliability triggers happening during package
+ * update checks.
+ */
+ private int mDelayBeforeReliabilityCheckMillis;
+
// Reliability check state: If a check was triggered but not acknowledged within
// mCheckTimeAllowedMillis then another one can be triggered.
private Long mLastTriggerTimestamp = null;
@@ -122,6 +129,7 @@ public class PackageTracker implements IntentHelper.Listener {
mDataAppPackageName = mConfigHelper.getDataAppPackageName();
mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis();
mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount();
+ mDelayBeforeReliabilityCheckMillis = mCheckTimeAllowedMillis + (60 * 1000);
// Validate the device configuration including the application packages.
// The manifest entries in the apps themselves are not validated until use as they can
@@ -135,9 +143,10 @@ public class PackageTracker implements IntentHelper.Listener {
// Initialize the intent helper.
mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this);
- // Enable the reliability triggering so we will have at least one reliability trigger if
- // a package isn't updated.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger so we will have at least one after boot. This will allow
+ // us to catch if a package updated wasn't handled to completion. There's no hurry: it's ok
+ // to delay for a while before doing this even if idle.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
Slog.i(TAG, "Time zone updater / data package tracking enabled");
}
@@ -195,7 +204,6 @@ public class PackageTracker implements IntentHelper.Listener {
* @param packageChanged true if this method was called because a known packaged definitely
* changed, false if the cause is a reliability trigger
*/
- @Override
public synchronized void triggerUpdateIfNeeded(boolean packageChanged) {
if (!mTrackingEnabled) {
throw new IllegalStateException("Unexpected call. Tracking is disabled.");
@@ -212,8 +220,8 @@ public class PackageTracker implements IntentHelper.Listener {
+ " updaterApp=" + updaterAppManifestValid
+ ", dataApp=" + dataAppManifestValid);
- // There's no point in doing reliability checks if the current packages are bad.
- mIntentHelper.disableReliabilityTriggering();
+ // There's no point in doing any reliability triggers if the current packages are bad.
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
@@ -238,7 +246,8 @@ public class PackageTracker implements IntentHelper.Listener {
Slog.d(TAG,
"triggerUpdateIfNeeded: checkComplete call is not yet overdue."
+ " Not triggering.");
- // Not doing any work, but also not disabling future reliability triggers.
+ // Don't do any work now but we do schedule a future reliability trigger.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
return;
}
} else if (mCheckFailureCount > mFailedCheckRetryCount) {
@@ -247,13 +256,13 @@ public class PackageTracker implements IntentHelper.Listener {
Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures"
+ " exceeded. Stopping reliability triggers until next reboot or package"
+ " update.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
} else if (mCheckFailureCount == 0) {
// Case 4.
Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was"
+ " successful.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
}
@@ -263,7 +272,7 @@ public class PackageTracker implements IntentHelper.Listener {
if (currentInstalledVersions == null) {
// This should not happen if the device is configured in a valid way.
Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
@@ -288,7 +297,7 @@ public class PackageTracker implements IntentHelper.Listener {
// The last check succeeded and nothing has changed. Do nothing and disable
// reliability checks.
Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
}
@@ -299,6 +308,8 @@ public class PackageTracker implements IntentHelper.Listener {
if (checkToken == null) {
Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token."
+ " Not sending check request.");
+ // Trigger again later: perhaps we'll have better luck.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
return;
}
@@ -309,9 +320,9 @@ public class PackageTracker implements IntentHelper.Listener {
// Update the reliability check state in case the update fails.
setCheckInProgress();
- // Enable reliability triggering in case the check doesn't succeed and there is no
- // response at all. Enabling reliability triggering is idempotent.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger in case the update check doesn't succeed and there is no
+ // response at all. It will be cancelled if the check is successful in recordCheckResult.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
}
/**
@@ -370,9 +381,9 @@ public class PackageTracker implements IntentHelper.Listener {
+ " storage state.");
mPackageStatusStorage.resetCheckState();
- // Enable reliability triggering and reset the failure count so we know that the
+ // Schedule a reliability trigger and reset the failure count so we know that the
// next reliability trigger will do something.
- mIntentHelper.enableReliabilityTriggering();
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount = 0;
} else {
// This is the expected case when tracking is enabled: a check was triggered and it has
@@ -385,13 +396,13 @@ public class PackageTracker implements IntentHelper.Listener {
setCheckComplete();
if (success) {
- // Since the check was successful, no more reliability checks are required until
+ // Since the check was successful, no reliability trigger is required until
// there is a package change.
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
mCheckFailureCount = 0;
} else {
- // Enable reliability triggering to potentially check again in future.
- mIntentHelper.enableReliabilityTriggering();
+ // Enable schedule a reliability trigger to check again in future.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount++;
}
} else {
@@ -400,8 +411,8 @@ public class PackageTracker implements IntentHelper.Listener {
Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken
+ " with success=" + success + ". Optimistic lock failure");
- // Enable reliability triggering to potentially try again in future.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger to potentially try again in future.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount++;
}
}
@@ -515,6 +526,7 @@ public class PackageTracker implements IntentHelper.Listener {
", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' +
", mDataAppPackageName='" + mDataAppPackageName + '\'' +
", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis +
+ ", mDelayBeforeReliabilityCheckMillis=" + mDelayBeforeReliabilityCheckMillis +
", mFailedCheckRetryCount=" + mFailedCheckRetryCount +
", mLastTriggerTimestamp=" + mLastTriggerTimestamp +
", mCheckTriggered=" + mCheckTriggered +
diff --git a/com/android/server/timezone/PackageTrackerHelperImpl.java b/com/android/server/timezone/PackageTrackerHelperImpl.java
index 2e0c21bf..b89dd383 100644
--- a/com/android/server/timezone/PackageTrackerHelperImpl.java
+++ b/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -26,6 +26,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Slog;
import java.util.List;
@@ -114,8 +115,8 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa
@Override
public boolean contentProviderRegistered(String authority, String requiredPackageName) {
int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
- ProviderInfo providerInfo =
- mPackageManager.resolveContentProvider(authority, flags);
+ ProviderInfo providerInfo = mPackageManager.resolveContentProviderAsUser(
+ authority, flags, UserHandle.SYSTEM.getIdentifier());
if (providerInfo == null) {
Slog.i(TAG, "contentProviderRegistered: No content provider registered with authority="
+ authority);
@@ -136,7 +137,8 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa
throws PackageManager.NameNotFoundException {
int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
- List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceivers(intent, flags);
+ List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceiversAsUser(
+ intent, flags, UserHandle.SYSTEM);
if (resolveInfo.size() != 1) {
Slog.i(TAG, "receiverRegistered: Zero or multiple broadcast receiver registered for"
+ " intent=" + intent + ", found=" + resolveInfo);
diff --git a/com/android/server/timezone/RulesManagerService.java b/com/android/server/timezone/RulesManagerService.java
index 3ad4419c..52b49baf 100644
--- a/com/android/server/timezone/RulesManagerService.java
+++ b/com/android/server/timezone/RulesManagerService.java
@@ -47,6 +47,7 @@ import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import libcore.icu.ICU;
+import libcore.util.TimeZoneFinder;
import libcore.util.ZoneInfoDB;
import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
@@ -69,18 +70,22 @@ public final class RulesManagerService extends IRulesManager.Stub {
DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
public static class Lifecycle extends SystemService {
- private RulesManagerService mService;
-
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
- mService = RulesManagerService.create(getContext());
- mService.start();
+ RulesManagerService service = RulesManagerService.create(getContext());
+ service.start();
+
+ // Publish the binder service so it can be accessed from other (appropriately
+ // permissioned) processes.
+ publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
- publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, mService);
+ // Publish the service instance locally so we can use it directly from within the system
+ // server from TimeZoneUpdateIdler.
+ publishLocalService(RulesManagerService.class, service);
}
}
@@ -475,9 +480,10 @@ public final class RulesManagerService extends IRulesManager.Stub {
case 'a': {
// Report the active rules version (i.e. the rules in use by the current
// process).
- pw.println("Active rules version (ICU, libcore): "
+ pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
+ ICU.getTZDataVersion() + ","
- + ZoneInfoDB.getInstance().getVersion());
+ + ZoneInfoDB.getInstance().getVersion() + ","
+ + TimeZoneFinder.getInstance().getIanaVersion());
break;
}
default: {
@@ -490,12 +496,24 @@ public final class RulesManagerService extends IRulesManager.Stub {
}
pw.println("RulesManagerService state: " + toString());
- pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
- + ZoneInfoDB.getInstance().getVersion());
+ pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
+ + ICU.getTZDataVersion() + ","
+ + ZoneInfoDB.getInstance().getVersion() + ","
+ + TimeZoneFinder.getInstance().getIanaVersion());
pw.println("Distro state: " + rulesState.toString());
mPackageTracker.dump(pw);
}
+ /**
+ * Called when the device is considered idle.
+ */
+ void notifyIdle() {
+ // No package has changed: we are just triggering because the device is idle and there
+ // *might* be work to do.
+ final boolean packageChanged = false;
+ mPackageTracker.triggerUpdateIfNeeded(packageChanged);
+ }
+
@Override
public String toString() {
return "RulesManagerService{" +
diff --git a/com/android/server/timezone/TimeZoneUpdateIdler.java b/com/android/server/timezone/TimeZoneUpdateIdler.java
new file mode 100644
index 00000000..a7767a4f
--- /dev/null
+++ b/com/android/server/timezone/TimeZoneUpdateIdler.java
@@ -0,0 +1,100 @@
+/*
+ * 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.timezone;
+
+import com.android.server.LocalServices;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * A JobService used to trigger time zone rules update work when a device falls idle.
+ */
+public final class TimeZoneUpdateIdler extends JobService {
+
+ private static final String TAG = "timezone.TimeZoneUpdateIdler";
+
+ /** The static job ID used to handle on-idle work. */
+ // Must be unique within UID (system service)
+ private static final int TIME_ZONE_UPDATE_IDLE_JOB_ID = 27042305;
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ RulesManagerService rulesManagerService =
+ LocalServices.getService(RulesManagerService.class);
+
+ Slog.d(TAG, "onStartJob() called");
+
+ // Note: notifyIdle() explicitly handles canceling / re-scheduling so no need to reschedule
+ // here.
+ rulesManagerService.notifyIdle();
+
+ // Everything is handled synchronously. We are done.
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // Reschedule if stopped unless it was cancelled due to unschedule().
+ boolean reschedule = params.getStopReason() != JobParameters.REASON_CANCELED;
+ Slog.d(TAG, "onStopJob() called: Reschedule=" + reschedule);
+ return reschedule;
+ }
+
+ /**
+ * Schedules the TimeZoneUpdateIdler job service to run once.
+ *
+ * @param context Context to use to get a job scheduler.
+ */
+ public static void schedule(Context context, long minimumDelayMillis) {
+ // Request that the JobScheduler tell us when the device falls idle.
+ JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ // The TimeZoneUpdateIdler will send an intent that will trigger the Receiver.
+ ComponentName idlerJobServiceName =
+ new ComponentName(context, TimeZoneUpdateIdler.class);
+
+ // We require the device is idle, but also that it is charging to be as non-invasive as
+ // we can.
+ JobInfo.Builder jobInfoBuilder =
+ new JobInfo.Builder(TIME_ZONE_UPDATE_IDLE_JOB_ID, idlerJobServiceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setMinimumLatency(minimumDelayMillis);
+
+ Slog.d(TAG, "schedule() called: minimumDelayMillis=" + minimumDelayMillis);
+ jobScheduler.schedule(jobInfoBuilder.build());
+ }
+
+ /**
+ * Unschedules the TimeZoneUpdateIdler job service.
+ *
+ * @param context Context to use to get a job scheduler.
+ */
+ public static void unschedule(Context context) {
+ JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ Slog.d(TAG, "unschedule() called");
+ jobScheduler.cancel(TIME_ZONE_UPDATE_IDLE_JOB_ID);
+ }
+}
diff --git a/com/android/server/twilight/TwilightState.java b/com/android/server/twilight/TwilightState.java
index 30a8cccb..71304a7a 100644
--- a/com/android/server/twilight/TwilightState.java
+++ b/com/android/server/twilight/TwilightState.java
@@ -18,7 +18,10 @@ package com.android.server.twilight;
import android.text.format.DateFormat;
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.TimeZone;
/**
* The twilight state, consisting of the sunrise and sunset times (in millis) for the current
@@ -45,12 +48,11 @@ public final class TwilightState {
}
/**
- * Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}.
+ * Returns a new {@link LocalDateTime} instance initialized to {@link #sunriseTimeMillis()}.
*/
- public Calendar sunrise() {
- final Calendar sunrise = Calendar.getInstance();
- sunrise.setTimeInMillis(mSunriseTimeMillis);
- return sunrise;
+ public LocalDateTime sunrise() {
+ final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunriseTimeMillis), zoneId);
}
/**
@@ -62,12 +64,11 @@ public final class TwilightState {
}
/**
- * Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}.
+ * Returns a new {@link LocalDateTime} instance initialized to {@link #sunsetTimeMillis()}.
*/
- public Calendar sunset() {
- final Calendar sunset = Calendar.getInstance();
- sunset.setTimeInMillis(mSunsetTimeMillis);
- return sunset;
+ public LocalDateTime sunset() {
+ final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunsetTimeMillis), zoneId);
}
/**
diff --git a/com/android/server/usage/AppStandbyController.java b/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 00000000..b2446ba7
--- /dev/null
+++ b/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,987 @@
+/**
+ * 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.usage;
+
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ */
+public class AppStandbyController {
+
+ private static final String TAG = "AppStandbyController";
+ private static final boolean DEBUG = false;
+
+ static final boolean COMPRESS_TIME = false;
+ private static final long ONE_MINUTE = 60 * 1000;
+
+ // To name the lock for stack traces
+ static class Lock {}
+
+ /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+ private final Object mAppIdleLock = new Lock();
+
+ /** Keeps the history and state for each app. */
+ @GuardedBy("mAppIdleLock")
+ private AppIdleHistory mAppIdleHistory;
+
+ @GuardedBy("mAppIdleLock")
+ private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+ mPackageAccessListeners = new ArrayList<>();
+
+ /** Whether we've queried the list of carrier privileged apps. */
+ @GuardedBy("mAppIdleLock")
+ private boolean mHaveCarrierPrivilegedApps;
+
+ /** List of carrier-privileged apps that should be excluded from standby */
+ @GuardedBy("mAppIdleLock")
+ private List<String> mCarrierPrivilegedApps;
+
+ // Messages for the handler
+ static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_FORCE_IDLE_STATE = 4;
+ static final int MSG_CHECK_IDLE_STATES = 5;
+ static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
+ static final int MSG_PAROLE_END_TIMEOUT = 7;
+ static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_PAROLE_STATE_CHANGED = 9;
+ static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+
+ long mAppIdleScreenThresholdMillis;
+ long mCheckIdleIntervalMillis;
+ long mAppIdleWallclockThresholdMillis;
+ long mAppIdleParoleIntervalMillis;
+ long mAppIdleParoleDurationMillis;
+ boolean mAppIdleEnabled;
+ boolean mAppIdleTempParoled;
+ boolean mCharging;
+ private long mLastAppIdleParoledTime;
+ private boolean mSystemServicesReady = false;
+
+ private volatile boolean mPendingOneTimeCheckIdleStates;
+
+ private final Handler mHandler;
+ private final Context mContext;
+
+ private DisplayManager mDisplayManager;
+ private IDeviceIdleController mDeviceIdleController;
+ private AppWidgetManager mAppWidgetManager;
+ private IBatteryStats mBatteryStats;
+ private PowerManager mPowerManager;
+ private PackageManager mPackageManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
+ AppStandbyController(Context context, Looper looper) {
+ mContext = context;
+ mHandler = new AppStandbyHandler(looper);
+ mPackageManager = mContext.getPackageManager();
+ mAppIdleEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableAutoPowerModes);
+ if (mAppIdleEnabled) {
+ IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
+ }
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+ }
+
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+
+ mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+ null, mHandler);
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // Observe changes to the threshold
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.registerObserver();
+ settingsObserver.updateSettings();
+
+ mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mDisplayManager = (DisplayManager) mContext.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+ }
+
+ if (mPendingOneTimeCheckIdleStates) {
+ postOneTimeCheckIdleStates();
+ }
+
+ mSystemServicesReady = true;
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+ }
+ }
+
+ void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ // Get sync adapters for the authority
+ String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+ authority, userId);
+ for (String packageName: packages) {
+ // Only force the sync adapters to active if the provider is not in the same package and
+ // the sync adapter is a system package.
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+ if (pi == null || pi.applicationInfo == null) {
+ continue;
+ }
+ if (!packageName.equals(providerPkgName)) {
+ setAppIdleAsync(packageName, false, userId);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+
+ void setChargingState(boolean charging) {
+ synchronized (mAppIdleLock) {
+ if (mCharging != charging) {
+ mCharging = charging;
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ /** Paroled here means temporary pardon from being inactive */
+ void setAppIdleParoled(boolean paroled) {
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (mAppIdleTempParoled != paroled) {
+ mAppIdleTempParoled = paroled;
+ if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
+ if (paroled) {
+ postParoleEndTimeout();
+ } else {
+ mLastAppIdleParoledTime = now;
+ postNextParoleTimeout(now);
+ }
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ boolean isParoledOrCharging() {
+ synchronized (mAppIdleLock) {
+ return mAppIdleTempParoled || mCharging;
+ }
+ }
+
+ private void postNextParoleTimeout(long now) {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
+ mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
+ // Compute when the next parole needs to happen. We check more frequently than necessary
+ // since the message handler delays are based on elapsedRealTime and not wallclock time.
+ // The comparison is done in wallclock time.
+ long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+ if (timeLeft < 0) {
+ timeLeft = 0;
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
+ }
+
+ private void postParoleEndTimeout() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
+ mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
+ }
+
+ private void postParoleStateChanged() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+ mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+ mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+ }
+
+ void postCheckIdleStates(int userId) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ }
+
+ /**
+ * We send a different message to check idle states once, otherwise we would end up
+ * scheduling a series of repeating checkIdleStates each time we fired off one.
+ */
+ void postOneTimeCheckIdleStates() {
+ if (mDeviceIdleController == null) {
+ // Not booted yet; wait for it!
+ mPendingOneTimeCheckIdleStates = true;
+ } else {
+ mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ mPendingOneTimeCheckIdleStates = false;
+ }
+ }
+
+ /**
+ * Check all running users' or specified user's apps to see if they enter an idle state.
+ * @return Returns whether checking should continue periodically.
+ */
+ boolean checkIdleStates(int checkUserId) {
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+
+ final int[] runningUserIds;
+ try {
+ runningUserIds = ActivityManager.getService().getRunningUserIds();
+ if (checkUserId != UserHandle.USER_ALL
+ && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ final int userId = runningUserIds[i];
+ if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Checking idle state for user " + userId);
+ }
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ for (int p = 0; p < packageCount; p++) {
+ final PackageInfo pi = packages.get(p);
+ final String packageName = pi.packageName;
+ final boolean isIdle = isAppIdleFiltered(packageName,
+ UserHandle.getAppId(pi.applicationInfo.uid),
+ userId, elapsedRealtime);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+ userId, isIdle ? 1 : 0, packageName));
+ if (isIdle) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "checkIdleStates took "
+ + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ return true;
+ }
+
+ /** Check if it's been a while since last parole and let idle apps do some work */
+ void checkParoleTimeout() {
+ boolean setParoled = false;
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (!mAppIdleTempParoled) {
+ final long timeSinceLastParole = now - mLastAppIdleParoledTime;
+ if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
+ if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
+ setParoled = true;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
+ postNextParoleTimeout(now);
+ }
+ }
+ }
+ if (setParoled) {
+ setAppIdleParoled(true);
+ }
+ }
+
+ private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (idle) {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+ packageName, uid);
+ } else {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+ packageName, uid);
+ }
+ } catch (PackageManager.NameNotFoundException | RemoteException e) {
+ }
+ }
+
+ void onDeviceIdleModeChanged() {
+ final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
+ if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
+ boolean paroled = false;
+ synchronized (mAppIdleLock) {
+ final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+ if (!deviceIdle
+ && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
+ if (DEBUG) {
+ Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ }
+ paroled = true;
+ } else if (deviceIdle) {
+ if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
+ paroled = false;
+ } else {
+ return;
+ }
+ }
+ setAppIdleParoled(paroled);
+ }
+
+ void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ synchronized (mAppIdleLock) {
+ // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+ // about apps that are on some kind of whitelist anyway.
+ final boolean previouslyIdle = mAppIdleHistory.isIdle(
+ event.mPackage, userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
+ || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
+ || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
+ mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ if (previouslyIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ 0, event.mPackage));
+ notifyBatteryStats(event.mPackage, userId, false);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+ * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+ * the threshold for idle.
+ *
+ * This method is always called from the handler thread, so not much synchronization is
+ * required.
+ */
+ void forceIdleState(String packageName, int userId, boolean idle) {
+ final int appId = getAppId(packageName);
+ if (appId < 0) return;
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+ }
+ final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if (previouslyIdle != stillIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ stillIdle ? 1 : 0, packageName));
+ if (!stillIdle) {
+ notifyBatteryStats(packageName, userId, idle);
+ }
+ }
+ }
+
+ public void onUserRemoved(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.onUserRemoved(userId);
+ }
+ }
+
+ private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ if (!mPackageAccessListeners.contains(listener)) {
+ mPackageAccessListeners.add(listener);
+ }
+ }
+ }
+
+ void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ mPackageAccessListeners.remove(listener);
+ }
+ }
+
+ int getAppId(String packageName) {
+ try {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ return ai.uid;
+ } catch (PackageManager.NameNotFoundException re) {
+ return -1;
+ }
+ }
+
+ boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps) {
+ if (isParoledOrCharging()) {
+ return false;
+ }
+ if (shouldObfuscateInstantApps &&
+ mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+ return false;
+ }
+ return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+ }
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or temporarily allowed to make exceptions.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime) {
+ if (packageName == null) return false;
+ // If not enabled at all, of course nobody is ever idle.
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+ if (appId < Process.FIRST_APPLICATION_UID) {
+ // System uids never go idle.
+ return false;
+ }
+ if (packageName.equals("android")) {
+ // Nor does the framework (which should be redundant with the above, but for MR1 we will
+ // retain this for safety).
+ return false;
+ }
+ if (mSystemServicesReady) {
+ try {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ if (isActiveDeviceAdmin(packageName, userId)) {
+ return false;
+ }
+
+ if (isActiveNetworkScorer(packageName)) {
+ return false;
+ }
+
+ if (mAppWidgetManager != null
+ && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+ return false;
+ }
+
+ if (isDeviceProvisioningPackage(packageName)) {
+ return false;
+ }
+ }
+
+ if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
+ return false;
+ }
+
+ // Check this last, as it is the most expensive check
+ // TODO: Optimize this by fetching the carrier privileged apps ahead of time
+ if (isCarrierApp(packageName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ int[] getIdleUidsForUser(int userId) {
+ if (!mAppIdleEnabled) {
+ return new int[0];
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ List<ApplicationInfo> apps;
+ try {
+ ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+ .getInstalledApplications(/* flags= */ 0, userId);
+ if (slice == null) {
+ return new int[0];
+ }
+ apps = slice.getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
+ // associated with that uid, upper 16 bits is the number of those apps that is idle.
+ SparseIntArray uidStates = new SparseIntArray();
+
+ // Now resolve all app state. Iterating over all apps, keeping track of how many
+ // we find for each uid and how many of those are idle.
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = apps.get(i);
+
+ // Check whether this app is idle.
+ boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+ userId, elapsedRealtime);
+
+ int index = uidStates.indexOfKey(ai.uid);
+ if (index < 0) {
+ uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ } else {
+ int value = uidStates.valueAt(index);
+ uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ int numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ numIdle++;
+ }
+ }
+
+ int[] res = new int[numIdle];
+ numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ res[numIdle] = uidStates.keyAt(i);
+ numIdle++;
+ }
+ }
+
+ return res;
+ }
+
+ void setAppIdleAsync(String packageName, boolean idle, int userId) {
+ if (packageName == null) return;
+
+ mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ private boolean isActiveDeviceAdmin(String packageName, int userId) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ if (dpm == null) return false;
+ return dpm.packageHasActiveAdmins(packageName, userId);
+ }
+
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ private boolean isDeviceProvisioningPackage(String packageName) {
+ String deviceProvisioningPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
+
+ private boolean isCarrierApp(String packageName) {
+ synchronized (mAppIdleLock) {
+ if (!mHaveCarrierPrivilegedApps) {
+ fetchCarrierPrivilegedAppsLA();
+ }
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
+ }
+ }
+
+ void clearCarrierPrivilegedApps() {
+ if (DEBUG) {
+ Slog.i(TAG, "Clearing carrier privileged apps list");
+ }
+ synchronized (mAppIdleLock) {
+ mHaveCarrierPrivilegedApps = false;
+ mCarrierPrivilegedApps = null; // Need to be refetched.
+ }
+ }
+
+ @GuardedBy("mAppIdleLock")
+ private void fetchCarrierPrivilegedAppsLA() {
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mHaveCarrierPrivilegedApps = true;
+ if (DEBUG) {
+ Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+ }
+ }
+
+ private boolean isActiveNetworkScorer(String packageName) {
+ NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+ Context.NETWORK_SCORE_SERVICE);
+ return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+ }
+
+ void informListeners(String packageName, int userId, boolean isIdle) {
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ }
+ }
+
+ void informParoleStateChanged() {
+ final boolean paroled = isParoledOrCharging();
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
+ }
+
+ void flushToDisk(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes(userId);
+ }
+ }
+
+ void flushDurationsToDisk() {
+ // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+ // considered not-idle, which is the safest outcome in such an event.
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleDurations();
+ }
+ }
+
+ boolean isDisplayOn() {
+ return mDisplayManager
+ .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ }
+
+ void clearAppIdleForPackage(String packageName, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.clearUsage(packageName, userId);
+ }
+ }
+
+ private class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ clearCarrierPrivilegedApps();
+ }
+ if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
+ }
+ }
+ }
+
+ void initializeDefaultsForSystemApps(int userId) {
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ synchronized (mAppIdleLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+ mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+
+ void postReportContentProviderUsage(String name, String packageName, int userId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = packageName;
+ args.arg3 = userId;
+ mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+ .sendToTarget();
+ }
+
+ void dumpHistory(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dumpHistory(idpw, userId);
+ }
+ }
+
+ void dumpUser(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dump(idpw, userId);
+ }
+ }
+
+ void dumpState(String[] args, PrintWriter pw) {
+ synchronized (mAppIdleLock) {
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
+ }
+
+ pw.println();
+ pw.println("Settings:");
+
+ pw.print(" mAppIdleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleWallclockThresholdMillis=");
+ TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mCheckIdleIntervalMillis=");
+ TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleIntervalMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+ pw.println();
+
+ pw.println();
+ pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+ pw.print(" mCharging="); pw.print(mCharging);
+ pw.print(" mLastAppIdleParoledTime=");
+ TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
+ pw.println();
+ }
+
+ class AppStandbyHandler extends Handler {
+
+ AppStandbyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FORCE_IDLE_STATE:
+ forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
+ case MSG_CHECK_IDLE_STATES:
+ if (checkIdleStates(msg.arg1)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+ mCheckIdleIntervalMillis);
+ }
+ break;
+
+ case MSG_ONE_TIME_CHECK_IDLE_STATES:
+ mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ checkIdleStates(UserHandle.USER_ALL);
+ break;
+
+ case MSG_CHECK_PAROLE_TIMEOUT:
+ checkParoleTimeout();
+ break;
+
+ case MSG_PAROLE_END_TIMEOUT:
+ if (DEBUG) Slog.d(TAG, "Ending parole");
+ setAppIdleParoled(false);
+ break;
+
+ case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ reportContentProviderUsage((String) args.arg1, // authority name
+ (String) args.arg2, // package name
+ (int) args.arg3); // userId
+ args.recycle();
+ break;
+
+ case MSG_PAROLE_STATE_CHANGED:
+ if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+ + ", Charging state:" + mCharging);
+ informParoleStateChanged();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+
+ }
+ }
+ };
+
+ private class DeviceStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ setChargingState(intent.getIntExtra("plugged", 0) != 0);
+ } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ onDeviceIdleModeChanged();
+ }
+ }
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener
+ = new DisplayManager.DisplayListener() {
+
+ @Override public void onDisplayAdded(int displayId) {
+ }
+
+ @Override public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ final boolean displayOn = isDisplayOn();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+ }
+ }
+ }
+ };
+
+ /**
+ * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+ */
+ private class SettingsObserver extends ContentObserver {
+ /**
+ * This flag has been used to disable app idle on older builds with bug b/26355386.
+ */
+ @Deprecated
+ private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+ private static final String KEY_IDLE_DURATION = "idle_duration2";
+ private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+ private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+ private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void registerObserver() {
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_IDLE_CONSTANTS), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ postOneTimeCheckIdleStates();
+ }
+
+ void updateSettings() {
+ synchronized (mAppIdleLock) {
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
+ // Default: 12 hours of screen-on time sans dream-time
+ mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+ mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+ COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+ mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+ // Default: 24 hours between paroles
+ mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+ COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+ mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+ mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+ mAppIdleScreenThresholdMillis);
+ }
+ }
+ }
+
+}
+
diff --git a/com/android/server/usage/UsageStatsService.java b/com/android/server/usage/UsageStatsService.java
index 25e471cb..afafea19 100644
--- a/com/android/server/usage/UsageStatsService.java
+++ b/com/android/server/usage/UsageStatsService.java
@@ -18,37 +18,24 @@ package com.android.server.usage;
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUidObserver;
-import android.app.admin.DevicePolicyManager;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-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.UserInfo;
import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -56,7 +43,6 @@ import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -64,21 +50,12 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
import android.util.ArraySet;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -88,7 +65,6 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -107,7 +83,6 @@ public class UsageStatsService extends SystemService implements
static final boolean COMPRESS_TIME = false;
private static final long TEN_SECONDS = 10 * 1000;
- private static final long ONE_MINUTE = 60 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
@@ -115,24 +90,10 @@ public class UsageStatsService extends SystemService implements
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
- long mAppIdleScreenThresholdMillis;
- long mCheckIdleIntervalMillis;
- long mAppIdleWallclockThresholdMillis;
- long mAppIdleParoleIntervalMillis;
- long mAppIdleParoleDurationMillis;
-
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
- static final int MSG_INFORM_LISTENERS = 3;
- static final int MSG_FORCE_IDLE_STATE = 4;
- static final int MSG_CHECK_IDLE_STATES = 5;
- static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
- static final int MSG_PAROLE_END_TIMEOUT = 7;
- static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
- static final int MSG_PAROLE_STATE_CHANGED = 9;
- static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
private final Object mLock = new Object();
Handler mHandler;
@@ -140,11 +101,7 @@ public class UsageStatsService extends SystemService implements
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
- AppWidgetManager mAppWidgetManager;
IDeviceIdleController mDeviceIdleController;
- private DisplayManager mDisplayManager;
- private PowerManager mPowerManager;
- private IBatteryStats mBatteryStats;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
@@ -152,26 +109,7 @@ public class UsageStatsService extends SystemService implements
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
- boolean mAppIdleEnabled;
- boolean mAppIdleTempParoled;
- boolean mCharging;
- private long mLastAppIdleParoledTime;
-
- private volatile boolean mPendingOneTimeCheckIdleStates;
- private boolean mSystemServicesReady = false;
-
- private final Object mAppIdleLock = new Object();
- @GuardedBy("mAppIdleLock")
- private AppIdleHistory mAppIdleHistory;
-
- @GuardedBy("mAppIdleLock")
- private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
-
- @GuardedBy("mAppIdleLock")
- private boolean mHaveCarrierPrivilegedApps;
- @GuardedBy("mAppIdleLock")
- private List<String> mCarrierPrivilegedApps;
+ AppStandbyController mAppStandby;
public UsageStatsService(Context context) {
super(context);
@@ -185,6 +123,8 @@ public class UsageStatsService extends SystemService implements
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mHandler = new H(BackgroundThread.get().getLooper());
+ mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+
File systemDataDir = new File(Environment.getDataDirectory(), "system");
mUsageStatsDir = new File(systemDataDir, "usagestats");
mUsageStatsDir.mkdirs();
@@ -198,30 +138,9 @@ public class UsageStatsService extends SystemService implements
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
null, mHandler);
- IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
-
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
- null, mHandler);
-
- mAppIdleEnabled = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_enableAutoPowerModes);
- if (mAppIdleEnabled) {
- IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
- deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
- }
-
synchronized (mLock) {
cleanUpRemovedUsersLocked();
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
- }
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
@@ -233,28 +152,10 @@ public class UsageStatsService extends SystemService implements
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- // Observe changes to the threshold
- SettingsObserver settingsObserver = new SettingsObserver(mHandler);
- settingsObserver.registerObserver();
- settingsObserver.updateSettings();
+ mAppStandby.onBootPhase(phase);
- mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mBatteryStats = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mDisplayManager = (DisplayManager) getContext().getSystemService(
- Context.DISPLAY_SERVICE);
- mPowerManager = getContext().getSystemService(PowerManager.class);
-
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
- }
-
- if (mPendingOneTimeCheckIdleStates) {
- postOneTimeCheckIdleStates();
- }
if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
try {
@@ -268,18 +169,9 @@ public class UsageStatsService extends SystemService implements
} else {
Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
}
-
- mSystemServicesReady = true;
- } else if (phase == PHASE_BOOT_COMPLETED) {
- setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
}
}
- private boolean isDisplayOn() {
- return mDisplayManager
- .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
- }
-
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -291,60 +183,12 @@ public class UsageStatsService extends SystemService implements
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
if (userId >=0) {
- postCheckIdleStates(userId);
+ mAppStandby.postCheckIdleStates(userId);
}
}
}
}
- private class PackageReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- clearCarrierPrivilegedApps();
- }
- if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
- getSendingUserId());
- }
- }
- }
-
- private class DeviceStateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- setChargingState(intent.getIntExtra("plugged", 0) != 0);
- } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
- onDeviceIdleModeChanged();
- }
- }
- }
-
- private final DisplayManager.DisplayListener mDisplayListener
- = new DisplayManager.DisplayListener() {
-
- @Override public void onDisplayAdded(int displayId) {
- }
-
- @Override public void onDisplayRemoved(int displayId) {
- }
-
- @Override public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- final boolean displayOn = isDisplayOn();
- synchronized (UsageStatsService.this.mAppIdleLock) {
- mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
- }
- }
- }
- };
-
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -388,42 +232,18 @@ public class UsageStatsService extends SystemService implements
@Override
public void onStatsReloaded() {
- postOneTimeCheckIdleStates();
+ mAppStandby.postOneTimeCheckIdleStates();
}
@Override
public void onNewUpdate(int userId) {
- initializeDefaultsForSystemApps(userId);
- }
-
- private void initializeDefaultsForSystemApps(int userId) {
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- synchronized (mAppIdleLock) {
- for (int i = 0; i < packageCount; i++) {
- final PackageInfo pi = packages.get(i);
- String packageName = pi.packageName;
- if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
- }
- }
- }
+ mAppStandby.initializeDefaultsForSystemApps(userId);
}
private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) {
return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId);
}
- void clearAppIdleForPackage(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.clearUsage(packageName, userId);
- }
- }
-
private void cleanUpRemovedUsersLocked() {
final List<UserInfo> users = mUserManager.getUsers(true);
if (users == null || users.size() == 0) {
@@ -451,195 +271,6 @@ public class UsageStatsService extends SystemService implements
}
}
- void setChargingState(boolean charging) {
- synchronized (mAppIdleLock) {
- if (mCharging != charging) {
- mCharging = charging;
- postParoleStateChanged();
- }
- }
- }
-
- /** Paroled here means temporary pardon from being inactive */
- void setAppIdleParoled(boolean paroled) {
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (mAppIdleTempParoled != paroled) {
- mAppIdleTempParoled = paroled;
- if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
- if (paroled) {
- postParoleEndTimeout();
- } else {
- mLastAppIdleParoledTime = now;
- postNextParoleTimeout(now);
- }
- postParoleStateChanged();
- }
- }
- }
-
- boolean isParoledOrCharging() {
- synchronized (mAppIdleLock) {
- return mAppIdleTempParoled || mCharging;
- }
- }
-
- private void postNextParoleTimeout(long now) {
- if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
- mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
- // Compute when the next parole needs to happen. We check more frequently than necessary
- // since the message handler delays are based on elapsedRealTime and not wallclock time.
- // The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
- if (timeLeft < 0) {
- timeLeft = 0;
- }
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
- }
-
- private void postParoleEndTimeout() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
- mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
- }
-
- private void postParoleStateChanged() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
- mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
- mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
- }
-
- void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
- }
-
- /**
- * We send a different message to check idle states once, otherwise we would end up
- * scheduling a series of repeating checkIdleStates each time we fired off one.
- */
- void postOneTimeCheckIdleStates() {
- if (mDeviceIdleController == null) {
- // Not booted yet; wait for it!
- mPendingOneTimeCheckIdleStates = true;
- } else {
- mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
- mPendingOneTimeCheckIdleStates = false;
- }
- }
-
- /**
- * Check all running users' or specified user's apps to see if they enter an idle state.
- * @return Returns whether checking should continue periodically.
- */
- boolean checkIdleStates(int checkUserId) {
- if (!mAppIdleEnabled) {
- return false;
- }
-
- final int[] runningUserIds;
- try {
- runningUserIds = ActivityManager.getService().getRunningUserIds();
- if (checkUserId != UserHandle.USER_ALL
- && !ArrayUtils.contains(runningUserIds, checkUserId)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- for (int i = 0; i < runningUserIds.length; i++) {
- final int userId = runningUserIds[i];
- if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
- continue;
- }
- if (DEBUG) {
- Slog.d(TAG, "Checking idle state for user " + userId);
- }
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- for (int p = 0; p < packageCount; p++) {
- final PackageInfo pi = packages.get(p);
- final String packageName = pi.packageName;
- final boolean isIdle = isAppIdleFiltered(packageName,
- UserHandle.getAppId(pi.applicationInfo.uid),
- userId, elapsedRealtime);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
- if (isIdle) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
- }
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "checkIdleStates took "
- + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- return true;
- }
-
- /** Check if it's been a while since last parole and let idle apps do some work */
- void checkParoleTimeout() {
- boolean setParoled = false;
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (!mAppIdleTempParoled) {
- final long timeSinceLastParole = now - mLastAppIdleParoledTime;
- if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
- if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- setParoled = true;
- } else {
- if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout(now);
- }
- }
- }
- if (setParoled) {
- setAppIdleParoled(true);
- }
- }
-
- private void notifyBatteryStats(String packageName, int userId, boolean idle) {
- try {
- final int uid = mPackageManager.getPackageUidAsUser(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (idle) {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
- packageName, uid);
- } else {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
- packageName, uid);
- }
- } catch (NameNotFoundException | RemoteException e) {
- }
- }
-
- void onDeviceIdleModeChanged() {
- final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
- if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
- boolean paroled = false;
- synchronized (mAppIdleLock) {
- final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
- if (!deviceIdle
- && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
- if (DEBUG) {
- Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
- }
- paroled = true;
- } else if (deviceIdle) {
- if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
- paroled = false;
- } else {
- return;
- }
- }
- setAppIdleParoled(paroled);
- }
-
private static void deleteRecursively(File f) {
File[] files = f.listFiles();
if (files != null) {
@@ -724,76 +355,7 @@ public class UsageStatsService extends SystemService implements
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
service.reportEvent(event);
- synchronized (mAppIdleLock) {
- // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
- // about apps that are on some kind of whitelist anyway.
- final boolean previouslyIdle = mAppIdleHistory.isIdle(
- event.mPackage, userId, elapsedRealtime);
- // Inform listeners if necessary
- if ((event.mEventType == Event.MOVE_TO_FOREGROUND
- || event.mEventType == Event.MOVE_TO_BACKGROUND
- || event.mEventType == Event.SYSTEM_INTERACTION
- || event.mEventType == Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
- if (previouslyIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ 0, event.mPackage));
- notifyBatteryStats(event.mPackage, userId, false);
- }
- }
- }
- }
- }
-
- void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
- // Get sync adapters for the authority
- String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
- authority, userId);
- for (String packageName: packages) {
- // Only force the sync adapters to active if the provider is not in the same package and
- // the sync adapter is a system package.
- try {
- PackageInfo pi = mPackageManager.getPackageInfoAsUser(
- packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
- if (pi == null || pi.applicationInfo == null) {
- continue;
- }
- if (!packageName.equals(providerPkgName)) {
- setAppIdleAsync(packageName, false, userId);
- }
- } catch (NameNotFoundException e) {
- // Shouldn't happen
- }
- }
- }
-
- /**
- * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
- * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
- * the threshold for idle.
- *
- * This method is always called from the handler thread, so not much synchronization is
- * required.
- */
- void forceIdleState(String packageName, int userId, boolean idle) {
- final int appId = getAppId(packageName);
- if (appId < 0) return;
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
- }
- final boolean stillIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- // Inform listeners if necessary
- if (previouslyIdle != stillIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ stillIdle ? 1 : 0, packageName));
- if (!stillIdle) {
- notifyBatteryStats(packageName, userId, idle);
- }
+ mAppStandby.reportEvent(event, elapsedRealtime, userId);
}
}
@@ -813,9 +375,7 @@ public class UsageStatsService extends SystemService implements
synchronized (mLock) {
Slog.i(TAG, "Removing user " + userId + " and all data.");
mUserState.remove(userId);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.onUserRemoved(userId);
- }
+ mAppStandby.onUserRemoved(userId);
cleanUpRemovedUsersLocked();
}
}
@@ -887,253 +447,6 @@ public class UsageStatsService extends SystemService implements
}
}
- private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
- }
- }
-
- void addListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- if (!mPackageAccessListeners.contains(listener)) {
- mPackageAccessListeners.add(listener);
- }
- }
- }
-
- void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- mPackageAccessListeners.remove(listener);
- }
- }
-
- int getAppId(String packageName) {
- try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- return ai.uid;
- } catch (NameNotFoundException re) {
- return -1;
- }
- }
-
- boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
- boolean shouldObfuscateInstantApps) {
- if (isParoledOrCharging()) {
- return false;
- }
- if (shouldObfuscateInstantApps &&
- mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
- return false;
- }
- return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
- }
-
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- private boolean isAppIdleFiltered(String packageName, int appId, int userId,
- long elapsedRealtime) {
- if (packageName == null) return false;
- // If not enabled at all, of course nobody is ever idle.
- if (!mAppIdleEnabled) {
- return false;
- }
- if (appId < Process.FIRST_APPLICATION_UID) {
- // System uids never go idle.
- return false;
- }
- if (packageName.equals("android")) {
- // Nor does the framework (which should be redundant with the above, but for MR1 we will
- // retain this for safety).
- return false;
- }
- if (mSystemServicesReady) {
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- if (isActiveDeviceAdmin(packageName, userId)) {
- return false;
- }
-
- if (isActiveNetworkScorer(packageName)) {
- return false;
- }
-
- if (mAppWidgetManager != null
- && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
- return false;
- }
-
- if (isDeviceProvisioningPackage(packageName)) {
- return false;
- }
- }
-
- if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
- return false;
- }
-
- // Check this last, as it is the most expensive check
- // TODO: Optimize this by fetching the carrier privileged apps ahead of time
- if (isCarrierApp(packageName)) {
- return false;
- }
-
- return true;
- }
-
- int[] getIdleUidsForUser(int userId) {
- if (!mAppIdleEnabled) {
- return new int[0];
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- List<ApplicationInfo> apps;
- try {
- ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
- .getInstalledApplications(/* flags= */ 0, userId);
- if (slice == null) {
- return new int[0];
- }
- apps = slice.getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
- // associated with that uid, upper 16 bits is the number of those apps that is idle.
- SparseIntArray uidStates = new SparseIntArray();
-
- // Now resolve all app state. Iterating over all apps, keeping track of how many
- // we find for each uid and how many of those are idle.
- for (int i = apps.size() - 1; i >= 0; i--) {
- ApplicationInfo ai = apps.get(i);
-
- // Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
- userId, elapsedRealtime);
-
- int index = uidStates.indexOfKey(ai.uid);
- if (index < 0) {
- uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
- } else {
- int value = uidStates.valueAt(index);
- uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- int numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- numIdle++;
- }
- }
-
- int[] res = new int[numIdle];
- numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- res[numIdle] = uidStates.keyAt(i);
- numIdle++;
- }
- }
-
- return res;
- }
-
- void setAppIdleAsync(String packageName, boolean idle, int userId) {
- if (packageName == null) return;
-
- mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
- .sendToTarget();
- }
-
- private boolean isActiveDeviceAdmin(String packageName, int userId) {
- DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
- if (dpm == null) return false;
- return dpm.packageHasActiveAdmins(packageName, userId);
- }
-
- /**
- * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
- * returns {@code false}.
- */
- private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = getContext().getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
- }
-
- private boolean isCarrierApp(String packageName) {
- synchronized (mAppIdleLock) {
- if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLA();
- }
- if (mCarrierPrivilegedApps != null) {
- return mCarrierPrivilegedApps.contains(packageName);
- }
- return false;
- }
- }
-
- void clearCarrierPrivilegedApps() {
- if (DEBUG) {
- Slog.i(TAG, "Clearing carrier privileged apps list");
- }
- synchronized (mAppIdleLock) {
- mHaveCarrierPrivilegedApps = false;
- mCarrierPrivilegedApps = null; // Need to be refetched.
- }
- }
-
- @GuardedBy("mAppIdleLock")
- private void fetchCarrierPrivilegedAppsLA() {
- TelephonyManager telephonyManager =
- getContext().getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
- mHaveCarrierPrivilegedApps = true;
- if (DEBUG) {
- Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
- }
- }
-
- private boolean isActiveNetworkScorer(String packageName) {
- NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService(
- Context.NETWORK_SCORE_SERVICE);
- return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
- }
-
- void informListeners(String packageName, int userId, boolean isIdle) {
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, isIdle);
- }
- }
-
- void informParoleStateChanged() {
- final boolean paroled = isParoledOrCharging();
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onParoleStateChanged(paroled);
- }
- }
-
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
@@ -1143,15 +456,10 @@ public class UsageStatsService extends SystemService implements
for (int i = 0; i < userCount; i++) {
UserUsageStatsService service = mUserState.valueAt(i);
service.persistActiveStats();
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
- }
- }
- // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
- // considered not-idle, which is the safest outcome in such an event.
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleDurations();
+ mAppStandby.flushToDisk(mUserState.keyAt(i));
}
+ mAppStandby.flushDurationsToDisk();
+
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
@@ -1166,7 +474,8 @@ public class UsageStatsService extends SystemService implements
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
- idpw.printPair("user", mUserState.keyAt(i));
+ int userId = mUserState.keyAt(i);
+ idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
if (argSet.contains("--checkin")) {
@@ -1176,57 +485,19 @@ public class UsageStatsService extends SystemService implements
idpw.println();
if (args.length > 0) {
if ("history".equals(args[0])) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpHistory(idpw, userId);
} else if ("flush".equals(args[0])) {
- UsageStatsService.this.flushToDiskLocked();
+ flushToDiskLocked();
pw.println("Flushed stats to disk");
}
}
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpUser(idpw, userId);
idpw.decreaseIndent();
}
pw.println();
- synchronized (mAppIdleLock) {
- pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
- + "): " + mCarrierPrivilegedApps);
- }
-
- pw.println();
- pw.println("Settings:");
-
- pw.print(" mAppIdleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleWallclockThresholdMillis=");
- TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
- pw.println();
-
- pw.print(" mCheckIdleIntervalMillis=");
- TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleIntervalMillis=");
- TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
- pw.println();
-
- pw.println();
- pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
- pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
- pw.print(" mCharging="); pw.print(mCharging);
- pw.print(" mLastAppIdleParoledTime=");
- TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
- pw.println();
+ mAppStandby.dumpState(args, pw);
}
}
@@ -1250,50 +521,6 @@ public class UsageStatsService extends SystemService implements
onUserRemoved(msg.arg1);
break;
- case MSG_INFORM_LISTENERS:
- informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_FORCE_IDLE_STATE:
- forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
- }
- break;
-
- case MSG_ONE_TIME_CHECK_IDLE_STATES:
- mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
- checkIdleStates(UserHandle.USER_ALL);
- break;
-
- case MSG_CHECK_PAROLE_TIMEOUT:
- checkParoleTimeout();
- break;
-
- case MSG_PAROLE_END_TIMEOUT:
- if (DEBUG) Slog.d(TAG, "Ending parole");
- setAppIdleParoled(false);
- break;
-
- case MSG_REPORT_CONTENT_PROVIDER_USAGE:
- SomeArgs args = (SomeArgs) msg.obj;
- reportContentProviderUsage((String) args.arg1, // authority name
- (String) args.arg2, // package name
- (int) args.arg3); // userId
- args.recycle();
- break;
-
- case MSG_PAROLE_STATE_CHANGED:
- if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
- + ", Charging state:" + mCharging);
- informParoleStateChanged();
- break;
-
default:
super.handleMessage(msg);
break;
@@ -1301,72 +528,6 @@ public class UsageStatsService extends SystemService implements
}
}
- /**
- * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
- */
- private class SettingsObserver extends ContentObserver {
- /**
- * This flag has been used to disable app idle on older builds with bug b/26355386.
- */
- @Deprecated
- private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-
- private static final String KEY_IDLE_DURATION = "idle_duration2";
- private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
- private static final String KEY_PAROLE_INTERVAL = "parole_interval";
- private static final String KEY_PAROLE_DURATION = "parole_duration";
-
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- SettingsObserver(Handler handler) {
- super(handler);
- }
-
- void registerObserver() {
- getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.APP_IDLE_CONSTANTS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- updateSettings();
- postOneTimeCheckIdleStates();
- }
-
- void updateSettings() {
- synchronized (mAppIdleLock) {
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
- }
-
- // Default: 12 hours of screen-on time sans dream-time
- mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
- mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
- mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
-
- // Default: 24 hours between paroles
- mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
- COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
- mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
- mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
- mAppIdleScreenThresholdMillis);
- }
- }
- }
-
private final class BinderService extends IUsageStatsManager.Stub {
private boolean hasPermission(String callingPackage) {
@@ -1462,7 +623,8 @@ public class UsageStatsService extends SystemService implements
Binder.getCallingUid(), userId);
final long token = Binder.clearCallingIdentity();
try {
- return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+ return mAppStandby.isAppIdleFilteredOrParoled(
+ packageName, userId,
SystemClock.elapsedRealtime(), obfuscateInstantApps);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1483,9 +645,9 @@ public class UsageStatsService extends SystemService implements
"No permission to change app idle state");
final long token = Binder.clearCallingIdentity();
try {
- final int appId = getAppId(packageName);
+ final int appId = mAppStandby.getAppId(packageName);
if (appId < 0) return;
- UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
+ mAppStandby.setAppIdleAsync(packageName, idle, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1509,7 +671,7 @@ public class UsageStatsService extends SystemService implements
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_CARRIER_SERVICES,
"onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
- UsageStatsService.this.clearCarrierPrivilegedApps();
+ mAppStandby.clearCarrierPrivilegedApps();
}
@Override
@@ -1624,28 +786,23 @@ public class UsageStatsService extends SystemService implements
@Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = name;
- args.arg2 = packageName;
- args.arg3 = userId;
- mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
- .sendToTarget();
+ mAppStandby.postReportContentProviderUsage(name, packageName, userId);
}
@Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
- return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
- SystemClock.elapsedRealtime());
+ return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
+ userId, SystemClock.elapsedRealtime());
}
@Override
public int[] getIdleUidsForUser(int userId) {
- return UsageStatsService.this.getIdleUidsForUser(userId);
+ return mAppStandby.getIdleUidsForUser(userId);
}
@Override
public boolean isAppIdleParoleOn() {
- return isParoledOrCharging();
+ return mAppStandby.isParoledOrCharging();
}
@Override
@@ -1658,20 +815,20 @@ public class UsageStatsService extends SystemService implements
@Override
public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
- UsageStatsService.this.addListener(listener);
+ mAppStandby.addListener(listener);
listener.onParoleStateChanged(isAppIdleParoleOn());
}
@Override
public void removeAppIdleStateChangeListener(
AppIdleStateChangeListener listener) {
- UsageStatsService.this.removeListener(listener);
+ mAppStandby.removeListener(listener);
}
@Override
public byte[] getBackupPayload(int user, String key) {
// Check to ensure that only user 0's data is b/r for now
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
@@ -1684,7 +841,7 @@ public class UsageStatsService extends SystemService implements
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
diff --git a/com/android/server/usb/UsbAlsaManager.java b/com/android/server/usb/UsbAlsaManager.java
index 68c1d5f6..acc27bee 100644
--- a/com/android/server/usb/UsbAlsaManager.java
+++ b/com/android/server/usb/UsbAlsaManager.java
@@ -314,7 +314,11 @@ public final class UsbAlsaManager {
return null;
}
- mDevicesParser.scan();
+ if (!mDevicesParser.scan()) {
+ Slog.e(TAG, "Error parsing ALSA devices file.");
+ return null;
+ }
+
int device = mDevicesParser.getDefaultDeviceNum(card);
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
diff --git a/com/android/server/utils/ManagedApplicationService.java b/com/android/server/utils/ManagedApplicationService.java
index 0f251fd8..c5553881 100644
--- a/com/android/server/utils/ManagedApplicationService.java
+++ b/com/android/server/utils/ManagedApplicationService.java
@@ -16,19 +16,24 @@
package com.android.server.utils;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import java.text.SimpleDateFormat;
import java.util.Objects;
+import java.util.Date;
/**
* Manages the lifecycle of an application-provided service bound from system server.
@@ -38,39 +43,126 @@ import java.util.Objects;
public class ManagedApplicationService {
private final String TAG = getClass().getSimpleName();
+ /**
+ * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event
+ * is received.
+ */
+ public static final int RETRY_FOREVER = 1;
+
+ /**
+ * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected
+ * event will cause this to fully unbind the service and never attempt to reconnect.
+ */
+ public static final int RETRY_NEVER = 2;
+
+ /**
+ * Attempt to reconnect the service until the maximum number of retries is reached, then stop.
+ *
+ * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each
+ * subsequent retry will occur after 2x the duration used for the previous retry up to the
+ * MAX_RETRY_DURATION_MS duration.
+ *
+ * In this case, retries mean a full unbindService/bindService pair to handle cases when the
+ * usual service re-connection logic in ActiveServices has very high backoff times or when the
+ * serviceconnection has fully died due to a package update or similar.
+ */
+ public static final int RETRY_BEST_EFFORT = 3;
+
+ // Maximum number of retries before giving up (for RETRY_BEST_EFFORT).
+ private static final int MAX_RETRY_COUNT = 4;
+ // Max time between retry attempts.
+ private static final long MAX_RETRY_DURATION_MS = 16000;
+ // Min time between retry attempts.
+ private static final long MIN_RETRY_DURATION_MS = 2000;
+ // Time since the last retry attempt after which to clear the retry attempt counter.
+ private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4;
+
private final Context mContext;
private final int mUserId;
private final ComponentName mComponent;
private final int mClientLabel;
private final String mSettingsAction;
private final BinderChecker mChecker;
-
- private final DeathRecipient mDeathRecipient = new DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mLock) {
- mBoundInterface = null;
- }
- }
- };
+ private final boolean mIsImportant;
+ private final int mRetryType;
+ private final Handler mHandler;
+ private final Runnable mRetryRunnable = this::doRetry;
+ private final EventCallback mEventCb;
private final Object mLock = new Object();
// State protected by mLock
- private ServiceConnection mPendingConnection;
private ServiceConnection mConnection;
private IInterface mBoundInterface;
private PendingEvent mPendingEvent;
+ private int mRetryCount;
+ private long mLastRetryTimeMs;
+ private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+ private boolean mRetrying;
+
+ public static interface LogFormattable {
+ String toLogString(SimpleDateFormat dateFormat);
+ }
+
+ /**
+ * Lifecycle event of this managed service.
+ */
+ public static class LogEvent implements LogFormattable {
+ public static final int EVENT_CONNECTED = 1;
+ public static final int EVENT_DISCONNECTED = 2;
+ public static final int EVENT_BINDING_DIED = 3;
+ public static final int EVENT_STOPPED_PERMANENTLY = 4;
+
+ // Time of the events in "current time ms" timebase.
+ public final long timestamp;
+ // Name of the component for this system service.
+ public final ComponentName component;
+ // ID of the event that occurred.
+ public final int event;
+
+ public LogEvent(long timestamp, ComponentName component, int event) {
+ this.timestamp = timestamp;
+ this.component = component;
+ this.event = event;
+ }
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ return dateFormat.format(new Date(timestamp)) + " " + eventToString(event)
+ + " Managed Service: "
+ + ((component == null) ? "None" : component.flattenToString());
+ }
+
+ public static String eventToString(int event) {
+ switch (event) {
+ case EVENT_CONNECTED:
+ return "Connected";
+ case EVENT_DISCONNECTED:
+ return "Disconnected";
+ case EVENT_BINDING_DIED:
+ return "Binding Died For";
+ case EVENT_STOPPED_PERMANENTLY:
+ return "Permanently Stopped";
+ default:
+ return "Unknown Event Occurred";
+ }
+ }
+ }
private ManagedApplicationService(final Context context, final ComponentName component,
final int userId, int clientLabel, String settingsAction,
- BinderChecker binderChecker) {
+ BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler,
+ EventCallback eventCallback) {
mContext = context;
mComponent = component;
mUserId = userId;
mClientLabel = clientLabel;
mSettingsAction = settingsAction;
mChecker = binderChecker;
+ mIsImportant = isImportant;
+ mRetryType = retryType;
+ mHandler = handler;
+ mEventCb = eventCallback;
}
/**
@@ -85,7 +177,17 @@ public class ManagedApplicationService {
* Implement to call IInterface methods after service is connected.
*/
public interface PendingEvent {
- void runEvent(IInterface service) throws RemoteException;
+ void runEvent(IInterface service) throws RemoteException;
+ }
+
+ /**
+ * Implement to be notified about any problems with remote service.
+ */
+ public interface EventCallback {
+ /**
+ * Called when an sevice lifecycle event occurs.
+ */
+ void onServiceEvent(LogEvent event);
}
/**
@@ -95,19 +197,28 @@ public class ManagedApplicationService {
* @param component the {@link ComponentName} of the application service to bind.
* @param userId the user ID of user to bind the application service as.
* @param clientLabel the resource ID of a label displayed to the user indicating the
- * binding service.
+ * binding service, or 0 if none is desired.
* @param settingsAction an action that can be used to open the Settings UI to enable/disable
- * binding to these services.
- * @param binderChecker an interface used to validate the returned binder object.
+ * binding to these services, or null if none is desired.
+ * @param binderChecker an interface used to validate the returned binder object, or null if
+ * this interface is unchecked.
+ * @param isImportant bind the user service with BIND_IMPORTANT.
+ * @param retryType reconnect behavior to have when bound service is disconnected.
+ * @param handler the Handler to use for retries and delivering EventCallbacks.
+ * @param eventCallback a callback used to deliver disconnection events, or null if you
+ * don't care.
* @return a ManagedApplicationService instance.
*/
public static ManagedApplicationService build(@NonNull final Context context,
- @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
- @NonNull String settingsAction, @NonNull BinderChecker binderChecker) {
+ @NonNull final ComponentName component, final int userId, int clientLabel,
+ @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
+ boolean isImportant, int retryType, @NonNull Handler handler,
+ @Nullable EventCallback eventCallback) {
return new ManagedApplicationService(context, component, userId, clientLabel,
- settingsAction, binderChecker);
+ settingsAction, binderChecker, isImportant, retryType, handler, eventCallback);
}
+
/**
* @return the user ID of the user that owns the bound service.
*/
@@ -138,13 +249,12 @@ public class ManagedApplicationService {
return true;
}
-
- /**
- * Send an event to run as soon as the binder interface is available.
- *
- * @param event a {@link PendingEvent} to send.
- */
- public void sendEvent(@NonNull PendingEvent event) {
+ /**
+ * Send an event to run as soon as the binder interface is available.
+ *
+ * @param event a {@link PendingEvent} to send.
+ */
+ public void sendEvent(@NonNull PendingEvent event) {
IInterface iface;
synchronized (mLock) {
iface = mBoundInterface;
@@ -167,15 +277,13 @@ public class ManagedApplicationService {
*/
public void disconnect() {
synchronized (mLock) {
- // Wipe out pending connections
- mPendingConnection = null;
-
// Unbind existing connection, if it exists
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection = null;
+ if (mConnection == null) {
+ return;
}
+ mContext.unbindService(mConnection);
+ mConnection = null;
mBoundInterface = null;
}
}
@@ -185,48 +293,70 @@ public class ManagedApplicationService {
*/
public void connect() {
synchronized (mLock) {
- if (mConnection != null || mPendingConnection != null) {
+ if (mConnection != null) {
// We're already connected or are trying to connect
return;
}
- final PendingIntent pendingIntent = PendingIntent.getActivity(
- mContext, 0, new Intent(mSettingsAction), 0);
- final Intent intent = new Intent().setComponent(mComponent).
- putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
- putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+ Intent intent = new Intent().setComponent(mComponent);
+ if (mClientLabel != 0) {
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel);
+ }
+ if (mSettingsAction != null) {
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT,
+ PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0));
+ }
+
+ mConnection = new ServiceConnection() {
+ @Override
+ public void onBindingDied(ComponentName componentName) {
+ final long timestamp = System.currentTimeMillis();
+ Slog.w(TAG, "Service binding died: " + componentName);
+ synchronized (mLock) {
+ if (mConnection != this) {
+ return;
+ }
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_BINDING_DIED));
+ });
+
+ mBoundInterface = null;
+ startRetriesLocked();
+ }
+ }
- final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ final long timestamp = System.currentTimeMillis();
+ Slog.i(TAG, "Service connected: " + componentName);
IInterface iface = null;
PendingEvent pendingEvent = null;
synchronized (mLock) {
- if (mPendingConnection == this) {
- // No longer pending, remove from pending connection
- mPendingConnection = null;
- mConnection = this;
- } else {
- // Service connection wasn't pending, must have been disconnected
- mContext.unbindService(this);
+ if (mConnection != this) {
+ // Must've been unbound.
return;
}
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_CONNECTED));
+ });
- try {
- iBinder.linkToDeath(mDeathRecipient, 0);
+ stopRetriesLocked();
+
+ mBoundInterface = null;
+ if (mChecker != null) {
mBoundInterface = mChecker.asInterface(iBinder);
if (!mChecker.checkType(mBoundInterface)) {
- // Received an invalid binder, disconnect
- mContext.unbindService(this);
+ // Received an invalid binder, disconnect.
mBoundInterface = null;
+ Slog.w(TAG, "Invalid binder from " + componentName);
+ startRetriesLocked();
+ return;
}
iface = mBoundInterface;
pendingEvent = mPendingEvent;
mPendingEvent = null;
- } catch (RemoteException e) {
- // DOA
- Slog.w(TAG, "Unable to bind service: " + intent, e);
- mBoundInterface = null;
}
}
if (iface != null && pendingEvent != null) {
@@ -234,28 +364,44 @@ public class ManagedApplicationService {
pendingEvent.runEvent(iface);
} catch (RuntimeException | RemoteException ex) {
Slog.e(TAG, "Received exception from user service: ", ex);
+ startRetriesLocked();
}
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
- Slog.w(TAG, "Service disconnected: " + intent);
- mConnection = null;
- mBoundInterface = null;
+ final long timestamp = System.currentTimeMillis();
+ Slog.w(TAG, "Service disconnected: " + componentName);
+ synchronized (mLock) {
+ if (mConnection != this) {
+ return;
+ }
+
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_DISCONNECTED));
+ });
+
+ mBoundInterface = null;
+ startRetriesLocked();
+ }
}
};
- mPendingConnection = serviceConnection;
-
+ int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+ if (mIsImportant) {
+ flags |= Context.BIND_IMPORTANT;
+ }
try {
- if (!mContext.bindServiceAsUser(intent, serviceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ if (!mContext.bindServiceAsUser(intent, mConnection, flags,
new UserHandle(mUserId))) {
Slog.w(TAG, "Unable to bind service: " + intent);
+ startRetriesLocked();
}
} catch (SecurityException e) {
Slog.w(TAG, "Unable to bind service: " + intent, e);
+ startRetriesLocked();
}
}
}
@@ -263,4 +409,81 @@ public class ManagedApplicationService {
private boolean matches(final ComponentName component, final int userId) {
return Objects.equals(mComponent, component) && mUserId == userId;
}
+
+ private void startRetriesLocked() {
+ if (checkAndDeliverServiceDiedCbLocked()) {
+ // If we delivered the service callback, disconnect and stop retrying.
+ disconnect();
+ return;
+ }
+
+ if (mRetrying) {
+ // Retry already queued, don't queue a new one.
+ return;
+ }
+ mRetrying = true;
+ queueRetryLocked();
+ }
+
+ private void stopRetriesLocked() {
+ mRetrying = false;
+ mHandler.removeCallbacks(mRetryRunnable);
+ }
+
+ private void queueRetryLocked() {
+ long now = SystemClock.uptimeMillis();
+ if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) {
+ // It's been longer than the reset time since we last had to retry. Re-initialize.
+ mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+ mRetryCount = 0;
+ }
+ mLastRetryTimeMs = now;
+ mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs);
+ mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS);
+ mRetryCount++;
+ }
+
+ private boolean checkAndDeliverServiceDiedCbLocked() {
+
+ if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT
+ && mRetryCount >= MAX_RETRY_COUNT)) {
+ // If we never retry, or we've exhausted our retries, post the onServiceDied callback.
+ Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying.");
+ if (mEventCb != null) {
+ final long timestamp = System.currentTimeMillis();
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_STOPPED_PERMANENTLY));
+ });
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void doRetry() {
+ synchronized (mLock) {
+ if (mConnection == null) {
+ // We disconnected for good. Don't attempt to retry.
+ return;
+ }
+ if (!mRetrying) {
+ // We successfully connected. Don't attempt to retry.
+ return;
+ }
+ Slog.i(TAG, "Attempting to reconnect " + mComponent + "...");
+ // While frameworks may restart the remote Service if we stay bound, we have little
+ // control of the backoff timing for reconnecting the service. In the event of a
+ // process crash, the backoff time can be very large (1-30 min), which is not
+ // acceptable for the types of services this is used for. Instead force an unbind/bind
+ // sequence to cause a more immediate retry.
+ disconnect();
+ if (checkAndDeliverServiceDiedCbLocked()) {
+ // No more retries.
+ return;
+ }
+ queueRetryLocked();
+ connect();
+ }
+ }
}
diff --git a/com/android/server/utils/PriorityDump.java b/com/android/server/utils/PriorityDump.java
index c05cc3ff..054f1564 100644
--- a/com/android/server/utils/PriorityDump.java
+++ b/com/android/server/utils/PriorityDump.java
@@ -59,10 +59,10 @@ public class SpringfieldNuclearPowerPlant extends Binder {
Donuts in the box: 1
Nuclear reactor status: DANGER - MELTDOWN IMMINENT
- $ adb shell dumpsys snpp --dump_priority CRITICAL
+ $ adb shell dumpsys snpp --dump-priority CRITICAL
Donuts in the box: 1
- $ adb shell dumpsys snpp --dump_priority NORMAL
+ $ adb shell dumpsys snpp --dump-priority NORMAL
Nuclear reactor status: DANGER - MELTDOWN IMMINENT
* </code></pre>
@@ -84,7 +84,7 @@ public class SpringfieldNuclearPowerPlant extends Binder {
*/
public final class PriorityDump {
- public static final String PRIORITY_ARG = "--dump_priority";
+ public static final String PRIORITY_ARG = "--dump-priority";
private PriorityDump() {
throw new UnsupportedOperationException();
@@ -92,12 +92,12 @@ public final class PriorityDump {
/**
* Parses {@code} and call the proper {@link PriorityDumper} method when the first argument is
- * {@code --dump_priority}, stripping the priority and its type.
+ * {@code --dump-priority}, stripping the priority and its type.
* <p>
- * For example, if called as {@code --dump_priority HIGH arg1 arg2 arg3}, it will call
+ * For example, if called as {@code --dump-priority HIGH arg1 arg2 arg3}, it will call
* <code>dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}) </code>
* <p>
- * If the {@code --dump_priority} is not set, it calls
+ * If the {@code --dump-priority} is not set, it calls
* {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[])} passing the whole
* {@code args} instead.
*/
@@ -124,7 +124,7 @@ public final class PriorityDump {
}
/**
- * Gets an array without the {@code --dump_priority PRIORITY} prefix.
+ * Gets an array without the {@code --dump-priority PRIORITY} prefix.
*/
private static String[] getStrippedArgs(String[] args) {
final String[] stripped = new String[args.length - 2];
diff --git a/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 3788cf33..b040a632 100644
--- a/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_ASSISTANT_HIDDEN_SESSION;
import static android.app.ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION;
import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION;
import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
@@ -222,8 +223,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(StackId.ASSISTANT_STACK_ID);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
return mAm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
intent, resolvedType, options.toBundle(), mUser);
} catch (RemoteException e) {
diff --git a/com/android/server/vr/Vr2dDisplay.java b/com/android/server/vr/Vr2dDisplay.java
index 8f50a39a..95d03d4b 100644
--- a/com/android/server/vr/Vr2dDisplay.java
+++ b/com/android/server/vr/Vr2dDisplay.java
@@ -294,6 +294,9 @@ class Vr2dDisplay {
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
null /* surface */, flags, null /* callback */, null /* handler */,
diff --git a/com/android/server/vr/VrManagerInternal.java b/com/android/server/vr/VrManagerInternal.java
index bdd9de01..7b1e12e2 100644
--- a/com/android/server/vr/VrManagerInternal.java
+++ b/com/android/server/vr/VrManagerInternal.java
@@ -74,6 +74,13 @@ public abstract class VrManagerInternal {
public abstract void onScreenStateChanged(boolean isScreenOn);
/**
+ * Set whether the keyguard is currently active/showing.
+ *
+ * @param isShowing is {@code true} if the keyguard is active/showing.
+ */
+ public abstract void onKeyguardStateChanged(boolean isShowing);
+
+ /**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
*
diff --git a/com/android/server/vr/VrManagerService.java b/com/android/server/vr/VrManagerService.java
index 1f0b2f00..e7e4efcc 100644
--- a/com/android/server/vr/VrManagerService.java
+++ b/com/android/server/vr/VrManagerService.java
@@ -66,6 +66,8 @@ import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
@@ -108,16 +110,18 @@ public class VrManagerService extends SystemService implements EnabledComponentC
static final boolean DBG = false;
private static final int PENDING_STATE_DELAY_MS = 300;
- private static final int EVENT_LOG_SIZE = 32;
+ private static final int EVENT_LOG_SIZE = 64;
private static final int INVALID_APPOPS_MODE = -1;
/** Null set of sleep sleep flags. */
private static final int FLAG_NONE = 0;
/** Flag set when the device is not sleeping. */
- private static final int FLAG_AWAKE = 1;
+ private static final int FLAG_AWAKE = 1 << 0;
/** Flag set when the screen has been turned on. */
- private static final int FLAG_SCREEN_ON = 2;
+ private static final int FLAG_SCREEN_ON = 1 << 1;
+ /** Flag set when the keyguard is not active. */
+ private static final int FLAG_KEYGUARD_UNLOCKED = 1 << 2;
/** Flag indicating that all system sleep flags have been set.*/
- private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON;
+ private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON | FLAG_KEYGUARD_UNLOCKED;
private static native void initializeNative();
private static native void setVrModeNative(boolean enabled);
@@ -134,6 +138,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private int mVrAppProcessId;
private EnabledComponentsObserver mComponentObserver;
private ManagedApplicationService mCurrentVrService;
+ private ManagedApplicationService mCurrentVrCompositorService;
private ComponentName mDefaultVrService;
private Context mContext;
private ComponentName mCurrentVrModeComponent;
@@ -147,19 +152,45 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE;
private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE;
private VrState mPendingState;
- private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
+ private boolean mLogLimitHit;
+ private final ArrayDeque<LogFormattable> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
private INotificationManager mNotificationManager;
/** Tracks the state of the screen and keyguard UI.*/
- private int mSystemSleepFlags = FLAG_AWAKE;
+ private int mSystemSleepFlags = FLAG_AWAKE | FLAG_KEYGUARD_UNLOCKED;
/**
* Set when ACTION_USER_UNLOCKED is fired. We shouldn't try to bind to the
- * vr service before then.
+ * vr service before then. This gets set only once the first time the user unlocks the device
+ * and stays true thereafter.
*/
private boolean mUserUnlocked;
private Vr2dDisplay mVr2dDisplay;
private boolean mBootsToVr;
+ // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor
+ // service).
+ private final ManagedApplicationService.EventCallback mEventCallback
+ = new ManagedApplicationService.EventCallback() {
+ @Override
+ public void onServiceEvent(LogEvent event) {
+ logEvent(event);
+
+ ComponentName component = null;
+ synchronized (mLock) {
+ component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent());
+ }
+
+ // If not on an AIO device and we permanently stopped trying to connect to the
+ // VrListenerService (or don't have one bound), leave persistent VR mode and VR mode.
+ if (!mBootsToVr && event.event == LogEvent.EVENT_STOPPED_PERMANENTLY &&
+ (component == null || component.equals(event.component))) {
+ Slog.e(TAG, "VrListenerSevice has died permanently, leaving system VR mode.");
+ // We're not a native VR device. Leave VR + persistent mode.
+ setPersistentVrModeEnabled(false);
+ }
+ }
+ };
+
private static final int MSG_VR_STATE_CHANGE = 0;
private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2;
@@ -180,7 +211,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC
if (mBootsToVr) {
setPersistentVrModeEnabled(true);
}
- consumeAndApplyPendingStateLocked();
if (mBootsToVr && !mVrModeEnabled) {
setVrMode(true, mDefaultVrService, 0, -1, null);
}
@@ -202,29 +232,40 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
private void setSleepState(boolean isAsleep) {
- synchronized(mLock) {
+ setSystemState(FLAG_AWAKE, !isAsleep);
+ }
- if (!isAsleep) {
- mSystemSleepFlags |= FLAG_AWAKE;
- } else {
- mSystemSleepFlags &= ~FLAG_AWAKE;
- }
+ private void setScreenOn(boolean isScreenOn) {
+ setSystemState(FLAG_SCREEN_ON, isScreenOn);
+ }
- updateVrModeAllowedLocked();
- }
+ private void setKeyguardShowing(boolean isShowing) {
+ setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
}
- private void setScreenOn(boolean isScreenOn) {
+ private void setSystemState(int flags, boolean isOn) {
synchronized(mLock) {
- if (isScreenOn) {
- mSystemSleepFlags |= FLAG_SCREEN_ON;
+ int oldState = mSystemSleepFlags;
+ if (isOn) {
+ mSystemSleepFlags |= flags;
} else {
- mSystemSleepFlags &= ~FLAG_SCREEN_ON;
+ mSystemSleepFlags &= ~flags;
+ }
+ if (oldState != mSystemSleepFlags) {
+ if (DBG) Slog.d(TAG, "System state: " + getStateAsString());
+ updateVrModeAllowedLocked();
}
- updateVrModeAllowedLocked();
}
}
+ private String getStateAsString() {
+ return new StringBuilder()
+ .append((mSystemSleepFlags & FLAG_AWAKE) != 0 ? "awake, " : "")
+ .append((mSystemSleepFlags & FLAG_SCREEN_ON) != 0 ? "screen_on, " : "")
+ .append((mSystemSleepFlags & FLAG_KEYGUARD_UNLOCKED) != 0 ? "keyguard_off" : "")
+ .toString();
+ }
+
private void setUserUnlocked() {
synchronized(mLock) {
mUserUnlocked = true;
@@ -276,7 +317,24 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
};
- private static class VrState {
+ // Event used to log when settings are changed for dumpsys logs.
+ private static class SettingEvent implements LogFormattable {
+ public final long timestamp;
+ public final String what;
+
+ SettingEvent(String what) {
+ this.timestamp = System.currentTimeMillis();
+ this.what = what;
+ }
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ return dateFormat.format(new Date(timestamp)) + " " + what;
+ }
+ }
+
+ // Event used to track changes of the primary on-screen VR activity.
+ private static class VrState implements LogFormattable {
final boolean enabled;
final boolean running2dInVr;
final int userId;
@@ -286,7 +344,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC
final long timestamp;
final boolean defaultPermissionsGranted;
-
VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId,
int processId, ComponentName callingPackage) {
this.enabled = enabled;
@@ -310,6 +367,39 @@ public class VrManagerService extends SystemService implements EnabledComponentC
this.defaultPermissionsGranted = defaultPermissionsGranted;
this.timestamp = System.currentTimeMillis();
}
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ String tab = " ";
+ String newLine = "\n";
+ StringBuilder sb = new StringBuilder(dateFormat.format(new Date(timestamp)));
+ sb.append(tab);
+ sb.append("State changed to:");
+ sb.append(tab);
+ sb.append((enabled) ? "ENABLED" : "DISABLED");
+ sb.append(newLine);
+ if (enabled) {
+ sb.append(tab);
+ sb.append("User=");
+ sb.append(userId);
+ sb.append(newLine);
+ sb.append(tab);
+ sb.append("Current VR Activity=");
+ sb.append((callingPackage == null) ? "None" : callingPackage.flattenToString());
+ sb.append(newLine);
+ sb.append(tab);
+ sb.append("Bound VrListenerService=");
+ sb.append((targetPackageName == null) ? "None"
+ : targetPackageName.flattenToString());
+ sb.append(newLine);
+ if (defaultPermissionsGranted) {
+ sb.append(tab);
+ sb.append("Default permissions granted to the bound VrListenerService.");
+ sb.append(newLine);
+ }
+ }
+ return sb.toString();
+ }
}
private static final BinderChecker sBinderChecker = new BinderChecker() {
@@ -490,6 +580,13 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
@Override
+ public void setAndBindCompositor(String componentName) {
+ enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
+ VrManagerService.this.setAndBindCompositor(
+ (componentName == null) ? null : ComponentName.unflattenFromString(componentName));
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -497,6 +594,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC
pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
pw.println("Persistent VR mode is currently: " +
((mPersistentVrModeEnabled) ? "enabled" : "disabled"));
+ pw.println("Currently bound VR listener service: "
+ + ((mCurrentVrService == null)
+ ? "None" : mCurrentVrService.getComponent().flattenToString()));
+ pw.println("Currently bound VR compositor service: "
+ + ((mCurrentVrCompositorService == null)
+ ? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
pw.println("Previous state transitions:\n");
String tab = " ";
dumpStateTransitions(pw);
@@ -582,6 +685,11 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
@Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ VrManagerService.this.setKeyguardShowing(isShowing);
+ }
+
+ @Override
public boolean isCurrentVrListener(String packageName, int userId) {
return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
@@ -785,6 +893,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC
+ mCurrentVrService.getComponent() + " for user "
+ mCurrentVrService.getUserId());
mCurrentVrService.disconnect();
+ updateCompositorServiceLocked(UserHandle.USER_NULL, null);
mCurrentVrService = null;
} else {
nothingChanged = true;
@@ -798,6 +907,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC
Slog.i(TAG, "VR mode component changed to " + component
+ ", disconnecting " + mCurrentVrService.getComponent()
+ " for user " + mCurrentVrService.getUserId());
+ updateCompositorServiceLocked(UserHandle.USER_NULL, null);
createAndConnectService(component, userId);
sendUpdatedCaller = true;
} else {
@@ -985,7 +1095,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private void createAndConnectService(@NonNull ComponentName component, int userId) {
- mCurrentVrService = VrManagerService.create(mContext, component, userId);
+ mCurrentVrService = createVrListenerService(component, userId);
mCurrentVrService.connect();
Slog.i(TAG, "Connecting " + component + " for user " + userId);
}
@@ -1020,13 +1130,27 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
/**
- * Helper function for making ManagedApplicationService instances.
+ * Helper function for making ManagedApplicationService for VrListenerService instances.
*/
- private static ManagedApplicationService create(@NonNull Context context,
- @NonNull ComponentName component, int userId) {
- return ManagedApplicationService.build(context, component, userId,
+ private ManagedApplicationService createVrListenerService(@NonNull ComponentName component,
+ int userId) {
+ int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+ : ManagedApplicationService.RETRY_NEVER;
+ return ManagedApplicationService.build(mContext, component, userId,
R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
- sBinderChecker);
+ sBinderChecker, /*isImportant*/true, retryType, mHandler, mEventCallback);
+ }
+
+ /**
+ * Helper function for making ManagedApplicationService for VR Compositor instances.
+ */
+ private ManagedApplicationService createVrCompositorService(@NonNull ComponentName component,
+ int userId) {
+ int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+ : ManagedApplicationService.RETRY_BEST_EFFORT;
+ return ManagedApplicationService.build(mContext, component, userId, /*clientLabel*/0,
+ /*settingsAction*/null, /*binderChecker*/null, /*isImportant*/true, retryType,
+ mHandler, /*disconnectCallback*/mEventCallback);
}
/**
@@ -1057,44 +1181,35 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private void logStateLocked() {
ComponentName currentBoundService = (mCurrentVrService == null) ? null :
- mCurrentVrService.getComponent();
- VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
- mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted);
- if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
- mLoggingDeque.removeFirst();
+ mCurrentVrService.getComponent();
+ logEvent(new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
+ mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted));
+ }
+
+ private void logEvent(LogFormattable event) {
+ synchronized (mLoggingDeque) {
+ if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
+ mLoggingDeque.removeFirst();
+ mLogLimitHit = true;
+ }
+ mLoggingDeque.add(event);
}
- mLoggingDeque.add(current);
}
private void dumpStateTransitions(PrintWriter pw) {
SimpleDateFormat d = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- String tab = " ";
- if (mLoggingDeque.size() == 0) {
- pw.print(tab);
- pw.println("None");
- }
- for (VrState state : mLoggingDeque) {
- pw.print(d.format(new Date(state.timestamp)));
- pw.print(tab);
- pw.print("State changed to:");
- pw.print(tab);
- pw.println((state.enabled) ? "ENABLED" : "DISABLED");
- if (state.enabled) {
- pw.print(tab);
- pw.print("User=");
- pw.println(state.userId);
- pw.print(tab);
- pw.print("Current VR Activity=");
- pw.println((state.callingPackage == null) ?
- "None" : state.callingPackage.flattenToString());
- pw.print(tab);
- pw.print("Bound VrListenerService=");
- pw.println((state.targetPackageName == null) ?
- "None" : state.targetPackageName.flattenToString());
- if (state.defaultPermissionsGranted) {
- pw.print(tab);
- pw.println("Default permissions granted to the bound VrListenerService.");
- }
+ synchronized (mLoggingDeque) {
+ if (mLoggingDeque.size() == 0) {
+ pw.print(" ");
+ pw.println("None");
+ }
+
+ if (mLogLimitHit) {
+ pw.println("..."); // Indicates log overflow
+ }
+
+ for (LogFormattable event : mLoggingDeque) {
+ pw.println(event.toLogString(d));
}
}
}
@@ -1177,10 +1292,41 @@ public class VrManagerService extends SystemService implements EnabledComponentC
return INVALID_DISPLAY;
}
+ private void setAndBindCompositor(ComponentName componentName) {
+ final int userId = UserHandle.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ synchronized (mLock) {
+ updateCompositorServiceLocked(userId, componentName);
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private void updateCompositorServiceLocked(int userId, ComponentName componentName) {
+ if (mCurrentVrCompositorService != null
+ && mCurrentVrCompositorService.disconnectIfNotMatching(componentName, userId)) {
+ Slog.i(TAG, "Disconnecting compositor service: "
+ + mCurrentVrCompositorService.getComponent());
+ // Check if existing service matches the requested one, if not (or if the requested
+ // component is null) disconnect it.
+ mCurrentVrCompositorService = null;
+ }
+
+ if (componentName != null && mCurrentVrCompositorService == null) {
+ // We don't have an existing service matching the requested component, so attempt to
+ // connect one.
+ Slog.i(TAG, "Connecting compositor service: " + componentName);
+ mCurrentVrCompositorService = createVrCompositorService(componentName, userId);
+ mCurrentVrCompositorService.connect();
+ }
+ }
+
private void setPersistentModeAndNotifyListenersLocked(boolean enabled) {
if (mPersistentVrModeEnabled == enabled) {
return;
}
+ String eventName = "Persistent VR mode " + ((enabled) ? "enabled" : "disabled");
+ Slog.i(TAG, eventName);
+ logEvent(new SettingEvent(eventName));
mPersistentVrModeEnabled = enabled;
mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE,
diff --git a/com/android/server/webkit/SystemImpl.java b/com/android/server/webkit/SystemImpl.java
index bf769ed4..1e334b83 100644
--- a/com/android/server/webkit/SystemImpl.java
+++ b/com/android/server/webkit/SystemImpl.java
@@ -304,6 +304,6 @@ public class SystemImpl implements SystemInterface {
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
- | PackageManager.MATCH_ANY_USER;
+ | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
}
diff --git a/com/android/server/wifi/NetworkListStoreData.java b/com/android/server/wifi/NetworkListStoreData.java
index 5ddfd4df..f287d4b9 100644
--- a/com/android/server/wifi/NetworkListStoreData.java
+++ b/com/android/server/wifi/NetworkListStoreData.java
@@ -16,10 +16,12 @@
package com.android.server.wifi;
+import android.content.Context;
import android.net.IpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
+import android.os.Process;
import android.util.Log;
import android.util.Pair;
@@ -52,6 +54,8 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData {
private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION =
"WifiEnterpriseConfiguration";
+ private final Context mContext;
+
/**
* List of saved shared networks visible to all the users to be stored in the shared store file.
*/
@@ -62,7 +66,9 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData {
*/
private List<WifiConfiguration> mUserConfigurations;
- NetworkListStoreData() {}
+ NetworkListStoreData(Context context) {
+ mContext = context;
+ }
@Override
public void serializeData(XmlSerializer out, boolean shared)
@@ -282,6 +288,19 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData {
"Configuration key does not match. Retrieved: " + configKeyParsed
+ ", Calculated: " + configKeyCalculated);
}
+ // Set creatorUid/creatorName for networks which don't have it set to valid value.
+ String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid);
+ if (creatorName == null) {
+ Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey()
+ + ", creatorUid=" + configuration.creatorUid);
+ configuration.creatorUid = Process.SYSTEM_UID;
+ configuration.creatorName = creatorName;
+ } else if (!creatorName.equals(configuration.creatorName)) {
+ Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey()
+ + ", creatorUid=" + configuration.creatorUid
+ + ", creatorName=" + configuration.creatorName);
+ configuration.creatorName = creatorName;
+ }
configuration.setNetworkSelectionStatus(status);
configuration.setIpConfiguration(ipConfiguration);
diff --git a/com/android/server/wifi/OpenNetworkNotifier.java b/com/android/server/wifi/OpenNetworkNotifier.java
index 31ff44b7..eee4ac53 100644
--- a/com/android/server/wifi/OpenNetworkNotifier.java
+++ b/com/android/server/wifi/OpenNetworkNotifier.java
@@ -40,11 +40,13 @@ import android.os.Messenger;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
import com.android.server.wifi.util.ScanResultUtil;
import java.io.FileDescriptor;
@@ -124,6 +126,7 @@ public class OpenNetworkNotifier {
private final Context mContext;
private final Handler mHandler;
private final FrameworkFacade mFrameworkFacade;
+ private final WifiMetrics mWifiMetrics;
private final Clock mClock;
private final WifiConfigManager mConfigManager;
private final WifiStateMachine mWifiStateMachine;
@@ -138,6 +141,7 @@ public class OpenNetworkNotifier {
Looper looper,
FrameworkFacade framework,
Clock clock,
+ WifiMetrics wifiMetrics,
WifiConfigManager wifiConfigManager,
WifiConfigStore wifiConfigStore,
WifiStateMachine wifiStateMachine,
@@ -146,6 +150,7 @@ public class OpenNetworkNotifier {
mContext = context;
mHandler = new Handler(looper);
mFrameworkFacade = framework;
+ mWifiMetrics = wifiMetrics;
mClock = clock;
mConfigManager = wifiConfigManager;
mWifiStateMachine = wifiStateMachine;
@@ -206,7 +211,7 @@ public class OpenNetworkNotifier {
case WifiManager.CONNECT_NETWORK_SUCCEEDED:
break;
case WifiManager.CONNECT_NETWORK_FAILED:
- handleConnectionFailure();
+ handleConnectionAttemptFailedToSend();
break;
default:
Log.e(TAG, "Unknown message " + msg.what);
@@ -226,6 +231,13 @@ public class OpenNetworkNotifier {
if (mState != STATE_NO_NOTIFICATION) {
getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_AVAILABLE);
+
+ if (mRecommendedNetwork != null) {
+ Log.d(TAG, "Notification with state="
+ + mState
+ + " was cleared for recommended network: "
+ + mRecommendedNetwork.SSID);
+ }
mState = STATE_NO_NOTIFICATION;
mRecommendedNetwork = null;
}
@@ -295,6 +307,10 @@ public class OpenNetworkNotifier {
postNotification(mNotificationBuilder.createNetworkConnectedNotification(
mRecommendedNetwork));
+
+ Log.d(TAG, "User connected to recommended network: " + mRecommendedNetwork.SSID);
+ mWifiMetrics.incrementConnectToNetworkNotification(
+ ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK);
mState = STATE_CONNECTED_NOTIFICATION;
mHandler.postDelayed(
() -> {
@@ -313,6 +329,10 @@ public class OpenNetworkNotifier {
return;
}
postNotification(mNotificationBuilder.createNetworkFailedNotification());
+
+ Log.d(TAG, "User failed to connect to recommended network: " + mRecommendedNetwork.SSID);
+ mWifiMetrics.incrementConnectToNetworkNotification(
+ ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT);
mState = STATE_CONNECT_FAILED_NOTIFICATION;
mHandler.postDelayed(
() -> {
@@ -328,8 +348,18 @@ public class OpenNetworkNotifier {
}
private void postInitialNotification(ScanResult recommendedNetwork) {
+ if (mRecommendedNetwork != null
+ && TextUtils.equals(mRecommendedNetwork.SSID, recommendedNetwork.SSID)) {
+ return;
+ }
postNotification(mNotificationBuilder.createConnectToNetworkNotification(
recommendedNetwork));
+ if (mState == STATE_NO_NOTIFICATION) {
+ mWifiMetrics.incrementConnectToNetworkNotification(
+ ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK);
+ } else {
+ mWifiMetrics.incrementNumOpenNetworkRecommendationUpdates();
+ }
mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION;
mRecommendedNetwork = recommendedNetwork;
mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay;
@@ -340,11 +370,15 @@ public class OpenNetworkNotifier {
}
private void handleConnectToNetworkAction() {
+ mWifiMetrics.incrementConnectToNetworkNotificationAction(mState,
+ ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK);
if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
return;
}
postNotification(mNotificationBuilder.createNetworkConnectingNotification(
mRecommendedNetwork));
+ mWifiMetrics.incrementConnectToNetworkNotification(
+ ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK);
Log.d(TAG, "User initiated connection to recommended network: " + mRecommendedNetwork.SSID);
WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(mRecommendedNetwork);
@@ -366,6 +400,8 @@ public class OpenNetworkNotifier {
}
private void handleSeeAllNetworksAction() {
+ mWifiMetrics.incrementConnectToNetworkNotificationAction(mState,
+ ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK);
startWifiSettings();
}
@@ -378,14 +414,26 @@ public class OpenNetworkNotifier {
clearPendingNotification(false /* resetRepeatTime */);
}
+ private void handleConnectionAttemptFailedToSend() {
+ handleConnectionFailure();
+ mWifiMetrics.incrementNumOpenNetworkConnectMessageFailedToSend();
+ }
+
private void handlePickWifiNetworkAfterConnectFailure() {
+ mWifiMetrics.incrementConnectToNetworkNotificationAction(mState,
+ ConnectToNetworkNotificationAndActionCount
+ .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
startWifiSettings();
}
private void handleUserDismissedAction() {
+ Log.d(TAG, "User dismissed notification with state=" + mState);
+ mWifiMetrics.incrementConnectToNetworkNotificationAction(mState,
+ ConnectToNetworkNotificationAndActionCount.ACTION_USER_DISMISSED_NOTIFICATION);
if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
// blacklist dismissed network
mBlacklistedSsids.add(mRecommendedNetwork.SSID);
+ mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size());
mConfigManager.saveToStore(false /* forceWrite */);
Log.d(TAG, "Network is added to the open network notification blacklist: "
+ mRecommendedNetwork.SSID);
@@ -418,6 +466,7 @@ public class OpenNetworkNotifier {
@Override
public void setSsids(Set<String> ssidList) {
mBlacklistedSsids.addAll(ssidList);
+ mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size());
}
}
@@ -440,8 +489,10 @@ public class OpenNetworkNotifier {
}
private boolean getValue() {
- return mFrameworkFacade.getIntegerSetting(mContext,
+ boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled(enabled);
+ return enabled;
}
}
}
diff --git a/com/android/server/wifi/VelocityBasedConnectedScore.java b/com/android/server/wifi/VelocityBasedConnectedScore.java
new file mode 100644
index 00000000..9d90332e
--- /dev/null
+++ b/com/android/server/wifi/VelocityBasedConnectedScore.java
@@ -0,0 +1,133 @@
+/*
+ * 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.wifi;
+
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.R;
+import com.android.server.wifi.util.KalmanFilter;
+import com.android.server.wifi.util.Matrix;
+
+/**
+ * Class used to calculate scores for connected wifi networks and report it to the associated
+ * network agent.
+ */
+public class VelocityBasedConnectedScore extends ConnectedScore {
+
+ // Device configs. The values are examples.
+ private final int mThresholdMinimumRssi5; // -82
+ private final int mThresholdMinimumRssi24; // -85
+
+ private int mFrequency = 5000;
+ private int mRssi = 0;
+ private final KalmanFilter mFilter;
+ private long mLastMillis;
+
+ public VelocityBasedConnectedScore(Context context, Clock clock) {
+ super(clock);
+ mThresholdMinimumRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdMinimumRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mFilter = new KalmanFilter();
+ mFilter.mH = new Matrix(2, new double[]{1.0, 0.0});
+ mFilter.mR = new Matrix(1, new double[]{1.0});
+ }
+
+ /**
+ * Set the Kalman filter's state transition matrix F and process noise covariance Q given
+ * a time step.
+ *
+ * @param dt delta time, in seconds
+ */
+ private void setDeltaTimeSeconds(double dt) {
+ mFilter.mF = new Matrix(2, new double[]{1.0, dt, 0.0, 1.0});
+ Matrix tG = new Matrix(1, new double[]{0.5 * dt * dt, dt});
+ double stda = 0.02; // standard deviation of modelled acceleration
+ mFilter.mQ = tG.dotTranspose(tG).dot(new Matrix(2, new double[]{
+ stda * stda, 0.0,
+ 0.0, stda * stda}));
+ }
+ /**
+ * Reset the filter state.
+ */
+ @Override
+ public void reset() {
+ mLastMillis = 0;
+ }
+
+ /**
+ * Updates scoring state using RSSI and measurement noise estimate
+ * <p>
+ * This is useful if an RSSI comes from another source (e.g. scan results) and the
+ * expected noise varies by source.
+ *
+ * @param rssi signal strength (dB).
+ * @param millis millisecond-resolution time.
+ * @param standardDeviation of the RSSI.
+ */
+ @Override
+ public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
+ if (millis <= 0) return;
+ if (mLastMillis <= 0 || millis < mLastMillis) {
+ double initialVariance = 9.0 * standardDeviation * standardDeviation;
+ mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});
+ mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0});
+ mLastMillis = millis;
+ return;
+ }
+ double dt = (millis - mLastMillis) * 0.001;
+ mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
+ setDeltaTimeSeconds(dt);
+ mFilter.predict();
+ mLastMillis = millis;
+ mFilter.update(new Matrix(1, new double[]{rssi}));
+ }
+
+ /**
+ * Updates the state.
+ */
+ @Override
+ public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
+ int frequency = wifiInfo.getFrequency();
+ if (frequency != mFrequency) {
+ reset(); // Probably roamed
+ mFrequency = frequency;
+ }
+ updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation);
+ }
+
+ /**
+ * Velocity scorer - predict the rssi a few seconds from now
+ */
+ @Override
+ public int generateScore() {
+ int badRssi = mFrequency >= 5000 ? mThresholdMinimumRssi5 : mThresholdMinimumRssi24;
+ double horizonSeconds = 15.0;
+ Matrix x = new Matrix(mFilter.mx);
+ double filteredRssi = x.get(0, 0);
+ setDeltaTimeSeconds(horizonSeconds);
+ x = mFilter.mF.dot(x);
+ double forecastRssi = x.get(0, 0);
+ if (forecastRssi > filteredRssi) {
+ forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase
+ }
+ int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE;
+ return score;
+ }
+}
diff --git a/com/android/server/wifi/WifiBackupRestore.java b/com/android/server/wifi/WifiBackupRestore.java
index 60c3b488..ae5e411f 100644
--- a/com/android/server/wifi/WifiBackupRestore.java
+++ b/com/android/server/wifi/WifiBackupRestore.java
@@ -228,9 +228,8 @@ public class WifiBackupRestore {
}
return parseNetworkConfigurationsFromXml(in, rootTagDepth, version);
- } catch (XmlPullParserException e) {
- Log.e(TAG, "Error parsing the backup data: " + e);
- } catch (IOException e) {
+ } catch (XmlPullParserException | IOException | ClassCastException
+ | IllegalArgumentException e) {
Log.e(TAG, "Error parsing the backup data: " + e);
}
return null;
diff --git a/com/android/server/wifi/WifiConfigManager.java b/com/android/server/wifi/WifiConfigManager.java
index f8b33cbb..ba1695a5 100644
--- a/com/android/server/wifi/WifiConfigManager.java
+++ b/com/android/server/wifi/WifiConfigManager.java
@@ -2333,12 +2333,13 @@ public class WifiConfigManager {
public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
- // Remove any permanently disabled networks.
+ // Remove any permanently or temporarily disabled networks.
Iterator<WifiConfiguration> iter = networks.iterator();
while (iter.hasNext()) {
WifiConfiguration config = iter.next();
if (config.ephemeral || config.isPasspoint()
- || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
+ || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
+ || config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
iter.remove();
}
}
@@ -2574,10 +2575,12 @@ public class WifiConfigManager {
* @param userId The identifier of the user that stopped.
*/
public void handleUserStop(int userId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Handling user stop for " + userId);
+ }
if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
saveToStore(true);
- clearInternalData();
- mCurrentUserId = UserHandle.USER_SYSTEM;
+ clearInternalUserData(mCurrentUserId);
}
}
@@ -2589,6 +2592,7 @@ public class WifiConfigManager {
* - List of deleted ephemeral networks.
*/
private void clearInternalData() {
+ localLog("clearInternalData: Clearing all internal data");
mConfiguredNetworks.clear();
mDeletedEphemeralSSIDs.clear();
mScanDetailCaches.clear();
@@ -2607,12 +2611,16 @@ public class WifiConfigManager {
* removed from memory.
*/
private Set<Integer> clearInternalUserData(int userId) {
+ localLog("clearInternalUserData: Clearing user internal data for " + userId);
Set<Integer> removedNetworkIds = new HashSet<>();
// Remove any private networks of the old user before switching the userId.
for (WifiConfiguration config : getInternalConfiguredNetworks()) {
if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
config.creatorUid, mUserManager.getProfiles(userId))) {
removedNetworkIds.add(config.networkId);
+ localLog("clearInternalUserData: removed config."
+ + " netId=" + config.networkId
+ + " configKey=" + config.configKey());
mConfiguredNetworks.remove(config.networkId);
}
}
diff --git a/com/android/server/wifi/WifiConnectivityManager.java b/com/android/server/wifi/WifiConnectivityManager.java
index 7e730c83..458f73ae 100644
--- a/com/android/server/wifi/WifiConnectivityManager.java
+++ b/com/android/server/wifi/WifiConnectivityManager.java
@@ -30,6 +30,8 @@ import android.net.wifi.WifiScanner.PnoSettings;
import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
+import android.os.WorkSource;
import android.util.LocalLog;
import android.util.Log;
@@ -214,7 +216,7 @@ public class WifiConnectivityManager {
@Override
public void onAlarm() {
- startSingleScan(mIsFullBandScan);
+ startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE);
}
}
@@ -741,7 +743,7 @@ public class WifiConnectivityManager {
localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
+ currentAssociationId);
}
- mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid);
+ mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid);
}
}
@@ -794,7 +796,7 @@ public class WifiConnectivityManager {
localLog("start a single scan from watchdogHandler");
scheduleWatchdogTimer();
- startSingleScan(true);
+ startSingleScan(true, WIFI_WORK_SOURCE);
}
}
@@ -823,7 +825,7 @@ public class WifiConnectivityManager {
}
mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
- startSingleScan(isFullBandScan);
+ startSingleScan(isFullBandScan, WIFI_WORK_SOURCE);
schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
// Set up the next scan interval in an exponential backoff fashion.
@@ -850,7 +852,7 @@ public class WifiConnectivityManager {
}
// Start a single scan
- private void startSingleScan(boolean isFullBandScan) {
+ private void startSingleScan(boolean isFullBandScan, WorkSource workSource) {
if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
return;
}
@@ -875,7 +877,7 @@ public class WifiConnectivityManager {
SingleScanListener singleScanListener =
new SingleScanListener(isFullBandScan);
- mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE);
+ mScanner.startScan(settings, singleScanListener, workSource);
}
// Start a periodic scan when screen is on
@@ -1132,11 +1134,11 @@ public class WifiConnectivityManager {
/**
* Handler for on-demand connectivity scan
*/
- public void forceConnectivityScan() {
- localLog("forceConnectivityScan");
+ public void forceConnectivityScan(WorkSource workSource) {
+ localLog("forceConnectivityScan in request of " + workSource);
mWaitForFullBandScanResults = true;
- startSingleScan(true);
+ startSingleScan(true, workSource);
}
/**
diff --git a/com/android/server/wifi/WifiCountryCode.java b/com/android/server/wifi/WifiCountryCode.java
index e69fb8e1..66a035f0 100644
--- a/com/android/server/wifi/WifiCountryCode.java
+++ b/com/android/server/wifi/WifiCountryCode.java
@@ -83,11 +83,9 @@ public class WifiCountryCode {
public synchronized void simCardRemoved() {
if (DBG) Log.d(TAG, "SIM Card Removed");
// SIM card is removed, we need to reset the country code to phone default.
- if (mRevertCountryCodeOnCellularLoss) {
- mTelephonyCountryCode = null;
- if (mReady) {
- updateCountryCode();
- }
+ mTelephonyCountryCode = null;
+ if (mReady) {
+ updateCountryCode();
}
}
@@ -98,12 +96,9 @@ public class WifiCountryCode {
*/
public synchronized void airplaneModeEnabled() {
if (DBG) Log.d(TAG, "Airplane Mode Enabled");
- mTelephonyCountryCode = null;
// Airplane mode is enabled, we need to reset the country code to phone default.
- if (mRevertCountryCodeOnCellularLoss) {
- mTelephonyCountryCode = null;
- // Country code will be set upon when wpa_supplicant starts next time.
- }
+ // Country code will be set upon when wpa_supplicant starts next time.
+ mTelephonyCountryCode = null;
}
/**
@@ -133,8 +128,10 @@ public class WifiCountryCode {
if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode);
// Empty country code.
if (TextUtils.isEmpty(countryCode)) {
- if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
- mTelephonyCountryCode = null;
+ if (mRevertCountryCodeOnCellularLoss) {
+ if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
+ mTelephonyCountryCode = null;
+ }
} else {
mTelephonyCountryCode = countryCode.toUpperCase();
}
diff --git a/com/android/server/wifi/WifiInjector.java b/com/android/server/wifi/WifiInjector.java
index fc3af83e..1cbb29ef 100644
--- a/com/android/server/wifi/WifiInjector.java
+++ b/com/android/server/wifi/WifiInjector.java
@@ -116,6 +116,7 @@ public class WifiInjector {
private final PasspointManager mPasspointManager;
private final SIMAccessor mSimAccessor;
private HandlerThread mWifiAwareHandlerThread;
+ private HandlerThread mRttHandlerThread;
private HalDeviceManager mHalDeviceManager;
private final IBatteryStats mBatteryStats;
private final WifiStateTracker mWifiStateTracker;
@@ -197,7 +198,7 @@ public class WifiInjector {
mWifiConfigManager = new WifiConfigManager(mContext, mClock,
UserManager.get(mContext), TelephonyManager.from(mContext),
mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil,
- mWifiPermissionsWrapper, new NetworkListStoreData(),
+ mWifiPermissionsWrapper, new NetworkListStoreData(mContext),
new DeletedEphemeralSsidsStoreData());
mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
@@ -225,7 +226,7 @@ public class WifiInjector {
new WrongPasswordNotifier(mContext, mFrameworkFacade));
mCertManager = new WifiCertManager(mContext);
mOpenNetworkNotifier = new OpenNetworkNotifier(mContext,
- mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock,
+ mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics,
mWifiConfigManager, mWifiConfigStore, mWifiStateMachine,
new OpenNetworkRecommender(),
new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
@@ -455,6 +456,19 @@ public class WifiInjector {
}
/**
+ * Returns a singleton instance of a HandlerThread for injection. Uses lazy initialization.
+ *
+ * TODO: share worker thread with other Wi-Fi handlers (b/27924886)
+ */
+ public HandlerThread getRttHandlerThread() {
+ if (mRttHandlerThread == null) { // lazy initialization
+ mRttHandlerThread = new HandlerThread("wifiRttService");
+ mRttHandlerThread.start();
+ }
+ return mRttHandlerThread;
+ }
+
+ /**
* Returns a single instance of HalDeviceManager for injection.
*/
public HalDeviceManager getHalDeviceManager() {
diff --git a/com/android/server/wifi/WifiMetrics.java b/com/android/server/wifi/WifiMetrics.java
index 5db5ee67..071b4f88 100644
--- a/com/android/server/wifi/WifiMetrics.java
+++ b/com/android/server/wifi/WifiMetrics.java
@@ -37,6 +37,7 @@ import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.PasspointMatch;
import com.android.server.wifi.hotspot2.PasspointProvider;
import com.android.server.wifi.nano.WifiMetricsProto;
+import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics;
import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
@@ -83,6 +84,7 @@ public class WifiMetrics {
public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50;
public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100;
public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250;
+ private static final int CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER = 1000;
private Clock mClock;
private boolean mScreenOn;
private int mWifiState;
@@ -150,6 +152,15 @@ public class WifiMetrics {
private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram =
new SparseIntArray();
+ /** Mapping of "Connect to Network" notifications to counts. */
+ private final SparseIntArray mConnectToNetworkNotificationCount = new SparseIntArray();
+ /** Mapping of "Connect to Network" notification user actions to counts. */
+ private final SparseIntArray mConnectToNetworkNotificationActionCount = new SparseIntArray();
+ private int mOpenNetworkRecommenderBlacklistSize = 0;
+ private boolean mIsWifiNetworksAvailableNotificationOn = false;
+ private int mNumOpenNetworkConnectMessageFailedToSend = 0;
+ private int mNumOpenNetworkRecommendationUpdates = 0;
+
class RouterFingerPrint {
private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
RouterFingerPrint() {
@@ -1237,6 +1248,55 @@ public class WifiMetrics {
}
}
+ /** Increments the occurence of a "Connect to Network" notification. */
+ public void incrementConnectToNetworkNotification(int notificationType) {
+ synchronized (mLock) {
+ int count = mConnectToNetworkNotificationCount.get(notificationType);
+ mConnectToNetworkNotificationCount.put(notificationType, count + 1);
+ }
+ }
+
+ /** Increments the occurence of an "Connect to Network" notification user action. */
+ public void incrementConnectToNetworkNotificationAction(int notificationType, int actionType) {
+ synchronized (mLock) {
+ int key = notificationType * CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER
+ + actionType;
+ int count = mConnectToNetworkNotificationActionCount.get(key);
+ mConnectToNetworkNotificationActionCount.put(key, count + 1);
+ }
+ }
+
+ /**
+ * Sets the number of SSIDs blacklisted from recommendation by the open network notification
+ * recommender.
+ */
+ public void setOpenNetworkRecommenderBlacklistSize(int size) {
+ synchronized (mLock) {
+ mOpenNetworkRecommenderBlacklistSize = size;
+ }
+ }
+
+ /** Sets if the available network notification feature is enabled. */
+ public void setIsWifiNetworksAvailableNotificationEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mIsWifiNetworksAvailableNotificationOn = enabled;
+ }
+ }
+
+ /** Increments the occurence of connection attempts that were initiated unsuccessfully */
+ public void incrementNumOpenNetworkRecommendationUpdates() {
+ synchronized (mLock) {
+ mNumOpenNetworkRecommendationUpdates++;
+ }
+ }
+
+ /** Increments the occurence of connection attempts that were initiated unsuccessfully */
+ public void incrementNumOpenNetworkConnectMessageFailedToSend() {
+ synchronized (mLock) {
+ mNumOpenNetworkConnectMessageFailedToSend++;
+ }
+ }
+
public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
public static final String CLEAN_DUMP_ARG = "clean";
@@ -1488,6 +1548,19 @@ public class WifiMetrics {
+ mPnoScanMetrics.numPnoScanFailedOverOffload);
pw.println("mPnoScanMetrics.numPnoFoundNetworkEvents="
+ mPnoScanMetrics.numPnoFoundNetworkEvents);
+
+ pw.println("mWifiLogProto.connectToNetworkNotificationCount="
+ + mConnectToNetworkNotificationCount.toString());
+ pw.println("mWifiLogProto.connectToNetworkNotificationActionCount="
+ + mConnectToNetworkNotificationActionCount.toString());
+ pw.println("mWifiLogProto.openNetworkRecommenderBlacklistSize="
+ + mOpenNetworkRecommenderBlacklistSize);
+ pw.println("mWifiLogProto.isWifiNetworksAvailableNotificationOn="
+ + mIsWifiNetworksAvailableNotificationOn);
+ pw.println("mWifiLogProto.numOpenNetworkRecommendationUpdates="
+ + mNumOpenNetworkRecommendationUpdates);
+ pw.println("mWifiLogProto.numOpenNetworkConnectMessageFailedToSend="
+ + mNumOpenNetworkConnectMessageFailedToSend);
}
}
}
@@ -1698,6 +1771,53 @@ public class WifiMetrics {
mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto();
mWifiLogProto.pnoScanMetrics = mPnoScanMetrics;
+
+ /**
+ * Convert the SparseIntArray of "Connect to Network" notification types and counts to
+ * proto's repeated IntKeyVal array.
+ */
+ ConnectToNetworkNotificationAndActionCount[] notificationCountArray =
+ new ConnectToNetworkNotificationAndActionCount[
+ mConnectToNetworkNotificationCount.size()];
+ for (int i = 0; i < mConnectToNetworkNotificationCount.size(); i++) {
+ ConnectToNetworkNotificationAndActionCount keyVal =
+ new ConnectToNetworkNotificationAndActionCount();
+ keyVal.notification = mConnectToNetworkNotificationCount.keyAt(i);
+ keyVal.recommender =
+ ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN;
+ keyVal.count = mConnectToNetworkNotificationCount.valueAt(i);
+ notificationCountArray[i] = keyVal;
+ }
+ mWifiLogProto.connectToNetworkNotificationCount = notificationCountArray;
+
+ /**
+ * Convert the SparseIntArray of "Connect to Network" notification types and counts to
+ * proto's repeated IntKeyVal array.
+ */
+ ConnectToNetworkNotificationAndActionCount[] notificationActionCountArray =
+ new ConnectToNetworkNotificationAndActionCount[
+ mConnectToNetworkNotificationActionCount.size()];
+ for (int i = 0; i < mConnectToNetworkNotificationActionCount.size(); i++) {
+ ConnectToNetworkNotificationAndActionCount keyVal =
+ new ConnectToNetworkNotificationAndActionCount();
+ int key = mConnectToNetworkNotificationActionCount.keyAt(i);
+ keyVal.notification = key / CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER;
+ keyVal.action = key % CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER;
+ keyVal.recommender =
+ ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN;
+ keyVal.count = mConnectToNetworkNotificationActionCount.valueAt(i);
+ notificationActionCountArray[i] = keyVal;
+ }
+ mWifiLogProto.connectToNetworkNotificationActionCount = notificationActionCountArray;
+
+ mWifiLogProto.openNetworkRecommenderBlacklistSize =
+ mOpenNetworkRecommenderBlacklistSize;
+ mWifiLogProto.isWifiNetworksAvailableNotificationOn =
+ mIsWifiNetworksAvailableNotificationOn;
+ mWifiLogProto.numOpenNetworkRecommendationUpdates =
+ mNumOpenNetworkRecommendationUpdates;
+ mWifiLogProto.numOpenNetworkConnectMessageFailedToSend =
+ mNumOpenNetworkConnectMessageFailedToSend;
}
}
@@ -1716,7 +1836,8 @@ public class WifiMetrics {
}
/**
- * Clear all WifiMetrics, except for currentConnectionEvent.
+ * Clear all WifiMetrics, except for currentConnectionEvent and Open Network Notification
+ * feature enabled state, blacklist size.
*/
private void clear() {
synchronized (mLock) {
@@ -1747,6 +1868,10 @@ public class WifiMetrics {
mAvailableSavedPasspointProviderProfilesInScanHistogram.clear();
mAvailableSavedPasspointProviderBssidsInScanHistogram.clear();
mPnoScanMetrics.clear();
+ mConnectToNetworkNotificationCount.clear();
+ mConnectToNetworkNotificationActionCount.clear();
+ mNumOpenNetworkRecommendationUpdates = 0;
+ mNumOpenNetworkConnectMessageFailedToSend = 0;
}
}
diff --git a/com/android/server/wifi/WifiNative.java b/com/android/server/wifi/WifiNative.java
index 5b12a364..0b1719db 100644
--- a/com/android/server/wifi/WifiNative.java
+++ b/com/android/server/wifi/WifiNative.java
@@ -79,6 +79,10 @@ public class WifiNative {
return mInterfaceName;
}
+ public WifiVendorHal getVendorHal() {
+ return mWifiVendorHal;
+ }
+
/**
* Enable verbose logging for all sub modules.
*/
diff --git a/com/android/server/wifi/WifiScoreReport.java b/com/android/server/wifi/WifiScoreReport.java
index 894d57cf..324cdba8 100644
--- a/com/android/server/wifi/WifiScoreReport.java
+++ b/com/android/server/wifi/WifiScoreReport.java
@@ -49,11 +49,13 @@ public class WifiScoreReport {
ConnectedScore mConnectedScore;
ConnectedScore mAggressiveConnectedScore;
+ ConnectedScore mFancyConnectedScore;
WifiScoreReport(Context context, WifiConfigManager wifiConfigManager, Clock clock) {
mClock = clock;
mConnectedScore = new LegacyConnectedScore(context, wifiConfigManager, clock);
mAggressiveConnectedScore = new AggressiveConnectedScore(context, clock);
+ mFancyConnectedScore = new VelocityBasedConnectedScore(context, clock);
}
/**
@@ -76,6 +78,7 @@ public class WifiScoreReport {
}
mConnectedScore.reset();
mAggressiveConnectedScore.reset();
+ mFancyConnectedScore.reset();
if (mVerboseLoggingEnabled) Log.d(TAG, "reset");
}
@@ -113,18 +116,20 @@ public class WifiScoreReport {
int aggressiveHandover, WifiMetrics wifiMetrics) {
int score;
- long millis = mConnectedScore.getMillis();
+ long millis = mClock.getWallClockMillis();
mConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
+ mFancyConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
int s0 = mConnectedScore.generateScore();
int s1 = mAggressiveConnectedScore.generateScore();
+ int s2 = mFancyConnectedScore.generateScore();
if (aggressiveHandover == 0) {
score = s0;
} else {
- score = s1;
+ score = s2;
}
//sanitize boundaries
@@ -135,7 +140,7 @@ public class WifiScoreReport {
score = 0;
}
- logLinkMetrics(wifiInfo, s0, s1);
+ logLinkMetrics(wifiInfo, millis, s0, s1, s2);
//report score
if (score != wifiInfo.score) {
@@ -163,8 +168,7 @@ public class WifiScoreReport {
/**
* Data logging for dumpsys
*/
- private void logLinkMetrics(WifiInfo wifiInfo, int s0, int s1) {
- long now = mClock.getWallClockMillis();
+ private void logLinkMetrics(WifiInfo wifiInfo, long now, int s0, int s1, int s2) {
if (now < FIRST_REASONABLE_WALL_CLOCK) return;
double rssi = wifiInfo.getRssi();
int freq = wifiInfo.getFrequency();
@@ -176,10 +180,10 @@ public class WifiScoreReport {
try {
String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now));
String s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
- "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d",
+ "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d",
timestamp, mSessionNumber, rssi, freq, linkSpeed,
txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate,
- s0, s1);
+ s0, s1, s2);
mLinkMetricsHistory.add(s);
} catch (Exception e) {
Log.e(TAG, "format problem", e);
@@ -201,7 +205,7 @@ public class WifiScoreReport {
* @param args unused
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1");
+ pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1,s2");
for (String line : mLinkMetricsHistory) {
pw.println(line);
}
diff --git a/com/android/server/wifi/WifiServiceImpl.java b/com/android/server/wifi/WifiServiceImpl.java
index bb995b7d..c83f6c6b 100644
--- a/com/android/server/wifi/WifiServiceImpl.java
+++ b/com/android/server/wifi/WifiServiceImpl.java
@@ -1421,7 +1421,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
public void reconnect() {
enforceChangePermission();
mLog.info("reconnect uid=%").c(Binder.getCallingUid()).flush();
- mWifiStateMachine.reconnectCommand();
+ mWifiStateMachine.reconnectCommand(new WorkSource(Binder.getCallingUid()));
}
/**
diff --git a/com/android/server/wifi/WifiStateMachine.java b/com/android/server/wifi/WifiStateMachine.java
index faad0e2d..1f6cada7 100644
--- a/com/android/server/wifi/WifiStateMachine.java
+++ b/com/android/server/wifi/WifiStateMachine.java
@@ -1321,7 +1321,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
/**
* Initiates connection to a network specified by the user/app. This method checks if the
- * requesting app holds the WIFI_CONFIG_OVERRIDE permission.
+ * requesting app holds the NETWORK_SETTINGS permission.
*
* @param netId Id network to initiate connection.
* @param uid UID of the app requesting the connection.
@@ -1350,7 +1350,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
logi("connectToUserSelectNetwork already connecting/connected=" + netId);
} else {
mWifiConnectivityManager.prepareForForcedConnection(netId);
- startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+ startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
}
return true;
}
@@ -1873,8 +1873,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
/**
* Initiate a reconnection to AP
*/
- public void reconnectCommand() {
- sendMessage(CMD_RECONNECT);
+ public void reconnectCommand(WorkSource workSource) {
+ sendMessage(CMD_RECONNECT, workSource);
}
/**
@@ -3980,9 +3980,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
deleteNetworkConfigAndSendReply(message, true);
break;
case WifiManager.SAVE_NETWORK:
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.BUSY);
+ saveNetworkConfigAndSendReply(message);
break;
case WifiManager.START_WPS:
replyToMessage(message, WifiManager.WPS_FAILED,
@@ -4537,7 +4535,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
mEnableAutoJoinWhenAssociated = allowed;
if (!old_state && allowed && mScreenOn
&& getCurrentState() == mConnectedState) {
- mWifiConnectivityManager.forceConnectivityScan();
+ mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
}
break;
case CMD_SELECT_TX_POWER_SCENARIO:
@@ -4824,7 +4822,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
// Notify PasspointManager of Passpoint network connected event.
WifiConfiguration currentNetwork = getCurrentWifiConfiguration();
- if (currentNetwork.isPasspoint()) {
+ if (currentNetwork != null && currentNetwork.isPasspoint()) {
mPasspointManager.onPasspointNetworkConnected(currentNetwork.FQDN);
}
}
@@ -5160,7 +5158,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj));
break;
case CMD_RECONNECT:
- mWifiConnectivityManager.forceConnectivityScan();
+ WorkSource workSource = (WorkSource) message.obj;
+ mWifiConnectivityManager.forceConnectivityScan(workSource);
break;
case CMD_REASSOCIATE:
lastConnectAttemptTimestamp = mClock.getWallClockMillis();
@@ -5180,7 +5179,23 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
+ int uid = message.arg2;
bssid = (String) message.obj;
+
+ synchronized (mWifiReqCountLock) {
+ if (!hasConnectionRequests()) {
+ if (mNetworkAgent == null) {
+ loge("CMD_START_CONNECT but no requests and not connected,"
+ + " bailing");
+ break;
+ } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
+ loge("CMD_START_CONNECT but no requests and connected, but app "
+ + "does not have sufficient permissions, bailing");
+ break;
+ }
+ }
+ }
+
config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId);
logd("CMD_START_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
@@ -5270,41 +5285,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
break;
case WifiManager.SAVE_NETWORK:
- config = (WifiConfiguration) message.obj;
- mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
- if (config == null) {
- loge("SAVE_NETWORK with null configuration"
- + mSupplicantStateTracker.getSupplicantStateName()
- + " my state " + getCurrentState().getName());
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.ERROR);
- break;
- }
- result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
- if (!result.isSuccess()) {
- loge("SAVE_NETWORK adding/updating config=" + config + " failed");
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.ERROR);
- break;
- }
- if (!mWifiConfigManager.enableNetwork(
- result.getNetworkId(), false, message.sendingUid)) {
- loge("SAVE_NETWORK enabling config=" + config + " failed");
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.ERROR);
- break;
- }
+ result = saveNetworkConfigAndSendReply(message);
netId = result.getNetworkId();
- if (mWifiInfo.getNetworkId() == netId) {
+ if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
+ mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
if (result.hasCredentialChanged()) {
+ config = (WifiConfiguration) message.obj;
// The network credentials changed and we're connected to this network,
// start a new connection with the updated credentials.
logi("SAVE_NETWORK credential changed for config=" + config.configKey()
+ ", Reconnecting.");
- startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+ startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
} else {
if (result.hasProxyChanged()) {
log("Reconfiguring proxy on connection");
@@ -5322,8 +5313,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
}
}
}
- broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
- replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
break;
case WifiManager.FORGET_NETWORK:
if (!deleteNetworkConfigAndSendReply(message, true)) {
@@ -5831,8 +5820,15 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NONE,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- sendConnectedState();
- transitionTo(mConnectedState);
+ if (getCurrentWifiConfiguration() == null) {
+ // The current config may have been removed while we were connecting,
+ // trigger a disconnect to clear up state.
+ mWifiNative.disconnect();
+ transitionTo(mDisconnectingState);
+ } else {
+ sendConnectedState();
+ transitionTo(mConnectedState);
+ }
break;
case CMD_IP_CONFIGURATION_LOST:
// Get Link layer stats so that we get fresh tx packet counters.
@@ -5999,6 +5995,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
case CMD_STOP_RSSI_MONITORING_OFFLOAD:
stopRssiMonitoringOffload();
break;
+ case CMD_RECONNECT:
+ log(" Ignore CMD_RECONNECT request because wifi is already connected");
+ break;
case CMD_RESET_SIM_NETWORKS:
if (message.arg1 == 0 // sim was removed
&& mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
@@ -6142,7 +6141,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
WifiConfiguration config = getCurrentWifiConfiguration();
if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
boolean prompt =
- mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid);
+ mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
if (mVerboseLoggingEnabled) {
log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
}
@@ -6851,6 +6850,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
case WifiManager.CONNECT_NETWORK:
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
+ log(" Ignore CMD_RECONNECT request because wps is running");
+ return HANDLED;
case CMD_REASSOCIATE:
deferMessage(message);
break;
@@ -7118,14 +7119,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
* Automatically connect to the network specified
*
* @param networkId ID of the network to connect to
+ * @param uid UID of the app triggering the connection.
* @param bssid BSSID of the network
*/
- public void startConnectToNetwork(int networkId, String bssid) {
- synchronized (mWifiReqCountLock) {
- if (hasConnectionRequests()) {
- sendMessage(CMD_START_CONNECT, networkId, 0, bssid);
- }
- }
+ public void startConnectToNetwork(int networkId, int uid, String bssid) {
+ sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
/**
@@ -7210,6 +7208,43 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss
}
}
+ /**
+ * Private method to handle calling WifiConfigManager to add & enable network configs and reply
+ * to the message from the sender of the outcome.
+ *
+ * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return
+ * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error.
+ */
+ private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) {
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+ if (config == null) {
+ loge("SAVE_NETWORK with null configuration "
+ + mSupplicantStateTracker.getSupplicantStateName()
+ + " my state " + getCurrentState().getName());
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
+ if (!result.isSuccess()) {
+ loge("SAVE_NETWORK adding/updating config=" + config + " failed");
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+ return result;
+ }
+ if (!mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), false, message.sendingUid)) {
+ loge("SAVE_NETWORK enabling config=" + config + " failed");
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+ replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+ return result;
+ }
+
private static String getLinkPropertiesSummary(LinkProperties lp) {
List<String> attributes = new ArrayList<>(6);
if (lp.hasIPv4Address()) {
diff --git a/com/android/server/wifi/WifiVendorHal.java b/com/android/server/wifi/WifiVendorHal.java
index 12674aa2..e0467d78 100644
--- a/com/android/server/wifi/WifiVendorHal.java
+++ b/com/android/server/wifi/WifiVendorHal.java
@@ -67,6 +67,7 @@ import android.net.wifi.WifiWakeReasonAndCounts;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
@@ -237,6 +238,11 @@ public class WifiVendorHal {
clearState();
}
+ // TODO: b/65014872 remove - have RttService (RTT2) interact directly with HalDeviceManager
+ public IWifiRttController getRttController() {
+ return mIWifiRttController;
+ }
+
private WifiNative.VendorHalDeathEventHandler mDeathEventHandler;
/**
@@ -2582,8 +2588,25 @@ public class WifiVendorHal {
// The problem here is that the two threads acquire the locks in opposite order.
// If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2
// will be deadlocked.
- eventHandler.onRingBufferData(
- ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data));
+ int sizeBefore = data.size();
+ boolean conversionFailure = false;
+ try {
+ eventHandler.onRingBufferData(
+ ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data));
+ int sizeAfter = data.size();
+ if (sizeAfter != sizeBefore) {
+ conversionFailure = true;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ conversionFailure = true;
+ }
+ if (conversionFailure) {
+ Log.wtf("WifiVendorHal", "Conversion failure detected in "
+ + "onDebugRingBufferDataAvailable. "
+ + "The input ArrayList |data| is potentially corrupted. "
+ + "Starting size=" + sizeBefore + ", "
+ + "final size=" + data.size());
+ }
});
}
diff --git a/com/android/server/wifi/WificondControl.java b/com/android/server/wifi/WificondControl.java
index 056777fd..b6104a8c 100644
--- a/com/android/server/wifi/WificondControl.java
+++ b/com/android/server/wifi/WificondControl.java
@@ -374,8 +374,13 @@ public class WificondControl {
new InformationElementUtil.Capabilities();
capabilities.from(ies, result.capability);
String flags = capabilities.generateCapabilitiesString();
- NetworkDetail networkDetail =
- new NetworkDetail(bssid, ies, null, result.frequency);
+ NetworkDetail networkDetail;
+ try {
+ networkDetail = new NetworkDetail(bssid, ies, null, result.frequency);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e);
+ continue;
+ }
ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
result.signalMbm / 100, result.frequency, result.tsf, ies, null);
diff --git a/com/android/server/wifi/aware/WifiAwareClientState.java b/com/android/server/wifi/aware/WifiAwareClientState.java
index 3570f1d7..987d49ef 100644
--- a/com/android/server/wifi/aware/WifiAwareClientState.java
+++ b/com/android/server/wifi/aware/WifiAwareClientState.java
@@ -20,7 +20,6 @@ import android.Manifest;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.net.wifi.RttManager;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.IWifiAwareEventCallback;
import android.os.RemoteException;
@@ -271,47 +270,6 @@ public class WifiAwareClientState {
}
/**
- * Called on RTT success - forwards call to client.
- */
- public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
- if (VDBG) {
- Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
- }
- try {
- mCallback.onRangingSuccess(rangingId, results);
- } catch (RemoteException e) {
- Log.w(TAG, "onRangingSuccess: RemoteException - ignored: " + e);
- }
- }
-
- /**
- * Called on RTT failure - forwards call to client.
- */
- public void onRangingFailure(int rangingId, int reason, String description) {
- if (VDBG) {
- Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
- + ", description=" + description);
- }
- try {
- mCallback.onRangingFailure(rangingId, reason, description);
- } catch (RemoteException e) {
- Log.w(TAG, "onRangingFailure: RemoteException - ignored: " + e);
- }
- }
-
- /**
- * Called on RTT operation aborted - forwards call to client.
- */
- public void onRangingAborted(int rangingId) {
- if (VDBG) Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId);
- try {
- mCallback.onRangingAborted(rangingId);
- } catch (RemoteException e) {
- Log.w(TAG, "onRangingAborted: RemoteException - ignored: " + e);
- }
- }
-
- /**
* Dump the internal state of the class.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/com/android/server/wifi/aware/WifiAwareRttStateManager.java b/com/android/server/wifi/aware/WifiAwareRttStateManager.java
deleted file mode 100644
index 9d0441f1..00000000
--- a/com/android/server/wifi/aware/WifiAwareRttStateManager.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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 com.android.server.wifi.aware;
-
-import android.content.Context;
-import android.net.wifi.IRttManager;
-import android.net.wifi.RttManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-
-/**
- * Manages interactions between the Aware and the RTT service. Duplicates some of the functionality
- * of the RttManager.
- */
-public class WifiAwareRttStateManager {
- private static final String TAG = "WifiAwareRttStateMgr";
-
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- private final SparseArray<WifiAwareClientState> mPendingOperations = new SparseArray<>();
- private AsyncChannel mAsyncChannel;
- private Context mContext;
-
- /**
- * Initializes the connection to the RTT service.
- */
- public void start(Context context, Looper looper) {
- if (VDBG) Log.v(TAG, "start()");
-
- IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE);
- IRttManager service = IRttManager.Stub.asInterface(b);
- if (service == null) {
- Log.e(TAG, "start(): not able to get WIFI_RTT_SERVICE");
- return;
- }
-
- startWithRttService(context, looper, service);
- }
-
- /**
- * Initializes the connection to the RTT service.
- */
- @VisibleForTesting
- public void startWithRttService(Context context, Looper looper, IRttManager service) {
- Messenger messenger;
- try {
- messenger = service.getMessenger(null, new int[1]);
- } catch (RemoteException e) {
- Log.e(TAG, "start(): not able to getMessenger() of WIFI_RTT_SERVICE");
- return;
- }
-
- mAsyncChannel = new AsyncChannel();
- mAsyncChannel.connect(context, new AwareRttHandler(looper), messenger);
- mContext = context;
- }
-
- private WifiAwareClientState getAndRemovePendingOperationClient(int rangingId) {
- WifiAwareClientState client = mPendingOperations.get(rangingId);
- mPendingOperations.delete(rangingId);
- return client;
- }
-
- /**
- * Start a ranging operation for the client + peer MAC.
- */
- public void startRanging(int rangingId, WifiAwareClientState client,
- RttManager.RttParams[] params) {
- if (VDBG) {
- Log.v(TAG, "startRanging: rangingId=" + rangingId + ", parms="
- + Arrays.toString(params));
- }
-
- if (mAsyncChannel == null) {
- Log.d(TAG, "startRanging(): AsyncChannel to RTT service not configured - failing");
- client.onRangingFailure(rangingId, RttManager.REASON_NOT_AVAILABLE,
- "Aware service not able to configure connection to RTT service");
- return;
- }
-
- mPendingOperations.put(rangingId, client);
- RttManager.ParcelableRttParams pparams = new RttManager.ParcelableRttParams(params);
- mAsyncChannel.sendMessage(RttManager.CMD_OP_START_RANGING, 0, rangingId, pparams);
- }
-
- private class AwareRttHandler extends Handler {
- AwareRttHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (VDBG) Log.v(TAG, "handleMessage(): " + msg.what);
-
- // channel configuration messages
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttManager.RttClient(mContext.getPackageName()));
- } else {
- Log.e(TAG, "Failed to set up channel connection to RTT service");
- mAsyncChannel = null;
- }
- return;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- /* NOP */
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection to RTT service lost");
- mAsyncChannel = null;
- return;
- }
-
- // RTT-specific messages
- WifiAwareClientState client = getAndRemovePendingOperationClient(msg.arg2);
- if (client == null) {
- Log.e(TAG, "handleMessage(): RTT message (" + msg.what
- + ") -- cannot find registered pending operation client for ID "
- + msg.arg2);
- return;
- }
-
- switch (msg.what) {
- case RttManager.CMD_OP_SUCCEEDED: {
- int rangingId = msg.arg2;
- RttManager.ParcelableRttResults results = (RttManager.ParcelableRttResults)
- msg.obj;
- if (VDBG) {
- Log.v(TAG, "CMD_OP_SUCCEEDED: rangingId=" + rangingId + ", results="
- + results);
- }
- for (int i = 0; i < results.mResults.length; ++i) {
- /*
- * TODO: store peer ID rather than null in the return result.
- */
- results.mResults[i].bssid = null;
- }
- client.onRangingSuccess(rangingId, results);
- break;
- }
- case RttManager.CMD_OP_FAILED: {
- int rangingId = msg.arg2;
- int reason = msg.arg1;
- String description = ((Bundle) msg.obj).getString(RttManager.DESCRIPTION_KEY);
- if (VDBG) {
- Log.v(TAG, "CMD_OP_FAILED: rangingId=" + rangingId + ", reason=" + reason
- + ", description=" + description);
- }
- client.onRangingFailure(rangingId, reason, description);
- break;
- }
- case RttManager.CMD_OP_ABORTED: {
- int rangingId = msg.arg2;
- if (VDBG) {
- Log.v(TAG, "CMD_OP_ABORTED: rangingId=" + rangingId);
- }
- client.onRangingAborted(rangingId);
- break;
- }
- default:
- Log.e(TAG, "handleMessage(): ignoring message " + msg.what);
- break;
- }
- }
- }
-
- /**
- * Dump the internal state of the class.
- */
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("WifiAwareRttStateManager:");
- pw.println(" mPendingOperations: [" + mPendingOperations + "]");
- }
-}
diff --git a/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index b77ae635..421d9ac4 100644
--- a/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -16,15 +16,16 @@
package com.android.server.wifi.aware;
+import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.NanStatusType;
-import android.net.wifi.RttManager;
import android.net.wifi.aware.Characteristics;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.DiscoverySession;
import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareMacAddressProvider;
import android.net.wifi.aware.IWifiAwareManager;
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
@@ -42,7 +43,7 @@ import com.android.server.wifi.util.WifiPermissionsWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
+import java.util.List;
/**
* Implementation of the IWifiAwareManager AIDL interface. Performs validity
@@ -63,7 +64,6 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
new SparseArray<>();
private int mNextClientId = 1;
- private int mNextRangingId = 1;
private final SparseIntArray mUidByClientId = new SparseIntArray();
public WifiAwareServiceImpl(Context context) {
@@ -134,7 +134,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
}
if (configRequest != null) {
- enforceConnectivityInternalPermission();
+ enforceNetworkStackPermission();
} else {
configRequest = new ConfigRequest.Builder().build();
}
@@ -326,7 +326,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
enforceChangePermission();
if (retryCount != 0) {
- enforceConnectivityInternalPermission();
+ enforceNetworkStackPermission();
}
if (message != null
@@ -352,30 +352,10 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
}
@Override
- public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) {
- enforceAccessPermission();
- enforceLocationPermission();
+ public void requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback) {
+ enforceNetworkStackPermission();
- // TODO: b/35676064 restricts access to this API until decide if will open.
- enforceConnectivityInternalPermission();
-
- int uid = getMockableCallingUid();
- enforceClientValidity(uid, clientId);
- if (VDBG) {
- Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
- + ", parms=" + Arrays.toString(params.mParams));
- }
-
- if (params.mParams.length == 0) {
- throw new IllegalArgumentException("Empty ranging parameters");
- }
-
- int rangingId;
- synchronized (mLock) {
- rangingId = mNextRangingId++;
- }
- mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId);
- return rangingId;
+ mStateManager.requestMacAddresses(uid, peerIds, callback);
}
@Override
@@ -424,8 +404,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
TAG);
}
- private void enforceConnectivityInternalPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
- TAG);
+ private void enforceNetworkStackPermission() {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_STACK, TAG);
}
}
diff --git a/com/android/server/wifi/aware/WifiAwareStateManager.java b/com/android/server/wifi/aware/WifiAwareStateManager.java
index 6ced9486..31bdff8c 100644
--- a/com/android/server/wifi/aware/WifiAwareStateManager.java
+++ b/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -21,11 +21,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.wifi.V1_0.NanStatusType;
-import android.net.wifi.RttManager;
import android.net.wifi.aware.Characteristics;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareMacAddressProvider;
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.net.wifi.aware.WifiAwareManager;
@@ -48,7 +48,6 @@ import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
-import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
import libcore.util.HexEncoding;
@@ -62,6 +61,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -108,7 +108,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
private static final int COMMAND_TYPE_ENQUEUE_SEND_MESSAGE = 107;
private static final int COMMAND_TYPE_ENABLE_USAGE = 108;
private static final int COMMAND_TYPE_DISABLE_USAGE = 109;
- private static final int COMMAND_TYPE_START_RANGING = 110;
private static final int COMMAND_TYPE_GET_CAPABILITIES = 111;
private static final int COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES = 112;
private static final int COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES = 113;
@@ -167,7 +166,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
private static final String MESSAGE_BUNDLE_KEY_MAC_ADDRESS = "mac_address";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_DATA = "message_data";
private static final String MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID = "req_instance_id";
- private static final String MESSAGE_BUNDLE_KEY_RANGING_ID = "ranging_id";
private static final String MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME = "message_queue_time";
private static final String MESSAGE_BUNDLE_KEY_RETRY_COUNT = "retry_count";
private static final String MESSAGE_BUNDLE_KEY_SUCCESS_FLAG = "success_flag";
@@ -203,7 +201,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
private volatile Capabilities mCapabilities;
private volatile Characteristics mCharacteristics = null;
private WifiAwareStateMachine mSm;
- private WifiAwareRttStateManager mRtt;
public WifiAwareDataPathStateManager mDataPathMgr;
private PowerManager mPowerManager;
@@ -348,7 +345,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
mSm.setDbg(DBG);
mSm.start();
- mRtt = new WifiAwareRttStateManager();
mDataPathMgr = new WifiAwareDataPathStateManager(this);
mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics,
permissionsWrapper);
@@ -417,6 +413,48 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
}
/*
+ * Cross-service API: synchronized but independent of state machine
+ */
+
+ /**
+ * Translate (and return in the callback) the peerId to its MAC address representation.
+ */
+ public void requestMacAddresses(int uid, List<Integer> peerIds,
+ IWifiAwareMacAddressProvider callback) {
+ mSm.getHandler().post(() -> {
+ if (VDBG) Log.v(TAG, "requestMacAddresses: uid=" + uid + ", peerIds=" + peerIds);
+ Map<Integer, byte[]> peerIdToMacMap = new HashMap<>();
+ for (int i = 0; i < mClients.size(); ++i) {
+ WifiAwareClientState client = mClients.valueAt(i);
+ if (client.getUid() != uid) {
+ continue;
+ }
+
+ SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
+ for (int j = 0; j < sessions.size(); ++j) {
+ WifiAwareDiscoverySessionState session = sessions.valueAt(j);
+
+ for (int peerId : peerIds) {
+ WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo(
+ peerId);
+ if (peerInfo != null) {
+ peerIdToMacMap.put(peerId, peerInfo.mMac);
+ }
+ }
+ }
+ }
+
+ try {
+ if (VDBG) Log.v(TAG, "requestMacAddresses: peerIdToMacMap=" + peerIdToMacMap);
+ callback.macAddress(peerIdToMacMap);
+ } catch (RemoteException e) {
+ Log.e(TAG, "requestMacAddress (sync): exception on callback -- " + e);
+
+ }
+ });
+ }
+
+ /*
* COMMANDS
*/
@@ -553,20 +591,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
}
/**
- * Place a request to range a peer on the discovery session on the state machine queue.
- */
- public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params,
- int rangingId) {
- Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
- msg.arg1 = COMMAND_TYPE_START_RANGING;
- msg.arg2 = clientId;
- msg.obj = params;
- msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
- msg.getData().putInt(MESSAGE_BUNDLE_KEY_RANGING_ID, rangingId);
- mSm.sendMessage(msg);
- }
-
- /**
* Enable usage of Aware. Doesn't actually turn on Aware (form clusters) - that
* only happens when a connection is created.
*/
@@ -1525,18 +1549,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
case COMMAND_TYPE_DISABLE_USAGE:
waitForResponse = disableUsageLocal(mCurrentTransactionId);
break;
- case COMMAND_TYPE_START_RANGING: {
- Bundle data = msg.getData();
-
- int clientId = msg.arg2;
- RttManager.RttParams[] params = (RttManager.RttParams[]) msg.obj;
- int sessionId = data.getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
- int rangingId = data.getInt(MESSAGE_BUNDLE_KEY_RANGING_ID);
-
- startRangingLocal(clientId, sessionId, params, rangingId);
- waitForResponse = false;
- break;
- }
case COMMAND_TYPE_GET_CAPABILITIES:
if (mCapabilities == null) {
waitForResponse = mWifiAwareNativeApi.getCapabilities(
@@ -1614,7 +1626,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
break;
case COMMAND_TYPE_DELAYED_INITIALIZATION:
mWifiAwareNativeManager.start();
- mRtt.start(mContext, mSm.getHandler().getLooper());
waitForResponse = false;
break;
default:
@@ -1827,9 +1838,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
case COMMAND_TYPE_DISABLE_USAGE:
Log.wtf(TAG, "processTimeout: DISABLE_USAGE - shouldn't be waiting!");
break;
- case COMMAND_TYPE_START_RANGING:
- Log.wtf(TAG, "processTimeout: START_RANGING - shouldn't be waiting!");
- break;
case COMMAND_TYPE_GET_CAPABILITIES:
Log.e(TAG,
"processTimeout: GET_CAPABILITIES timed-out - strange, will try again"
@@ -2308,49 +2316,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
return callDispatched;
}
- private void startRangingLocal(int clientId, int sessionId, RttManager.RttParams[] params,
- int rangingId) {
- if (VDBG) {
- Log.v(TAG, "startRangingLocal: clientId=" + clientId + ", sessionId=" + sessionId
- + ", parms=" + Arrays.toString(params) + ", rangingId=" + rangingId);
- }
-
- WifiAwareClientState client = mClients.get(clientId);
- if (client == null) {
- Log.e(TAG, "startRangingLocal: no client exists for clientId=" + clientId);
- return;
- }
-
- WifiAwareDiscoverySessionState session = client.getSession(sessionId);
- if (session == null) {
- Log.e(TAG, "startRangingLocal: no session exists for clientId=" + clientId
- + ", sessionId=" + sessionId);
- client.onRangingFailure(rangingId, RttManager.REASON_INVALID_REQUEST,
- "Invalid session ID");
- return;
- }
-
- for (RttManager.RttParams param : params) {
- String peerIdStr = param.bssid;
- try {
- WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo(
- Integer.parseInt(peerIdStr));
- if (peerInfo == null || peerInfo.mMac == null) {
- Log.d(TAG, "startRangingLocal: no MAC address for peer ID=" + peerIdStr);
- param.bssid = "";
- } else {
- param.bssid = NativeUtil.macAddressFromByteArray(peerInfo.mMac);
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "startRangingLocal: invalid peer ID specification (in bssid field): '"
- + peerIdStr + "'");
- param.bssid = "";
- }
- }
-
- mRtt.startRanging(rangingId, client, params);
- }
-
private boolean initiateDataPathSetupLocal(short transactionId,
WifiAwareNetworkSpecifier networkSpecifier, int peerId, int channelRequestType,
int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase,
@@ -3061,7 +3026,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe
}
pw.println(" mSettableParameters: " + mSettableParameters);
mSm.dump(fd, pw, args);
- mRtt.dump(fd, pw, args);
mDataPathMgr.dump(fd, pw, args);
mWifiAwareNativeApi.dump(fd, pw, args);
}
diff --git a/com/android/server/wifi/rtt/RttNative.java b/com/android/server/wifi/rtt/RttNative.java
new file mode 100644
index 00000000..d30fe915
--- /dev/null
+++ b/com/android/server/wifi/rtt/RttNative.java
@@ -0,0 +1,248 @@
+/*
+ * 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.wifi.rtt;
+
+import android.hardware.wifi.V1_0.IWifiRttController;
+import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
+import android.hardware.wifi.V1_0.RttBw;
+import android.hardware.wifi.V1_0.RttConfig;
+import android.hardware.wifi.V1_0.RttPeerType;
+import android.hardware.wifi.V1_0.RttPreamble;
+import android.hardware.wifi.V1_0.RttResult;
+import android.hardware.wifi.V1_0.RttStatus;
+import android.hardware.wifi.V1_0.RttType;
+import android.hardware.wifi.V1_0.WifiChannelWidthInMhz;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.wifi.ScanResult;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.wifi.HalDeviceManager;
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.WifiVendorHal;
+import com.android.server.wifi.util.NativeUtil;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TBD
+ */
+public class RttNative extends IWifiRttControllerEventCallback.Stub {
+ private static final String TAG = "RttNative";
+ private static final boolean VDBG = true; // STOPSHIP if true
+
+ private final RttServiceImpl mRttService;
+ private final HalDeviceManager mHalDeviceManager;
+ private final WifiVendorHal mWifiVendorHal;
+
+ private boolean mIsInitialized = false;
+
+ public RttNative(RttServiceImpl rttService, HalDeviceManager halDeviceManager,
+ WifiNative wifiNative) {
+ mRttService = rttService;
+ mHalDeviceManager = halDeviceManager;
+ mWifiVendorHal = wifiNative.getVendorHal();
+ }
+
+ /**
+ * Issue a range request to the HAL.
+ *
+ * @param cmdId Command ID for the request. Will be used in the corresponding
+ * {@link #onResults(int, ArrayList)}.
+ * @param request Range request.
+ * @return Success status: true for success, false for failure.
+ */
+ public boolean rangeRequest(int cmdId, RangingRequest request) {
+ if (VDBG) Log.v(TAG, "rangeRequest: cmdId=" + cmdId + ", request=" + request);
+ // TODO: b/65014872 replace by direct access to HalDeviceManager
+ IWifiRttController rttController = mWifiVendorHal.getRttController();
+ if (rttController == null) {
+ Log.e(TAG, "rangeRequest: RttController is null");
+ return false;
+ }
+ if (!mIsInitialized) {
+ try {
+ WifiStatus status = rttController.registerEventCallback(this);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG,
+ "rangeRequest: cannot register event callback -- code=" + status.code);
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "rangeRequest: exception registering callback: " + e);
+ return false;
+ }
+ mIsInitialized = true;
+ }
+
+ ArrayList<RttConfig> rttConfig = convertRangingRequestToRttConfigs(request);
+ if (rttConfig == null) {
+ Log.e(TAG, "rangeRequest: invalid request parameters");
+ return false;
+ }
+
+ try {
+ WifiStatus status = rttController.rangeRequest(cmdId, rttConfig);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "rangeRequest: cannot issue range request -- code=" + status.code);
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "rangeRequest: exception issuing range request: " + e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Callback from HAL with range results.
+ *
+ * @param cmdId Command ID specified in the original request
+ * {@link #rangeRequest(int, RangingRequest)}.
+ * @param halResults A list of range results.
+ */
+ @Override
+ public void onResults(int cmdId, ArrayList<RttResult> halResults) {
+ if (VDBG) Log.v(TAG, "onResults: cmdId=" + cmdId + ", # of results=" + halResults.size());
+ List<RangingResult> results = new ArrayList<>(halResults.size());
+
+ for (RttResult halResult: halResults) {
+ results.add(new RangingResult(
+ halResult.status == RttStatus.SUCCESS ? RangingResultCallback.STATUS_SUCCESS
+ : RangingResultCallback.STATUS_FAIL, halResult.addr,
+ halResult.distanceInMm / 10, halResult.distanceSdInMm / 10, halResult.rssi,
+ halResult.timeStampInUs));
+ }
+
+ mRttService.onRangingResults(cmdId, results);
+ }
+
+ private static ArrayList<RttConfig> convertRangingRequestToRttConfigs(RangingRequest request) {
+ ArrayList<RttConfig> rttConfigs = new ArrayList<>(request.mRttPeers.size());
+
+ // Skipping any configurations which have an error (printing out a message).
+ // The caller will only get results for valid configurations.
+ for (RangingRequest.RttPeer peer: request.mRttPeers) {
+ if (peer instanceof RangingRequest.RttPeerAp) {
+ ScanResult scanResult = ((RangingRequest.RttPeerAp) peer).scanResult;
+ RttConfig config = new RttConfig();
+
+ byte[] addr = NativeUtil.macAddressToByteArray(scanResult.BSSID);
+ if (addr.length != config.addr.length) {
+ Log.e(TAG, "Invalid configuration: unexpected BSSID length -- " + scanResult);
+ continue;
+ }
+ for (int i = 0; i < config.addr.length; ++i) {
+ config.addr[i] = addr[i];
+ }
+
+ try {
+ config.type =
+ scanResult.is80211mcResponder() ? RttType.TWO_SIDED : RttType.ONE_SIDED;
+ config.peer = RttPeerType.AP;
+ config.channel.width = halChannelWidthFromScanResult(
+ scanResult.channelWidth);
+ config.channel.centerFreq = scanResult.frequency;
+ if (scanResult.centerFreq0 > 0) {
+ config.channel.centerFreq0 = scanResult.centerFreq0;
+ }
+ if (scanResult.centerFreq1 > 0) {
+ config.channel.centerFreq1 = scanResult.centerFreq1;
+ }
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 0;
+ config.numRetriesPerFtmr = 0;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstDuration = 15;
+ if (config.channel.centerFreq > 5000) {
+ config.preamble = RttPreamble.VHT;
+ } else {
+ config.preamble = RttPreamble.HT;
+ }
+ config.bw = halChannelBandwidthFromScanResult(scanResult.channelWidth);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid configuration: " + e.getMessage());
+ continue;
+ }
+
+ rttConfigs.add(config);
+ } else {
+ Log.e(TAG, "convertRangingRequestToRttConfigs: unknown request type -- "
+ + peer.getClass().getCanonicalName());
+ return null;
+ }
+ }
+
+
+ return rttConfigs;
+ }
+
+ static int halChannelWidthFromScanResult(int scanResultChannelWidth) {
+ switch (scanResultChannelWidth) {
+ case ScanResult.CHANNEL_WIDTH_20MHZ:
+ return WifiChannelWidthInMhz.WIDTH_20;
+ case ScanResult.CHANNEL_WIDTH_40MHZ:
+ return WifiChannelWidthInMhz.WIDTH_40;
+ case ScanResult.CHANNEL_WIDTH_80MHZ:
+ return WifiChannelWidthInMhz.WIDTH_80;
+ case ScanResult.CHANNEL_WIDTH_160MHZ:
+ return WifiChannelWidthInMhz.WIDTH_160;
+ case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
+ return WifiChannelWidthInMhz.WIDTH_80P80;
+ default:
+ throw new IllegalArgumentException(
+ "halChannelWidthFromScanResult: bad " + scanResultChannelWidth);
+ }
+ }
+
+ static int halChannelBandwidthFromScanResult(int scanResultChannelWidth) {
+ switch (scanResultChannelWidth) {
+ case ScanResult.CHANNEL_WIDTH_20MHZ:
+ return RttBw.BW_20MHZ;
+ case ScanResult.CHANNEL_WIDTH_40MHZ:
+ return RttBw.BW_40MHZ;
+ case ScanResult.CHANNEL_WIDTH_80MHZ:
+ return RttBw.BW_80MHZ;
+ case ScanResult.CHANNEL_WIDTH_160MHZ:
+ return RttBw.BW_160MHZ;
+ case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
+ return RttBw.BW_160MHZ;
+ default:
+ throw new IllegalArgumentException(
+ "halChannelBandwidthFromScanResult: bad " + scanResultChannelWidth);
+ }
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("RttNative:");
+ pw.println(" mIsInitialized: " + mIsInitialized);
+ }
+}
diff --git a/com/android/server/wifi/rtt/RttService.java b/com/android/server/wifi/rtt/RttService.java
new file mode 100644
index 00000000..71e8a0ac
--- /dev/null
+++ b/com/android/server/wifi/rtt/RttService.java
@@ -0,0 +1,67 @@
+/*
+ * 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.wifi.rtt;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.server.SystemService;
+import com.android.server.wifi.HalDeviceManager;
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+
+/**
+ * TBD.
+ */
+public class RttService extends SystemService {
+ private static final String TAG = "RttService";
+ private RttServiceImpl mImpl;
+
+ public RttService(Context context) {
+ super(context);
+ mImpl = new RttServiceImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ Log.i(TAG, "Registering " + Context.WIFI_RTT2_SERVICE);
+ publishBinderService(Context.WIFI_RTT2_SERVICE, mImpl);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ Log.i(TAG, "Starting " + Context.WIFI_RTT2_SERVICE);
+
+ WifiInjector wifiInjector = WifiInjector.getInstance();
+ if (wifiInjector == null) {
+ Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!");
+ return;
+ }
+
+ HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager();
+ HandlerThread handlerThread = wifiInjector.getRttHandlerThread();
+ WifiNative wifiNative = wifiInjector.getWifiNative();
+ WifiPermissionsUtil wifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil();
+
+ RttNative rttNative = new RttNative(mImpl, halDeviceManager, wifiNative);
+ mImpl.start(handlerThread.getLooper(), rttNative, wifiPermissionsUtil);
+ }
+ }
+}
diff --git a/com/android/server/wifi/rtt/RttServiceImpl.java b/com/android/server/wifi/rtt/RttServiceImpl.java
new file mode 100644
index 00000000..d248768c
--- /dev/null
+++ b/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -0,0 +1,399 @@
+/*
+ * 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.wifi.rtt;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.IRttCallback;
+import android.net.wifi.rtt.IWifiRttManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.wifi.util.NativeUtil;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+/**
+ * Implementation of the IWifiRttManager AIDL interface and of the RttService state manager.
+ */
+public class RttServiceImpl extends IWifiRttManager.Stub {
+ private static final String TAG = "RttServiceImpl";
+ private static final boolean VDBG = true; // STOPSHIP if true
+
+ private final Context mContext;
+ private WifiPermissionsUtil mWifiPermissionsUtil;
+
+ private RttServiceSynchronized mRttServiceSynchronized;
+
+
+ public RttServiceImpl(Context context) {
+ mContext = context;
+ }
+
+ /*
+ * INITIALIZATION
+ */
+
+ /**
+ * Initializes the RTT service (usually with objects from an injector).
+ *
+ * @param looper The looper on which to synchronize operations.
+ * @param rttNative The Native interface to the HAL.
+ * @param wifiPermissionsUtil Utility for permission checks.
+ */
+ public void start(Looper looper, RttNative rttNative, WifiPermissionsUtil wifiPermissionsUtil) {
+ mWifiPermissionsUtil = wifiPermissionsUtil;
+ mRttServiceSynchronized = new RttServiceSynchronized(looper, rttNative);
+ }
+
+ /*
+ * ASYNCHRONOUS DOMAIN - can be called from different threads!
+ */
+
+ /**
+ * Proxy for the final native call of the parent class. Enables mocking of
+ * the function.
+ */
+ public int getMockableCallingUid() {
+ return getCallingUid();
+ }
+
+ /**
+ * Binder interface API to start a ranging operation. Called on binder thread, operations needs
+ * to be posted to handler thread.
+ */
+ @Override
+ public void startRanging(IBinder binder, String callingPackage, RangingRequest request,
+ IRttCallback callback) throws RemoteException {
+ if (VDBG) {
+ Log.v(TAG, "startRanging: binder=" + binder + ", callingPackage=" + callingPackage
+ + ", request=" + request + ", callback=" + callback);
+ }
+ // verify arguments
+ if (binder == null) {
+ throw new IllegalArgumentException("Binder must not be null");
+ }
+ if (request == null || request.mRttPeers == null || request.mRttPeers.size() == 0) {
+ throw new IllegalArgumentException("Request must not be null or empty");
+ }
+ for (RangingRequest.RttPeer peer: request.mRttPeers) {
+ if (peer == null) {
+ throw new IllegalArgumentException(
+ "Request must not contain empty peer specifications");
+ }
+ if (!(peer instanceof RangingRequest.RttPeerAp)) {
+ throw new IllegalArgumentException(
+ "Request contains unknown peer specification types");
+ }
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ request.enforceValidity();
+
+ final int uid = getMockableCallingUid();
+
+ // permission check
+ enforceAccessPermission();
+ enforceChangePermission();
+ enforceLocationPermission(callingPackage, uid);
+
+ // register for binder death
+ IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ if (VDBG) Log.v(TAG, "binderDied: uid=" + uid);
+ binder.unlinkToDeath(this, 0);
+
+ mRttServiceSynchronized.mHandler.post(() -> {
+ mRttServiceSynchronized.cleanUpOnClientDeath(uid);
+ });
+ }
+ };
+
+ try {
+ binder.linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error on linkToDeath - " + e);
+ }
+
+ mRttServiceSynchronized.mHandler.post(() -> {
+ mRttServiceSynchronized.queueRangingRequest(uid, binder, dr, callingPackage, request,
+ callback);
+ });
+ }
+
+ /**
+ * Called by HAL to report ranging results. Called on HAL thread - needs to post to local
+ * thread.
+ */
+ public void onRangingResults(int cmdId, List<RangingResult> results) {
+ if (VDBG) Log.v(TAG, "onRangingResults: cmdId=" + cmdId);
+ mRttServiceSynchronized.mHandler.post(() -> {
+ mRttServiceSynchronized.onRangingResults(cmdId, results);
+ });
+ }
+
+ private void enforceAccessPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
+ }
+
+ private void enforceChangePermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
+ }
+
+ private void enforceLocationPermission(String callingPackage, int uid) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ TAG);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump RttService from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ pw.println("Wi-Fi RTT Service");
+ mRttServiceSynchronized.dump(fd, pw, args);
+ }
+
+ /*
+ * SYNCHRONIZED DOMAIN
+ */
+
+ /**
+ * RTT service implementation - synchronized on a single thread. All commands should be posted
+ * to the exposed handler.
+ */
+ private class RttServiceSynchronized {
+ public Handler mHandler;
+
+ private RttNative mRttNative;
+ private int mNextCommandId = 1000;
+ private List<RttRequestInfo> mRttRequestQueue = new LinkedList<>();
+
+ RttServiceSynchronized(Looper looper, RttNative rttNative) {
+ mRttNative = rttNative;
+
+ mHandler = new Handler(looper);
+ }
+
+ private void cleanUpOnClientDeath(int uid) {
+ if (VDBG) {
+ Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid
+ + ", mRttRequestQueue=" + mRttRequestQueue);
+ }
+ ListIterator<RttRequestInfo> it = mRttRequestQueue.listIterator();
+ while (it.hasNext()) {
+ RttRequestInfo rri = it.next();
+ if (rri.uid == uid) {
+ // TODO: actually abort operation - though API is not clear or clean
+ if (rri.cmdId == 0) {
+ // Until that happens we will get results for the last operation: which is
+ // why we don't dispatch a new range request off the queue and keep the
+ // currently running operation in the queue
+ it.remove();
+ }
+ }
+ }
+
+ if (VDBG) {
+ Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid
+ + ", after cleanup - mRttRequestQueue=" + mRttRequestQueue);
+ }
+ }
+
+ private void queueRangingRequest(int uid, IBinder binder, IBinder.DeathRecipient dr,
+ String callingPackage, RangingRequest request, IRttCallback callback) {
+ RttRequestInfo newRequest = new RttRequestInfo();
+ newRequest.uid = uid;
+ newRequest.binder = binder;
+ newRequest.dr = dr;
+ newRequest.callingPackage = callingPackage;
+ newRequest.request = request;
+ newRequest.callback = callback;
+ mRttRequestQueue.add(newRequest);
+
+ if (VDBG) {
+ Log.v(TAG, "RttServiceSynchronized.queueRangingRequest: newRequest=" + newRequest);
+ }
+
+ executeNextRangingRequestIfPossible();
+ }
+
+ private void executeNextRangingRequestIfPossible() {
+ if (mRttRequestQueue.size() == 0) {
+ if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: no requests pending");
+ return;
+ }
+
+ RttRequestInfo nextRequest = mRttRequestQueue.get(0);
+ if (nextRequest.cmdId != 0) {
+ if (VDBG) {
+ Log.v(TAG, "executeNextRangingRequestIfPossible: called but a command is "
+ + "executing. topOfQueue=" + nextRequest);
+ }
+ return;
+ }
+
+ nextRequest.cmdId = mNextCommandId++;
+ startRanging(nextRequest);
+ }
+
+ private void startRanging(RttRequestInfo nextRequest) {
+ if (VDBG) {
+ Log.v(TAG, "RttServiceSynchronized.startRanging: nextRequest=" + nextRequest);
+ }
+
+ if (!mRttNative.rangeRequest(nextRequest.cmdId, nextRequest.request)) {
+ Log.w(TAG, "RttServiceSynchronized.startRanging: native rangeRequest call failed");
+ try {
+ nextRequest.callback.onRangingResults(RangingResultCallback.STATUS_FAIL, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RttServiceSynchronized.startRanging: HAL request failed, callback "
+ + "failed -- " + e);
+ }
+
+ mRttRequestQueue.remove(0);
+ executeNextRangingRequestIfPossible();
+ }
+ }
+
+ private void onRangingResults(int cmdId, List<RangingResult> results) {
+ if (mRttRequestQueue.size() == 0) {
+ Log.e(TAG, "RttServiceSynchronized.onRangingResults: no current RTT request "
+ + "pending!?");
+ return;
+ }
+ RttRequestInfo topOfQueueRequest = mRttRequestQueue.get(0);
+
+ if (VDBG) {
+ Log.v(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId
+ + ", topOfQueueRequest=" + topOfQueueRequest + ", results="
+ + Arrays.toString(results.toArray()));
+ }
+
+ if (topOfQueueRequest.cmdId != cmdId) {
+ Log.e(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId
+ + ", does not match pending RTT request cmdId=" + topOfQueueRequest.cmdId);
+ return;
+ }
+
+ boolean permissionGranted = mWifiPermissionsUtil.checkCallersLocationPermission(
+ topOfQueueRequest.callingPackage, topOfQueueRequest.uid);
+ try {
+ if (permissionGranted) {
+ addMissingEntries(topOfQueueRequest.request, results);
+ topOfQueueRequest.callback.onRangingResults(
+ RangingResultCallback.STATUS_SUCCESS, results);
+ } else {
+ Log.w(TAG, "RttServiceSynchronized.onRangingResults: location permission "
+ + "revoked - not forwarding results");
+ topOfQueueRequest.callback.onRangingResults(RangingResultCallback.STATUS_FAIL,
+ null);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "RttServiceSynchronized.onRangingResults: callback exception -- " + e);
+ }
+
+ // clean-up binder death listener: the callback for results is a onetime event - now
+ // done with the binder.
+ topOfQueueRequest.binder.unlinkToDeath(topOfQueueRequest.dr, 0);
+
+ mRttRequestQueue.remove(0);
+ executeNextRangingRequestIfPossible();
+ }
+
+ /*
+ * Make sure the results contain an entry for each request. Add results with FAIL status
+ * if missing.
+ */
+ private void addMissingEntries(RangingRequest request,
+ List<RangingResult> results) {
+ Set<String> resultEntries = new HashSet<>(results.size());
+ for (RangingResult result: results) {
+ resultEntries.add(new String(HexEncoding.encode(result.getMacAddress())));
+ }
+
+ for (RangingRequest.RttPeer peer: request.mRttPeers) {
+ byte[] addr;
+ if (peer instanceof RangingRequest.RttPeerAp) {
+ addr = NativeUtil.macAddressToByteArray(
+ ((RangingRequest.RttPeerAp) peer).scanResult.BSSID);
+ } else {
+ continue;
+ }
+ String canonicString = new String(HexEncoding.encode(addr));
+
+ if (!resultEntries.contains(canonicString)) {
+ if (VDBG) {
+ Log.v(TAG, "padRangingResultsWithMissingResults: missing=" + canonicString);
+ }
+ results.add(new RangingResult(RangingResultCallback.STATUS_FAIL, addr, 0, 0, 0,
+ 0));
+ }
+ }
+ }
+
+ // dump call (asynchronous most likely)
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(" mNextCommandId: " + mNextCommandId);
+ pw.println(" mRttRequestQueue: " + mRttRequestQueue);
+ mRttNative.dump(fd, pw, args);
+ }
+ }
+
+ private static class RttRequestInfo {
+ public int uid;
+ public IBinder binder;
+ public IBinder.DeathRecipient dr;
+ public String callingPackage;
+ public RangingRequest request;
+ public byte[] mac;
+ public IRttCallback callback;
+
+ public int cmdId = 0; // uninitialized cmdId value
+
+ @Override
+ public String toString() {
+ return new StringBuilder("RttRequestInfo: uid=").append(uid).append(", binder=").append(
+ binder).append(", dr=").append(dr).append(", callingPackage=").append(
+ callingPackage).append(", request=").append(request.toString()).append(
+ ", callback=").append(callback).append(", cmdId=").append(cmdId).toString();
+ }
+ }
+}
diff --git a/com/android/server/wifi/scanner/ChannelHelper.java b/com/android/server/wifi/scanner/ChannelHelper.java
index d87df072..6a01f0c8 100644
--- a/com/android/server/wifi/scanner/ChannelHelper.java
+++ b/com/android/server/wifi/scanner/ChannelHelper.java
@@ -242,7 +242,7 @@ public abstract class ChannelHelper {
if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
return toString(scanSettings.channels);
} else {
- return toString(scanSettings.band);
+ return bandToString(scanSettings.band);
}
}
@@ -255,7 +255,7 @@ public abstract class ChannelHelper {
if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
return toString(bucketSettings.channels, bucketSettings.num_channels);
} else {
- return toString(bucketSettings.band);
+ return bandToString(bucketSettings.band);
}
}
@@ -293,7 +293,10 @@ public abstract class ChannelHelper {
return sb.toString();
}
- private static String toString(int band) {
+ /**
+ * Converts a WifiScanner.WIFI_BAND_* constant to a meaningful String
+ */
+ public static String bandToString(int band) {
switch (band) {
case WifiScanner.WIFI_BAND_UNSPECIFIED:
return "unspecified";
@@ -310,7 +313,6 @@ public abstract class ChannelHelper {
case WifiScanner.WIFI_BAND_BOTH_WITH_DFS:
return "24Ghz & 5Ghz (DFS incl)";
}
-
return "invalid band";
}
}
diff --git a/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/com/android/server/wifi/scanner/HalWifiScannerImpl.java
index 7d0ccbaf..890f72e6 100644
--- a/com/android/server/wifi/scanner/HalWifiScannerImpl.java
+++ b/com/android/server/wifi/scanner/HalWifiScannerImpl.java
@@ -131,11 +131,6 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb
}
@Override
- public boolean shouldScheduleBackgroundScanForHwPno() {
- return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno();
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mWificondScannerDelegate.dump(fd, pw, args);
}
diff --git a/com/android/server/wifi/scanner/WifiScannerImpl.java b/com/android/server/wifi/scanner/WifiScannerImpl.java
index 5281b3ab..dacb0074 100644
--- a/com/android/server/wifi/scanner/WifiScannerImpl.java
+++ b/com/android/server/wifi/scanner/WifiScannerImpl.java
@@ -155,11 +155,5 @@ public abstract class WifiScannerImpl {
*/
public abstract boolean isHwPnoSupported(boolean isConnectedPno);
- /**
- * This returns whether a background scan should be running for HW PNO scan or not.
- * @return true if background scan needs to be started, false otherwise.
- */
- public abstract boolean shouldScheduleBackgroundScanForHwPno();
-
protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
}
diff --git a/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index 4b8e284c..02462f67 100644
--- a/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -75,7 +75,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub {
private static final String TAG = WifiScanningService.TAG;
private static final boolean DBG = false;
- private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms
private static final int UNKNOWN_PID = -1;
private final LocalLog mLocalLog = new LocalLog(512);
@@ -240,10 +239,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub {
private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0;
private static final int CMD_FULL_SCAN_RESULTS = BASE + 1;
- private static final int CMD_HOTLIST_AP_FOUND = BASE + 2;
- private static final int CMD_HOTLIST_AP_LOST = BASE + 3;
- private static final int CMD_WIFI_CHANGE_DETECTED = BASE + 4;
- private static final int CMD_WIFI_CHANGE_TIMEOUT = BASE + 5;
private static final int CMD_DRIVER_LOADED = BASE + 6;
private static final int CMD_DRIVER_UNLOADED = BASE + 7;
private static final int CMD_SCAN_PAUSED = BASE + 8;
@@ -1724,10 +1719,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub {
}
logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
- // HW PNO is supported, check if we need a background scan running for this.
- if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) {
- addBackgroundScanRequest(scanSettings);
- }
+
return true;
}
@@ -2213,7 +2205,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub {
static String describeTo(StringBuilder sb, ScanSettings scanSettings) {
sb.append("ScanSettings { ")
- .append(" band:").append(scanSettings.band)
+ .append(" band:").append(ChannelHelper.bandToString(scanSettings.band))
.append(" period:").append(scanSettings.periodInMs)
.append(" reportEvents:").append(scanSettings.reportEvents)
.append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan)
diff --git a/com/android/server/wifi/scanner/WificondScannerImpl.java b/com/android/server/wifi/scanner/WificondScannerImpl.java
index 12a0bdee..fb878e67 100644
--- a/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -34,7 +34,6 @@ import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -50,7 +49,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
private static final String TAG = "WificondScannerImpl";
private static final boolean DBG = false;
- public static final String BACKGROUND_PERIOD_ALARM_TAG = TAG + " Background Scan Period";
public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout";
// Max number of networks that can be specified to wificond per scan request
public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16;
@@ -69,20 +67,9 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
private final Object mSettingsLock = new Object();
// Next scan settings to apply when the previous scan completes
- private WifiNative.ScanSettings mPendingBackgroundScanSettings = null;
- private WifiNative.ScanEventHandler mPendingBackgroundScanEventHandler = null;
private WifiNative.ScanSettings mPendingSingleScanSettings = null;
private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null;
- // Active background scan settings/state
- private WifiNative.ScanSettings mBackgroundScanSettings = null;
- private WifiNative.ScanEventHandler mBackgroundScanEventHandler = null;
- private int mNextBackgroundScanPeriod = 0;
- private int mNextBackgroundScanId = 0;
- private boolean mBackgroundScanPeriodPending = false;
- private boolean mBackgroundScanPaused = false;
- private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY);
-
private ArrayList<ScanDetail> mNativeScanResults;
private WifiScanner.ScanData mLatestSingleScanResult =
new WifiScanner.ScanData(0, 0, new ScanResult[0]);
@@ -110,14 +97,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
*/
private static final long SCAN_TIMEOUT_MS = 15000;
- AlarmManager.OnAlarmListener mScanPeriodListener = new AlarmManager.OnAlarmListener() {
- public void onAlarm() {
- synchronized (mSettingsLock) {
- handleScanPeriod();
- }
- }
- };
-
AlarmManager.OnAlarmListener mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
public void onAlarm() {
synchronized (mSettingsLock) {
@@ -161,7 +140,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
mPendingSingleScanSettings = null;
mPendingSingleScanEventHandler = null;
stopHwPnoScan();
- stopBatchedScan();
mLastScanSettings = null; // finally clear any active scan
}
}
@@ -210,111 +188,23 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
@Override
public boolean startBatchedScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
- if (settings == null || eventHandler == null) {
- Log.w(TAG, "Invalid arguments for startBatched: settings=" + settings
- + ",eventHandler=" + eventHandler);
- return false;
- }
-
- if (settings.max_ap_per_scan < 0 || settings.max_ap_per_scan > MAX_APS_PER_SCAN) {
- return false;
- }
- if (settings.num_buckets < 0 || settings.num_buckets > MAX_SCAN_BUCKETS) {
- return false;
- }
- if (settings.report_threshold_num_scans < 0
- || settings.report_threshold_num_scans > SCAN_BUFFER_CAPACITY) {
- return false;
- }
- if (settings.report_threshold_percent < 0 || settings.report_threshold_percent > 100) {
- return false;
- }
- if (settings.base_period_ms <= 0) {
- return false;
- }
- for (int i = 0; i < settings.num_buckets; ++i) {
- WifiNative.BucketSettings bucket = settings.buckets[i];
- if (bucket.period_ms % settings.base_period_ms != 0) {
- return false;
- }
- }
-
- synchronized (mSettingsLock) {
- stopBatchedScan();
- if (DBG) {
- Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period="
- + settings.base_period_ms + " ms");
- }
- mPendingBackgroundScanSettings = settings;
- mPendingBackgroundScanEventHandler = eventHandler;
- handleScanPeriod(); // Try to start scan immediately
- return true;
- }
+ Log.w(TAG, "startBatchedScan() is not supported");
+ return false;
}
@Override
public void stopBatchedScan() {
- synchronized (mSettingsLock) {
- if (DBG) Log.d(TAG, "Stopping scan");
- mBackgroundScanSettings = null;
- mBackgroundScanEventHandler = null;
- mPendingBackgroundScanSettings = null;
- mPendingBackgroundScanEventHandler = null;
- mBackgroundScanPaused = false;
- mBackgroundScanPeriodPending = false;
- unscheduleScansLocked();
- }
- processPendingScans();
+ Log.w(TAG, "stopBatchedScan() is not supported");
}
@Override
public void pauseBatchedScan() {
- synchronized (mSettingsLock) {
- if (DBG) Log.d(TAG, "Pausing scan");
- // if there isn't a pending scan then make the current scan pending
- if (mPendingBackgroundScanSettings == null) {
- mPendingBackgroundScanSettings = mBackgroundScanSettings;
- mPendingBackgroundScanEventHandler = mBackgroundScanEventHandler;
- }
- mBackgroundScanSettings = null;
- mBackgroundScanEventHandler = null;
- mBackgroundScanPeriodPending = false;
- mBackgroundScanPaused = true;
-
- unscheduleScansLocked();
-
- WifiScanner.ScanData[] results = getLatestBatchedScanResults(/* flush = */ true);
- if (mPendingBackgroundScanEventHandler != null) {
- mPendingBackgroundScanEventHandler.onScanPaused(results);
- }
- }
- processPendingScans();
+ Log.w(TAG, "pauseBatchedScan() is not supported");
}
@Override
public void restartBatchedScan() {
- synchronized (mSettingsLock) {
- if (DBG) Log.d(TAG, "Restarting scan");
- if (mPendingBackgroundScanEventHandler != null) {
- mPendingBackgroundScanEventHandler.onScanRestarted();
- }
- mBackgroundScanPaused = false;
- handleScanPeriod();
- }
- }
-
- private void unscheduleScansLocked() {
- mAlarmManager.cancel(mScanPeriodListener);
- if (mLastScanSettings != null) {
- mLastScanSettings.backgroundScanActive = false;
- }
- }
-
- private void handleScanPeriod() {
- synchronized (mSettingsLock) {
- mBackgroundScanPeriodPending = true;
- processPendingScans();
- }
+ Log.w(TAG, "restartBatchedScan() is not supported");
}
private void handleScanTimeout() {
@@ -342,56 +232,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
final LastScanSettings newScanSettings =
new LastScanSettings(mClock.getElapsedSinceBootMillis());
- // Update scan settings if there is a pending scan
- if (!mBackgroundScanPaused) {
- if (mPendingBackgroundScanSettings != null) {
- mBackgroundScanSettings = mPendingBackgroundScanSettings;
- mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler;
- mNextBackgroundScanPeriod = 0;
- mPendingBackgroundScanSettings = null;
- mPendingBackgroundScanEventHandler = null;
- mBackgroundScanPeriodPending = true;
- }
- if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) {
- int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch
- for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets;
- ++bucket_id) {
- WifiNative.BucketSettings bucket =
- mBackgroundScanSettings.buckets[bucket_id];
- if (mNextBackgroundScanPeriod % (bucket.period_ms
- / mBackgroundScanSettings.base_period_ms) == 0) {
- if ((bucket.report_events
- & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) {
- reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
- }
- if ((bucket.report_events
- & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
- reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
- }
- // only no batch if all buckets specify it
- if ((bucket.report_events
- & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {
- reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH;
- }
-
- allFreqs.addChannels(bucket);
- }
- }
- if (!allFreqs.isEmpty()) {
- newScanSettings.setBackgroundScan(mNextBackgroundScanId++,
- mBackgroundScanSettings.max_ap_per_scan, reportEvents,
- mBackgroundScanSettings.report_threshold_num_scans,
- mBackgroundScanSettings.report_threshold_percent);
- }
- mNextBackgroundScanPeriod++;
- mBackgroundScanPeriodPending = false;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.getElapsedSinceBootMillis()
- + mBackgroundScanSettings.base_period_ms,
- BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler);
- }
- }
-
if (mPendingSingleScanSettings != null) {
boolean reportFullResults = false;
ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection();
@@ -422,16 +262,26 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
mPendingSingleScanEventHandler = null;
}
- if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive)
- && !allFreqs.isEmpty()) {
- pauseHwPnoScan();
- Set<Integer> freqs = allFreqs.getScanFreqs();
- boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
+ if (newScanSettings.singleScanActive) {
+ boolean success = false;
+ Set<Integer> freqs;
+ if (!allFreqs.isEmpty()) {
+ pauseHwPnoScan();
+ freqs = allFreqs.getScanFreqs();
+ success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
+ if (!success) {
+ Log.e(TAG, "Failed to start scan, freqs=" + freqs);
+ }
+ } else {
+ // There is a scan request but no available channels could be scanned for.
+ // We regard it as a scan failure in this case.
+ Log.e(TAG, "Failed to start scan because there is "
+ + "no available channel to scan for");
+ }
if (success) {
// TODO handle scan timeout
if (DBG) {
Log.d(TAG, "Starting wifi scan for freqs=" + freqs
- + ", background=" + newScanSettings.backgroundScanActive
+ ", single=" + newScanSettings.singleScanActive);
}
mLastScanSettings = newScanSettings;
@@ -439,7 +289,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
} else {
- Log.e(TAG, "Failed to start scan, freqs=" + freqs);
// indicate scan failure async
mEventHandler.post(new Runnable() {
public void run() {
@@ -449,7 +298,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
}
}
});
- // TODO(b/27769665) background scans should be failed too if scans fail enough
}
} else if (isHwPnoScanRequired()) {
newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
@@ -512,7 +360,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
mLastScanSettings.singleScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_FAILED);
}
- // TODO(b/27769665) background scans should be failed too if scans fail enough
mLastScanSettings = null;
}
}
@@ -597,18 +444,13 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
return;
}
- if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
mNativeScanResults = mWifiNative.getScanResults();
List<ScanResult> singleScanResults = new ArrayList<>();
- List<ScanResult> backgroundScanResults = new ArrayList<>();
int numFilteredScanResults = 0;
for (int i = 0; i < mNativeScanResults.size(); ++i) {
ScanResult result = mNativeScanResults.get(i).getScanResult();
long timestamp_ms = result.timestamp / 1000; // convert us -> ms
if (timestamp_ms > mLastScanSettings.startTime) {
- if (mLastScanSettings.backgroundScanActive) {
- backgroundScanResults.add(result);
- }
if (mLastScanSettings.singleScanActive
&& mLastScanSettings.singleScanFreqs.containsChannel(
result.frequency)) {
@@ -622,49 +464,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
}
- if (mLastScanSettings.backgroundScanActive) {
- if (mBackgroundScanEventHandler != null) {
- if ((mLastScanSettings.reportEvents
- & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
- for (ScanResult scanResult : backgroundScanResults) {
- // TODO(b/27506257): Fill in correct bucketsScanned value
- mBackgroundScanEventHandler.onFullScanResult(scanResult, 0);
- }
- }
- }
-
- Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR);
- ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps,
- backgroundScanResults.size())];
- for (int i = 0; i < scanResultsArray.length; ++i) {
- scanResultsArray[i] = backgroundScanResults.get(i);
- }
-
- if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {
- // TODO(b/27506257): Fill in correct bucketsScanned value
- mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0,
- scanResultsArray));
- }
-
- if (mBackgroundScanEventHandler != null) {
- if ((mLastScanSettings.reportEvents
- & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0
- || (mLastScanSettings.reportEvents
- & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0
- || (mLastScanSettings.reportEvents
- == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
- && (mBackgroundScanBuffer.size()
- >= (mBackgroundScanBuffer.capacity()
- * mLastScanSettings.reportPercentThreshold
- / 100)
- || mBackgroundScanBuffer.size()
- >= mLastScanSettings.reportNumScansThreshold))) {
- mBackgroundScanEventHandler
- .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
- }
- }
- }
-
if (mLastScanSettings.singleScanActive
&& mLastScanSettings.singleScanEventHandler != null) {
if (mLastScanSettings.reportSingleScanFullResults) {
@@ -675,7 +474,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
}
}
Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
- mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0,
+ mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
mLastScanSettings.singleScanEventHandler
@@ -689,13 +488,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
@Override
public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) {
- synchronized (mSettingsLock) {
- WifiScanner.ScanData[] results = mBackgroundScanBuffer.get();
- if (flush) {
- mBackgroundScanBuffer.clear();
- }
- return results;
- }
+ return null;
}
private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
@@ -768,11 +561,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
}
@Override
- public boolean shouldScheduleBackgroundScanForHwPno() {
- return false;
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mSettingsLock) {
pw.println("Latest native scan results:");
@@ -813,24 +601,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
this.startTime = startTime;
}
- // Background settings
- public boolean backgroundScanActive = false;
- public int scanId;
- public int maxAps;
- public int reportEvents;
- public int reportNumScansThreshold;
- public int reportPercentThreshold;
-
- public void setBackgroundScan(int scanId, int maxAps, int reportEvents,
- int reportNumScansThreshold, int reportPercentThreshold) {
- this.backgroundScanActive = true;
- this.scanId = scanId;
- this.maxAps = maxAps;
- this.reportEvents = reportEvents;
- this.reportNumScansThreshold = reportNumScansThreshold;
- this.reportPercentThreshold = reportPercentThreshold;
- }
-
// Single scan settings
public boolean singleScanActive = false;
public boolean reportSingleScanFullResults;
@@ -859,44 +629,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call
}
}
-
- private static class ScanBuffer {
- private final ArrayDeque<WifiScanner.ScanData> mBuffer;
- private int mCapacity;
-
- ScanBuffer(int capacity) {
- mCapacity = capacity;
- mBuffer = new ArrayDeque<>(mCapacity);
- }
-
- public int size() {
- return mBuffer.size();
- }
-
- public int capacity() {
- return mCapacity;
- }
-
- public boolean isFull() {
- return size() == mCapacity;
- }
-
- public void add(WifiScanner.ScanData scanData) {
- if (isFull()) {
- mBuffer.pollFirst();
- }
- mBuffer.offerLast(scanData);
- }
-
- public void clear() {
- mBuffer.clear();
- }
-
- public WifiScanner.ScanData[] get() {
- return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]);
- }
- }
-
/**
* HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO
* state too often which is not handled very well by some drivers.
diff --git a/com/android/server/wifi/util/KalmanFilter.java b/com/android/server/wifi/util/KalmanFilter.java
new file mode 100644
index 00000000..b961ed80
--- /dev/null
+++ b/com/android/server/wifi/util/KalmanFilter.java
@@ -0,0 +1,62 @@
+/*
+ * 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.wifi.util;
+
+/**
+ * Utility providiing a basic Kalman filter
+ *
+ * For background, see https://en.wikipedia.org/wiki/Kalman_filter
+ */
+public class KalmanFilter {
+ public Matrix mF; // stateTransition
+ public Matrix mQ; // processNoiseCovariance
+ public Matrix mH; // observationModel
+ public Matrix mR; // observationNoiseCovariance
+ public Matrix mP; // aPosterioriErrorCovariance
+ public Matrix mx; // stateEstimate
+
+ /**
+ * Performs the prediction phase of the filter, using the state estimate to produce
+ * a new estimate for the current timestep.
+ */
+ public void predict() {
+ mx = mF.dot(mx);
+ mP = mF.dot(mP).dotTranspose(mF).plus(mQ);
+ }
+
+ /**
+ * Updates the state estimate to incorporate the new observation z.
+ */
+ public void update(Matrix z) {
+ Matrix y = z.minus(mH.dot(mx));
+ Matrix tS = mH.dot(mP).dotTranspose(mH).plus(mR);
+ Matrix tK = mP.dotTranspose(mH).dot(tS.inverse());
+ mx = mx.plus(tK.dot(y));
+ mP = mP.minus(tK.dot(mH).dot(mP));
+ }
+
+ @Override
+ public String toString() {
+ return "{F: " + mF
+ + " Q: " + mQ
+ + " H: " + mH
+ + " R: " + mR
+ + " P: " + mP
+ + " x: " + mx
+ + "}";
+ }
+}
diff --git a/com/android/server/wm/AppWindowAnimator.java b/com/android/server/wm/AppWindowAnimator.java
index c76b9059..5365e27d 100644
--- a/com/android/server/wm/AppWindowAnimator.java
+++ b/com/android/server/wm/AppWindowAnimator.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -78,11 +78,8 @@ public class AppWindowAnimator {
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
- // TODO(b/62029108): combine both members into a private one. Create a member function to set
- // the thumbnail layer to +1 to the highest layer position and replace all setter instances
- // with this function. Remove all unnecessary calls to both variables in other classes.
- int thumbnailLayer;
- int thumbnailForceAboveLayer;
+ private int mThumbnailLayer;
+
Animation thumbnailAnimation;
final Transformation thumbnailTransformation = new Transformation();
// This flag indicates that the destruction of the thumbnail surface is synchronized with
@@ -256,7 +253,7 @@ public class AppWindowAnimator {
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- thumbnailLayer = mAppToken.getHighestAnimLayer();
+ updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
@@ -280,26 +277,35 @@ public class AppWindowAnimator {
thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
"thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
- + " layer=" + thumbnailLayer
+ + " layer=" + mThumbnailLayer
+ " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ "," + tmpFloats[Matrix.MSKEW_Y]
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- if (thumbnailForceAboveLayer > 0) {
- thumbnail.setLayer(thumbnailForceAboveLayer + 1);
- } else {
- // The thumbnail is layered below the window immediately above this
- // token's anim layer.
- thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- }
+ updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
+ * Updates the thumbnail layer z order to just above the highest animation layer if changed
+ */
+ void updateThumbnailLayer() {
+ if (thumbnail != null) {
+ final int layer = mAppToken.getHighestAnimLayer();
+ if (layer != mThumbnailLayer) {
+ if (DEBUG_LAYERS) Slog.v(TAG,
+ "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
+ thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ mThumbnailLayer = layer;
+ }
+ }
+ }
+
+ /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
@@ -473,7 +479,7 @@ public class AppWindowAnimator {
}
if (thumbnail != null) {
pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(" layer="); pw.println(mThumbnailLayer);
pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
pw.print(prefix); pw.print("thumbnailTransformation=");
pw.println(thumbnailTransformation.toShortString());
diff --git a/com/android/server/wm/AppWindowToken.java b/com/android/server/wm/AppWindowToken.java
index ea2f3059..a1eeff84 100644
--- a/com/android/server/wm/AppWindowToken.java
+++ b/com/android/server/wm/AppWindowToken.java
@@ -16,8 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -53,6 +52,7 @@ import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.proto.AppWindowTokenProto.NAME;
import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.app.Activity;
import android.content.res.Configuration;
@@ -1170,7 +1170,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
wAppAnimator.thumbnail.destroy();
}
wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
tAppAnimator.thumbnail = null;
}
@@ -1311,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.getStackById(PINNED_STACK_ID);
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
if (pinnedStack != null) {
pinnedStack.onAllWindowsDrawn();
}
@@ -1620,8 +1620,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
}
+ @CallSuper
@Override
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
writeNameToProto(proto, NAME);
super.writeToProto(proto, WINDOW_TOKEN);
diff --git a/com/android/server/wm/ConfigurationContainer.java b/com/android/server/wm/ConfigurationContainer.java
index 28ba9b3d..9e028d38 100644
--- a/com/android/server/wm/ConfigurationContainer.java
+++ b/com/android/server/wm/ConfigurationContainer.java
@@ -21,12 +21,19 @@ 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_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.app.WindowConfiguration.activityTypeToString;
+import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION;
+import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
+import static com.android.server.wm.proto.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
+import android.annotation.CallSuper;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
+import android.util.proto.ProtoOutputStream;
import java.util.ArrayList;
@@ -147,6 +154,17 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
onOverrideConfigurationChanged(mTmpConfig);
}
+ /**
+ * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
+ * with another activity.
+ */
+ public boolean inMultiWindowMode() {
+ /*@WindowConfiguration.WindowingMode*/ int windowingMode =
+ mFullConfiguration.windowConfiguration.getWindowingMode();
+ return windowingMode != WINDOWING_MODE_FULLSCREEN
+ && windowingMode != WINDOWING_MODE_UNDEFINED;
+ }
+
/** Returns true if this container is currently in split-screen windowing mode. */
public boolean inSplitScreenWindowingMode() {
/*@WindowConfiguration.WindowingMode*/ int windowingMode =
@@ -170,7 +188,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on
* its current state.
*/
- public boolean supportSplitScreenWindowingMode() {
+ public boolean supportsSplitScreenWindowingMode() {
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
}
@@ -220,9 +238,48 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
/*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
- return thisType == otherType
- || thisType == ACTIVITY_TYPE_UNDEFINED
- || otherType == ACTIVITY_TYPE_UNDEFINED;
+ if (thisType == otherType) {
+ return true;
+ }
+ if (thisType == ACTIVITY_TYPE_ASSISTANT) {
+ // Assistant activities are only compatible with themselves...
+ return false;
+ }
+ // Otherwise we are compatible if us or other is not currently defined.
+ return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ /**
+ * Returns true if this container is compatible with the input windowing mode and activity type.
+ * The container is compatible:
+ * - If {@param activityType} and {@param windowingMode} match this container activity type and
+ * windowing mode.
+ * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
+ * standard or undefined and its windowing mode matches {@param windowingMode}.
+ * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
+ * also standard or undefined and its activity type matches {@param activityType} regardless of
+ * if {@param windowingMode} matches the containers windowing mode.
+ */
+ public boolean isCompatible(int windowingMode, int activityType) {
+ final int thisActivityType = getActivityType();
+ final int thisWindowingMode = getWindowingMode();
+ final boolean sameActivityType = thisActivityType == activityType;
+ final boolean sameWindowingMode = thisWindowingMode == windowingMode;
+
+ if (sameActivityType && sameWindowingMode) {
+ return true;
+ }
+
+ if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
+ || !isActivityTypeStandardOrUndefined()) {
+ // Only activity type need to match for non-standard activity types that are defined.
+ return sameActivityType;
+ }
+
+ // Otherwise we are compatible if the windowing mode is the same.
+ return sameWindowingMode;
}
public void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
@@ -252,6 +309,24 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
}
+ /**
+ * Write to a protocol buffer output stream. Protocol buffer message definition is at
+ * {@link com.android.server.wm.proto.ConfigurationContainerProto}.
+ *
+ * @param protoOutputStream Stream to write the ConfigurationContainer object to.
+ * @param fieldId Field Id of the ConfigurationContainer as defined in the parent
+ * message.
+ * @hide
+ */
+ @CallSuper
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ mOverrideConfiguration.writeToProto(protoOutputStream, OVERRIDE_CONFIGURATION);
+ mFullConfiguration.writeToProto(protoOutputStream, FULL_CONFIGURATION);
+ mMergedOverrideConfiguration.writeToProto(protoOutputStream, MERGED_OVERRIDE_CONFIGURATION);
+ protoOutputStream.end(token);
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
diff --git a/com/android/server/wm/DisplayContent.java b/com/android/server/wm/DisplayContent.java
index 6cf608ab..0e68a8f6 100644
--- a/com/android/server/wm/DisplayContent.java
+++ b/com/android/server/wm/DisplayContent.java
@@ -18,9 +18,11 @@ package com.android.server.wm;
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.HOME_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_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -112,10 +114,11 @@ import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER;
import static com.android.server.wm.proto.DisplayProto.ROTATION;
import static com.android.server.wm.proto.DisplayProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.proto.DisplayProto.STACKS;
+import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
-import android.app.WindowConfiguration;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -389,10 +392,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
!= mTmpWindowAnimator.mAnimTransactionSequence) {
appAnimator.thumbnailTransactionSeq =
mTmpWindowAnimator.mAnimTransactionSequence;
- appAnimator.thumbnailLayer = 0;
- }
- if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
- appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
}
}
};
@@ -966,10 +965,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
- final boolean rotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
+ boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
- if (rotateSeamlessly) {
+ if (mayRotateSeamlessly) {
final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
if (seamlessRotated != null) {
// We can't rotate (seamlessly or not) while waiting for the last seamless rotation
@@ -978,7 +977,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// window-removal.
return false;
}
+
+ // In the presence of the PINNED stack or System Alert
+ // windows we unforuntately can not seamlessly rotate.
+ if (getStackById(PINNED_STACK_ID) != null) {
+ mayRotateSeamlessly = false;
+ }
+ for (int i = 0; i < mService.mSessions.size(); i++) {
+ if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+ mayRotateSeamlessly = false;
+ break;
+ }
+ }
}
+ final boolean rotateSeamlessly = mayRotateSeamlessly;
// TODO: Implement forced rotation changes.
// Set mAltOrientation to indicate that the application is receiving
@@ -1455,16 +1467,38 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return null;
}
+ /**
+ * Returns the topmost stack on the display that is compatible with the input windowing mode.
+ * Null is no compatible stack on the display.
+ */
+ TaskStack getStack(int windowingMode) {
+ return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ /**
+ * 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) {
+ 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
int getStackCount() {
return mTaskStackContainers.size();
}
@VisibleForTesting
- int getStackPosById(int stackId) {
+ int getStackPosition(int windowingMode, int activityType) {
for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.get(i);
- if (stack.mStackId == stackId) {
+ if (stack.isCompatible(windowingMode, activityType)) {
return i;
}
}
@@ -1499,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 (getStackById(PINNED_STACK_ID) == null) {
+ if (getStack(WINDOWING_MODE_PINNED) == null) {
mPinnedStackControllerLocked.onDisplayInfoChanged();
}
}
@@ -1720,21 +1754,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
out.set(mContentRect);
}
- TaskStack addStackToDisplay(int stackId, boolean onTop) {
+ TaskStack addStackToDisplay(int stackId, boolean onTop, StackWindowController controller) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
TaskStack stack = getStackById(stackId);
if (stack != null) {
- // It's already attached to the display...clear mDeferRemoval and move stack to
- // appropriate z-order on display as needed.
+ // 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);
+ stack = new TaskStack(mService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
}
@@ -2023,8 +2058,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
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) &&
- StackId.isStackAffectedByDragResizing(stack.mStackId)) {
+ if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
+ && stack.inSplitScreenWindowingMode()) {
stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
} else {
stack.resetAdjustedForIme(false);
@@ -2119,8 +2154,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
proto.write(ID, mDisplayId);
for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -2232,7 +2270,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @return The docked stack, but only if it is visible, and {@code null} otherwise.
*/
TaskStack getDockedStackLocked() {
- final TaskStack stack = getStackById(DOCKED_STACK_ID);
+ final TaskStack stack = getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
return (stack != null && stack.isVisible()) ? stack : null;
}
@@ -2241,7 +2279,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* visible.
*/
TaskStack getDockedStackIgnoringVisibility() {
- return getStackById(DOCKED_STACK_ID);
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
/** Find the visible, touch-deliverable window under the given point */
@@ -3350,7 +3388,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @see WindowManagerService#addStackToDisplay(int, int, boolean)
*/
void addStackToDisplay(TaskStack stack, boolean onTop) {
- if (stack.mStackId == HOME_STACK_ID) {
+ if (stack.isActivityTypeHome()) {
if (mHomeStack != null) {
throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first.");
}
@@ -3415,11 +3453,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
: requestedPosition >= topChildPosition;
int targetPosition = requestedPosition;
- if (toTop && stack.mStackId != PINNED_STACK_ID
- && getStackById(PINNED_STACK_ID) != null) {
+ 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.mStackId != PINNED_STACK_ID) {
+ if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
}
diff --git a/com/android/server/wm/DockedStackDividerController.java b/com/android/server/wm/DockedStackDividerController.java
index 030b986e..6f441b98 100644
--- a/com/android/server/wm/DockedStackDividerController.java
+++ b/com/android/server/wm/DockedStackDividerController.java
@@ -17,8 +17,10 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.DOCKED_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.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;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Surface.ROTATION_270;
@@ -330,7 +332,7 @@ public class DockedStackDividerController implements DimLayerUser {
mLastVisibility = visible;
notifyDockedDividerVisibilityChanged(visible);
if (!visible) {
- setResizeDimLayer(false, INVALID_STACK_ID, 0f);
+ setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
}
}
@@ -456,7 +458,8 @@ public class DockedStackDividerController implements DimLayerUser {
boolean isHomeStackResizable) {
long animDuration = 0;
if (animate) {
- final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
+ final TaskStack stack =
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final long transitionDuration = isAnimationMaximizing()
? mService.mAppTransition.getLastClipRevealTransitionDuration()
: DEFAULT_APP_TRANSITION_DURATION;
@@ -517,9 +520,18 @@ public class DockedStackDividerController implements DimLayerUser {
}
- void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ /**
+ * Shows a dim layer with {@param alpha} if {@param visible} is true and
+ * {@param targetWindowingMode} isn't
+ * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the
+ * display in that windowing mode.
+ */
+ void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
mService.openSurfaceTransaction();
- final TaskStack stack = mDisplayContent.getStackById(targetStackId);
+ // TODO: Maybe only allow split-screen windowing modes?
+ final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
+ ? mDisplayContent.getStack(targetWindowingMode)
+ : null;
final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
@@ -605,8 +617,8 @@ public class DockedStackDividerController implements DimLayerUser {
if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
return;
}
- final TaskStack fullscreenStack =
- mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ final TaskStack fullscreenStack = mDisplayContent.getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
setMinimizedDockedStack(homeVisible && !homeBehind, animate);
@@ -801,7 +813,8 @@ public class DockedStackDividerController implements DimLayerUser {
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
+ final TaskStack stack =
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
diff --git a/com/android/server/wm/DragResizeMode.java b/com/android/server/wm/DragResizeMode.java
index 8ab04065..c0bf1e89 100644
--- a/com/android/server/wm/DragResizeMode.java
+++ b/com/android/server/wm/DragResizeMode.java
@@ -16,11 +16,7 @@
package com.android.server.wm;
-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.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
/**
* Describes the mode in which a window is drag resizing.
@@ -39,15 +35,12 @@ class DragResizeMode {
*/
static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
- static boolean isModeAllowedForStack(int stackId, int mode) {
+ static boolean isModeAllowedForStack(TaskStack stack, int mode) {
switch (mode) {
case DRAG_RESIZE_MODE_FREEFORM:
- return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ return stack.getWindowingMode() == WINDOWING_MODE_FREEFORM;
case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
- return stackId == DOCKED_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID;
+ return stack.inSplitScreenWindowingMode();
default:
return false;
}
diff --git a/com/android/server/wm/PinnedStackController.java b/com/android/server/wm/PinnedStackController.java
index 1e7140a1..ef31598f 100644
--- a/com/android/server/wm/PinnedStackController.java
+++ b/com/android/server/wm/PinnedStackController.java
@@ -16,7 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -404,27 +405,29 @@ class PinnedStackController {
*/
private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
synchronized (mService.mWindowMap) {
- if (mPinnedStackListener != null) {
- try {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds();
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
- false /* useCurrentMinEdgeSize */);
- }
- final Rect animatingBounds = mTmpAnimatingBoundsRect;
- final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
- } else {
- animatingBounds.set(normalBounds);
- }
- mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering actions changed event.", e);
+ if (mPinnedStackListener == null) {
+ return;
+ }
+ try {
+ final Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+ final Rect normalBounds = getDefaultBounds();
+ if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+ transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+ false /* useCurrentMinEdgeSize */);
+ }
+ final Rect animatingBounds = mTmpAnimatingBoundsRect;
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
+ if (pinnedStack != null) {
+ pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
+ } else {
+ animatingBounds.set(normalBounds);
}
+ mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
+ animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
}
}
@@ -491,7 +494,7 @@ class PinnedStackController {
pw.println(prefix + "PinnedStackController");
pw.print(prefix + " defaultBounds="); getDefaultBounds().printShortString(pw);
pw.println();
- mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+ mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
pw.println();
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
@@ -512,7 +515,7 @@ class PinnedStackController {
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
- mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+ mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
proto.end(token);
}
diff --git a/com/android/server/wm/PinnedStackWindowController.java b/com/android/server/wm/PinnedStackWindowController.java
index 590ac6ed..41f076d7 100644
--- a/com/android/server/wm/PinnedStackWindowController.java
+++ b/com/android/server/wm/PinnedStackWindowController.java
@@ -16,19 +16,16 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
import android.app.RemoteAction;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.server.UiThread;
-
import java.util.List;
/**
@@ -40,8 +37,8 @@ public class PinnedStackWindowController extends StackWindowController {
private Rect mTmpToBounds = new Rect();
public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
- int displayId, boolean onTop, Rect outBounds) {
- super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+ int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+ super(stackId, listener, displayId, onTop, outBounds, service);
}
/**
@@ -101,7 +98,8 @@ public class PinnedStackWindowController extends StackWindowController {
}
schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
- mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpToBounds);
+ mService.getStackBounds(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
if (!mTmpToBounds.isEmpty()) {
// If there is a fullscreen bounds, use that
toBounds = new Rect(mTmpToBounds);
diff --git a/com/android/server/wm/RootWindowContainer.java b/com/android/server/wm/RootWindowContainer.java
index 8a749762..7832f5de 100644
--- a/com/android/server/wm/RootWindowContainer.java
+++ b/com/android/server/wm/RootWindowContainer.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.annotation.CallSuper;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.power.V1_0.PowerHint;
@@ -89,8 +90,9 @@ import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAYS;
-import static com.android.server.wm.proto.WindowManagerServiceProto.WINDOWS;
+import static com.android.server.wm.proto.RootWindowContainerProto.DISPLAYS;
+import static com.android.server.wm.proto.RootWindowContainerProto.WINDOWS;
+import static com.android.server.wm.proto.RootWindowContainerProto.WINDOW_CONTAINER;
/** Root {@link WindowContainer} for the device. */
class RootWindowContainer extends WindowContainer<DisplayContent> {
@@ -420,6 +422,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
return null;
}
+ TaskStack getStack(int windowingMode, int activityType) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final DisplayContent dc = mChildren.get(i);
+ final TaskStack stack = dc.getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
void setSecureSurfaceState(int userId, boolean disabled) {
forAllWindows((w) -> {
if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) {
@@ -1077,7 +1090,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
}
}
- void writeToProto(ProtoOutputStream proto) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
if (mService.mDisplayReady) {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
@@ -1088,6 +1105,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
forAllWindows((w) -> {
w.writeIdentifierToProto(proto, WINDOWS);
}, true);
+ proto.end(token);
}
@Override
diff --git a/com/android/server/wm/Session.java b/com/android/server/wm/Session.java
index 17812477..4dd147e5 100644
--- a/com/android/server/wm/Session.java
+++ b/com/android/server/wm/Session.java
@@ -719,4 +719,8 @@ public class Session extends IWindowSession.Stub
public String toString() {
return mStringName;
}
+
+ boolean hasAlertWindowSurfaces() {
+ return !mAlertWindowSurfaces.isEmpty();
+ }
}
diff --git a/com/android/server/wm/StackWindowController.java b/com/android/server/wm/StackWindowController.java
index a50ed71a..c0a4cb72 100644
--- a/com/android/server/wm/StackWindowController.java
+++ b/com/android/server/wm/StackWindowController.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import android.app.ActivityManager.StackId;
-import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -76,8 +74,7 @@ public class StackWindowController
+ " to unknown displayId=" + displayId);
}
- final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
- stack.setController(this);
+ dc.addStackToDisplay(stackId, onTop, this);
getRawBounds(outBounds);
}
}
diff --git a/com/android/server/wm/Task.java b/com/android/server/wm/Task.java
index 55b6c912..7e8d1308 100644
--- a/com/android/server/wm/Task.java
+++ b/com/android/server/wm/Task.java
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSC
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.res.Configuration.EMPTY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
@@ -34,8 +35,9 @@ import static com.android.server.wm.proto.TaskProto.BOUNDS;
import static com.android.server.wm.proto.TaskProto.FILLS_PARENT;
import static com.android.server.wm.proto.TaskProto.ID;
import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS;
+import static com.android.server.wm.proto.TaskProto.WINDOW_CONTAINER;
-import android.app.ActivityManager.StackId;
+import android.annotation.CallSuper;
import android.app.ActivityManager.TaskDescription;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -278,15 +280,9 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
// WindowConfiguration long term.
private int setBounds(Rect bounds, Configuration overrideConfig) {
if (overrideConfig == null) {
- overrideConfig = Configuration.EMPTY;
- }
- if (bounds == null && !Configuration.EMPTY.equals(overrideConfig)) {
- throw new IllegalArgumentException("null bounds but non empty configuration: "
- + overrideConfig);
- }
- if (bounds != null && Configuration.EMPTY.equals(overrideConfig)) {
- throw new IllegalArgumentException("non null bounds, but empty configuration");
+ overrideConfig = EMPTY;
}
+
boolean oldFullscreen = mFillsParent;
int rotation = Surface.ROTATION_0;
final DisplayContent displayContent = mStack.getDisplayContent();
@@ -321,7 +317,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
if (displayContent != null) {
displayContent.mDimLayerController.updateDimLayer(this);
}
- onOverrideConfigurationChanged(mFillsParent ? Configuration.EMPTY : overrideConfig);
+ onOverrideConfigurationChanged(overrideConfig);
return boundsChange;
}
@@ -404,7 +400,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
* the adjusted bounds's top.
*/
void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- if (!isResizeable() || Configuration.EMPTY.equals(getOverrideConfiguration())) {
+ if (!isResizeable() || EMPTY.equals(getOverrideConfiguration())) {
return;
}
@@ -529,7 +525,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
- if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) {
+ if (!DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ mStack.mStackId + " dragResizeMode=" + dragResizeMode);
}
@@ -552,7 +548,9 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return;
}
if (mFillsParent) {
- setBounds(null, Configuration.EMPTY);
+ // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a
+ // problem once we move mBounds into WindowConfiguration.
+ setBounds(null, getOverrideConfiguration());
return;
}
final int newRotation = displayContent.getDisplayInfo().rotation;
@@ -735,8 +733,11 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return "Task=" + mTaskId;
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
proto.write(ID, mTaskId);
for (int i = mChildren.size() - 1; i >= 0; i--) {
final AppWindowToken appWindowToken = mChildren.get(i);
diff --git a/com/android/server/wm/TaskSnapshotController.java b/com/android/server/wm/TaskSnapshotController.java
index 46324022..bff24f6e 100644
--- a/com/android/server/wm/TaskSnapshotController.java
+++ b/com/android/server/wm/TaskSnapshotController.java
@@ -254,7 +254,7 @@ class TaskSnapshotController {
@VisibleForTesting
int getSnapshotMode(Task task) {
final AppWindowToken topChild = task.getTopChild();
- if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) {
+ if (!task.isActivityTypeStandardOrUndefined()) {
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
return SNAPSHOT_MODE_APP_THEME;
@@ -297,7 +297,7 @@ class TaskSnapshotController {
return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
topChild.getConfiguration().orientation, mainWindow.mStableInsets,
- false /* reduced */, 1.0f /* scale */);
+ ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */);
}
/**
diff --git a/com/android/server/wm/TaskStack.java b/com/android/server/wm/TaskStack.java
index 4664dcbb..65278837 100644
--- a/com/android/server/wm/TaskStack.java
+++ b/com/android/server/wm/TaskStack.java
@@ -19,8 +19,11 @@ 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.HOME_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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -41,8 +44,9 @@ import static com.android.server.wm.proto.StackProto.BOUNDS;
import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
import static com.android.server.wm.proto.StackProto.ID;
import static com.android.server.wm.proto.StackProto.TASKS;
+import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER;
-import android.app.ActivityManager.StackId;
+import android.annotation.CallSuper;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
@@ -147,9 +151,10 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
Rect mPreAnimationBounds = new Rect();
- TaskStack(WindowManagerService service, int stackId) {
+ TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
mService = service;
mStackId = stackId;
+ setController(controller);
mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_minimize_thickness);
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
@@ -733,7 +738,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
outTempTaskBounds.setEmpty();
// When the home stack is resizable, should always have the same stack and task bounds
- if (mStackId == HOME_STACK_ID) {
+ if (isActivityTypeHome()) {
final Task homeTask = findHomeTask();
if (homeTask != null && homeTask.isResizeable()) {
// Calculate the home stack bounds when in docked mode and the home stack is
@@ -918,7 +923,9 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- mAnimationBackgroundSurface.hide();
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface.hide();
+ }
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -1005,7 +1012,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
mAdjustImeAmount = 0f;
mAdjustDividerAmount = 0f;
updateAdjustedBounds();
- mService.setResizeDimLayer(false, mStackId, 1.0f);
+ mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
@@ -1201,7 +1208,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
if (mAdjustedForIme && adjust && !isImeTarget) {
final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
* IME_ADJUST_DIM_AMOUNT;
- mService.setResizeDimLayer(true, mStackId, alpha);
+ mService.setResizeDimLayer(true, getWindowingMode(), alpha);
}
}
@@ -1219,8 +1226,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return mMinimizeAmount != 0f;
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
proto.write(ID, mStackId);
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).writeToProto(proto, TASKS);
@@ -1277,7 +1287,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
@Override
public boolean dimFullscreen() {
- return StackId.isHomeOrRecentsStack(mStackId) || fillsParent();
+ return !isActivityTypeStandard() || fillsParent();
}
@Override
@@ -1657,7 +1667,15 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
@Override
int getOrientation() {
- return (StackId.canSpecifyOrientation(mStackId))
- ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
+ return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
+ }
+
+ private boolean canSpecifyOrientation() {
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
}
}
diff --git a/com/android/server/wm/WindowContainer.java b/com/android/server/wm/WindowContainer.java
index 926719dd..40923c82 100644
--- a/com/android/server/wm/WindowContainer.java
+++ b/com/android/server/wm/WindowContainer.java
@@ -19,12 +19,14 @@ package com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
+import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
+import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
import android.annotation.CallSuper;
import android.content.res.Configuration;
import android.util.Pools;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ToBooleanFunction;
import java.util.Comparator;
@@ -685,6 +687,23 @@ a * Returns whether this child is on top of the window hierarchy.
}
}
+ /**
+ * Write to a protocol buffer output stream. Protocol buffer message definition is at
+ * {@link com.android.server.wm.proto.WindowContainerProto}.
+ *
+ * @param protoOutputStream Stream to write the WindowContainer object to.
+ * @param fieldId Field Id of the WindowContainer as defined in the parent message.
+ * @hide
+ */
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER);
+ protoOutputStream.write(ORIENTATION, mOrientation);
+ protoOutputStream.end(token);
+ }
+
String getName() {
return toString();
}
diff --git a/com/android/server/wm/WindowLayersController.java b/com/android/server/wm/WindowLayersController.java
index 857b13d2..7caf2fe9 100644
--- a/com/android/server/wm/WindowLayersController.java
+++ b/com/android/server/wm/WindowLayersController.java
@@ -21,11 +21,8 @@ import android.util.Slog;
import java.util.ArrayDeque;
import java.util.function.Consumer;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-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.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -187,12 +184,13 @@ class WindowLayersController {
}
}
- final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID;
- if (stackId == PINNED_STACK_ID) {
+ final int windowingMode = w.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_PINNED) {
mPinnedWindows.add(w);
- } else if (stackId == DOCKED_STACK_ID) {
+ } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mDockedWindows.add(w);
- } else if (stackId == ASSISTANT_STACK_ID) {
+ }
+ if (w.isActivityTypeAssistant()) {
mAssistantWindows.add(w);
}
}
@@ -268,20 +266,8 @@ class WindowLayersController {
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
+ w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
- if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
- }
- // TODO(b/62029108): the entire contents of the if statement should call the refactored
- // function to set the thumbnail layer for w.AppToken
- int highestLayer = w.mAppToken.getHighestAnimLayer();
- if (highestLayer > 0) {
- if (w.mAppToken.mAppAnimator.thumbnail != null
- && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
- w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
- }
- }
+ if (w.mAppToken != null) {
+ w.mAppToken.mAppAnimator.updateThumbnailLayer();
}
}
}
diff --git a/com/android/server/wm/WindowManagerService.java b/com/android/server/wm/WindowManagerService.java
index 32ee51c8..1fb21887 100644
--- a/com/android/server/wm/WindowManagerService.java
+++ b/com/android/server/wm/WindowManagerService.java
@@ -109,6 +109,7 @@ import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WIND
import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW;
import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION;
import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY;
+import static com.android.server.wm.proto.WindowManagerServiceProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION;
import android.Manifest;
@@ -246,6 +247,7 @@ import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
import com.android.server.power.BatterySaverPolicy.ServiceType;
import com.android.server.power.ShutdownThread;
+import com.android.server.utils.PriorityDump;
import java.io.BufferedWriter;
import java.io.DataInputStream;
@@ -390,6 +392,18 @@ public class WindowManagerService extends IWindowManager.Stub
};
final WindowSurfacePlacer mWindowPlacerLocked;
+ private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, new String[] {"-a"});
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doDump(fd, pw, args);
+ }
+ };
+
/**
* Current user when multi-user is enabled. Don't show windows of
* non-current user. Also see mCurrentProfileIds.
@@ -2029,10 +2043,14 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
+ " newVis=" + viewVisibility, stack);
}
- if (viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || !win.mAppToken.isClientHidden())) {
+ // We should only relayout if the view is visible, it is a starting window, or the
+ // associated appToken is not hidden.
+ final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
+ (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || !win.mAppToken.isClientHidden());
+
+ if (shouldRelayout) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
// We are about to create a surface, but we didn't run a layout yet. So better run
@@ -2179,8 +2197,18 @@ public class WindowManagerService extends IWindowManager.Stub
// and needs process it before handling the corresponding window frame. the variable
// {@code mergedConfiguration} is an out parameter that will be passed back to the
// client over IPC and checked there.
- win.getMergedConfiguration(mergedConfiguration);
- win.setReportedConfiguration(mergedConfiguration);
+ // Note: in the cases where the window is tied to an activity, we should not send a
+ // configuration update when the window has requested to be hidden. Doing so can lead
+ // to the client erroneously accepting a configuration that would have otherwise caused
+ // an activity restart. We instead hand back the last reported
+ // {@link MergedConfiguration}.
+ if (shouldRelayout) {
+ win.getMergedConfiguration(mergedConfiguration);
+ } else {
+ win.getLastReportedMergedConfiguration(mergedConfiguration);
+ }
+
+ win.setLastReportedMergedConfiguration(mergedConfiguration);
outFrame.set(win.mCompatFrame);
outOverscanInsets.set(win.mOverscanInsets);
@@ -2881,9 +2909,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void getStackBounds(int stackId, Rect bounds) {
+ public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
synchronized (mWindowMap) {
- final TaskStack stack = mRoot.getStackById(stackId);
+ final TaskStack stack = mRoot.getStack(windowingMode, activityType);
if (stack != null) {
stack.getBounds(bounds);
return;
@@ -6505,7 +6533,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void writeToProtoLocked(ProtoOutputStream proto) {
mPolicy.writeToProto(proto, POLICY);
- mRoot.writeToProto(proto);
+ mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER);
if (mCurrentFocus != null) {
mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
}
@@ -6793,8 +6821,11 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
+ }
+ private void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
boolean dumpAll = false;
boolean useProto = false;
@@ -7124,10 +7155,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
- visible, targetStackId, alpha);
+ visible, targetWindowingMode, alpha);
}
}
diff --git a/com/android/server/wm/WindowState.java b/com/android/server/wm/WindowState.java
index 1b055664..4ff0f391 100644
--- a/com/android/server/wm/WindowState.java
+++ b/com/android/server/wm/WindowState.java
@@ -116,7 +116,9 @@ import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER;
+import android.annotation.CallSuper;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -255,7 +257,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* We'll send configuration to client only if it is different from the last applied one and
* client won't perform unnecessary updates.
*/
- private final Configuration mLastReportedConfiguration = new Configuration();
+ private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
* Actual position of the surface shown on-screen (may be modified by animation). These are
@@ -1242,7 +1244,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// this is not necessarily what the client has processed yet. Find a
// better indicator consistent with the client.
return (mOrientationChanging || (isVisible()
- && getConfiguration().orientation != mLastReportedConfiguration.orientation))
+ && getConfiguration().orientation != getLastReportedConfiguration().orientation))
&& !mSeamlesslyRotated
&& !mOrientationChangeTimedOut;
}
@@ -1756,7 +1758,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** Returns true if last applied config was not yet requested by client. */
boolean isConfigChanged() {
- return !mLastReportedConfiguration.equals(getConfiguration());
+ return !getLastReportedConfiguration().equals(getConfiguration());
}
void onWindowReplacementTimeout() {
@@ -2310,8 +2312,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outConfiguration.setConfiguration(globalConfig, overrideConfig);
}
- void setReportedConfiguration(MergedConfiguration config) {
- mLastReportedConfiguration.setTo(config.getMergedConfiguration());
+ void setLastReportedMergedConfiguration(MergedConfiguration config) {
+ mLastReportedConfiguration.setTo(config);
+ }
+
+ void getLastReportedMergedConfiguration(MergedConfiguration config) {
+ config.setTo(mLastReportedConfiguration);
+ }
+
+ private Configuration getLastReportedConfiguration() {
+ return mLastReportedConfiguration.getMergedConfiguration();
}
void adjustStartingWindowFlags() {
@@ -2851,7 +2861,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
new MergedConfiguration(mService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
- setReportedConfiguration(mergedConfiguration);
+ setLastReportedMergedConfiguration(mergedConfiguration);
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
@@ -3078,7 +3088,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (task == null) {
return false;
}
- if (!StackId.isStackAffectedByDragResizing(getStackId())) {
+ if (!inSplitScreenWindowingMode()) {
return false;
}
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
@@ -3124,8 +3134,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|| (isChildWindow() && getParentWindow().isDockedResizing());
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(DISPLAY_ID, getDisplayId());
proto.write(STACK_ID, getStackId());
@@ -3168,7 +3181,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.print(" mShowToOwnerOnly="); pw.print(mShowToOwnerOnly);
pw.print(" package="); pw.print(mAttrs.packageName);
pw.print(" appop="); pw.println(AppOpsManager.opToName(mAppOp));
- pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
+ pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs.toString(prefix));
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
pw.print(" h="); pw.print(mRequestedHeight);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
@@ -3249,7 +3262,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration());
pw.print(prefix); pw.print("mLastReportedConfiguration=");
- pw.println(mLastReportedConfiguration);
+ pw.println(getLastReportedConfiguration());
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
@@ -3309,7 +3322,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.print(prefix); pw.print("mOrientationChanging=");
pw.print(mOrientationChanging);
pw.print(" configOrientationChanging=");
- pw.print(mLastReportedConfiguration.orientation
+ pw.print(getLastReportedConfiguration().orientation
!= getConfiguration().orientation);
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
diff --git a/com/android/server/wm/WindowSurfacePlacer.java b/com/android/server/wm/WindowSurfacePlacer.java
index 88625d35..af1fa2fe 100644
--- a/com/android/server/wm/WindowSurfacePlacer.java
+++ b/com/android/server/wm/WindowSurfacePlacer.java
@@ -342,10 +342,7 @@ class WindowSurfacePlacer {
mTmpLayerAndToken.token = null;
handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
- final int topClosingLayer = mTmpLayerAndToken.layer;
-
- final AppWindowToken topOpeningApp = handleOpeningApps(transit,
- animLp, voiceInteraction, topClosingLayer);
+ final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction);
mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
@@ -387,8 +384,9 @@ class WindowSurfacePlacer {
}
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
- boolean voiceInteraction, int topClosingLayer) {
+ boolean voiceInteraction) {
AppWindowToken topOpeningApp = null;
+ int topOpeningLayer = Integer.MIN_VALUE;
final int appsCount = mService.mOpeningApps.size();
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -422,7 +420,6 @@ class WindowSurfacePlacer {
}
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- int topOpeningLayer = 0;
if (animLp != null) {
final int layer = wtoken.getHighestAnimLayer();
if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -431,7 +428,7 @@ class WindowSurfacePlacer {
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
- createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
return topOpeningApp;
@@ -473,7 +470,7 @@ class WindowSurfacePlacer {
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
- createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
}
@@ -666,8 +663,7 @@ class WindowSurfacePlacer {
}
}
- private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
- int openingLayer, int closingLayer) {
+ private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) {
AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
if (openingAppAnimator == null || openingAppAnimator.animation == null) {
return;
@@ -724,7 +720,6 @@ class WindowSurfacePlacer {
anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
insets, thumbnailHeader, taskId, displayConfig.uiMode,
displayConfig.orientation);
- openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mService.mAppTransition.isNextThumbnailTransitionScaleUp();
} else {
@@ -734,8 +729,8 @@ class WindowSurfacePlacer {
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
- openingAppAnimator.thumbnailLayer = openingLayer;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
} catch (Surface.OutOfResourcesException e) {
diff --git a/com/android/server/wm/WindowToken.java b/com/android/server/wm/WindowToken.java
index 422615b1..943448ee 100644
--- a/com/android/server/wm/WindowToken.java
+++ b/com/android/server/wm/WindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.annotation.CallSuper;
import android.util.proto.ProtoOutputStream;
import java.util.Comparator;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -28,6 +29,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE;
import static com.android.server.wm.proto.WindowTokenProto.WINDOWS;
+import static com.android.server.wm.proto.WindowTokenProto.WINDOW_CONTAINER;
import android.os.Debug;
import android.os.IBinder;
@@ -263,8 +265,11 @@ class WindowToken extends WindowContainer<WindowState> {
super.onDisplayChanged(dc);
}
- void writeToProto(ProtoOutputStream proto, long fieldId) {
+ @CallSuper
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ super.writeToProto(proto, WINDOW_CONTAINER);
proto.write(HASH_CODE, System.identityHashCode(this));
for (int i = 0; i < mChildren.size(); i++) {
final WindowState w = mChildren.get(i);
diff --git a/com/android/settingslib/applications/ApplicationsState.java b/com/android/settingslib/applications/ApplicationsState.java
index 40c2b1f3..fa2499f6 100644
--- a/com/android/settingslib/applications/ApplicationsState.java
+++ b/com/android/settingslib/applications/ApplicationsState.java
@@ -52,6 +52,11 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
import java.io.File;
import java.io.IOException;
@@ -180,7 +185,11 @@ public class ApplicationsState {
}
public Session newSession(Callbacks callbacks) {
- Session s = new Session(callbacks);
+ return newSession(callbacks, null);
+ }
+
+ public Session newSession(Callbacks callbacks, Lifecycle lifecycle) {
+ Session s = new Session(callbacks, lifecycle);
synchronized (mEntriesMap) {
mSessions.add(s);
}
@@ -586,7 +595,7 @@ public class ApplicationsState {
.replaceAll("").toLowerCase();
}
- public class Session {
+ public class Session implements LifecycleObserver, OnPause, OnResume, OnDestroy {
final Callbacks mCallbacks;
boolean mResumed;
@@ -600,11 +609,19 @@ public class ApplicationsState {
ArrayList<AppEntry> mLastAppList;
boolean mRebuildForeground;
- Session(Callbacks callbacks) {
+ private final boolean mHasLifecycle;
+
+ Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ mHasLifecycle = true;
+ } else {
+ mHasLifecycle = false;
+ }
}
- public void resume() {
+ public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
synchronized (mEntriesMap) {
if (!mResumed) {
@@ -616,7 +633,7 @@ public class ApplicationsState {
if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
}
- public void pause() {
+ public void onPause() {
if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
synchronized (mEntriesMap) {
if (mResumed) {
@@ -735,8 +752,11 @@ public class ApplicationsState {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
- public void release() {
- pause();
+ public void onDestroy() {
+ if (!mHasLifecycle) {
+ // TODO: Legacy, remove this later once all usages are switched to Lifecycle
+ onPause();
+ }
synchronized (mEntriesMap) {
mSessions.remove(this);
}
diff --git a/com/android/settingslib/applications/StorageStatsSource.java b/com/android/settingslib/applications/StorageStatsSource.java
index 8fc9fa6a..9fbadee3 100644
--- a/com/android/settingslib/applications/StorageStatsSource.java
+++ b/com/android/settingslib/applications/StorageStatsSource.java
@@ -131,7 +131,7 @@ public class StorageStatsSource {
}
public long getTotalBytes() {
- return mStats.getCacheBytes() + mStats.getCodeBytes() + mStats.getDataBytes();
+ return mStats.getAppBytes() + mStats.getDataBytes();
}
}
} \ No newline at end of file
diff --git a/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 47cbb77a..6aae226b 100644
--- a/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -28,17 +28,20 @@ import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import android.text.TextUtils;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.ConfirmationDialogController;
-public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController
- implements ConfirmationDialogController {
+public abstract class AbstractEnableAdbPreferenceController extends
+ DeveloperOptionsPreferenceController implements ConfirmationDialogController {
private static final String KEY_ENABLE_ADB = "enable_adb";
public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
"com.android.settingslib.development.AbstractEnableAdbController."
+ "ENABLE_ADB_STATE_CHANGED";
- private SwitchPreference mPreference;
+ public static final int ADB_SETTING_ON = 1;
+ public static final int ADB_SETTING_OFF = 0;
+
+
+ protected SwitchPreference mPreference;
public AbstractEnableAdbPreferenceController(Context context) {
super(context);
@@ -64,12 +67,13 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref
private boolean isAdbEnabled() {
final ContentResolver cr = mContext.getContentResolver();
- return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+ return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, ADB_SETTING_OFF)
+ != ADB_SETTING_OFF;
}
@Override
public void updateState(Preference preference) {
- ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+ ((TwoStatePreference) preference).setChecked(isAdbEnabled());
}
public void enablePreference(boolean enabled) {
@@ -105,7 +109,7 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref
protected void writeAdbSetting(boolean enabled) {
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+ Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
notifyStateChanged();
}
diff --git a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
index c1677236..7998b2ef 100644
--- a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
+++ b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -26,10 +26,9 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
-public abstract class AbstractLogdSizePreferenceController extends AbstractPreferenceController
- implements Preference.OnPreferenceChangeListener {
+public abstract class AbstractLogdSizePreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener {
public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development."
+ "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
@@ -57,11 +56,6 @@ public abstract class AbstractLogdSizePreferenceController extends AbstractPrefe
}
@Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
public String getPreferenceKey() {
return SELECT_LOGD_SIZE_KEY;
}
diff --git a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
index 502fb174..67553adc 100644
--- a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
+++ b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -30,16 +30,15 @@ import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.ConfirmationDialogController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
-public abstract class AbstractLogpersistPreferenceController extends AbstractPreferenceController
- implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy,
- ConfirmationDialogController {
+public abstract class AbstractLogpersistPreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+ LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController {
private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
diff --git a/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
new file mode 100644
index 00000000..f68c04f9
--- /dev/null
+++ b/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.development;
+
+import android.content.Context;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller is used handle changes for the master switch in the developer options page.
+ *
+ * All Preference Controllers that are a part of the developer options page should inherit this
+ * class.
+ */
+public abstract class DeveloperOptionsPreferenceController extends
+ AbstractPreferenceController {
+
+ public DeveloperOptionsPreferenceController(Context context) {
+ super(context);
+ }
+
+ /**
+ * Child classes should override this method to create custom logic for hiding preferences.
+ *
+ * @return true if the preference is to be displayed.
+ */
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * Called when developer options is enabled
+ */
+ public void onDeveloperOptionsEnabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchEnabled();
+ }
+ }
+
+ /**
+ * Called when developer options is disabled
+ */
+ public void onDeveloperOptionsDisabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchDisabled();
+ }
+ }
+
+ /**
+ * Called when developer options is enabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchEnabled() {
+ }
+
+ /**
+ * Called when developer options is disabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchDisabled() {
+ }
+
+}
diff --git a/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
new file mode 100644
index 00000000..ff7536ad
--- /dev/null
+++ b/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Build;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
+ */
+public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController {
+ private static final String KEY_SERIAL_NUMBER = "serial_number";
+
+ private final String mSerialNumber;
+
+ public AbstractSerialNumberPreferenceController(Context context) {
+ this(context, Build.getSerial());
+ }
+
+ @VisibleForTesting
+ AbstractSerialNumberPreferenceController(Context context, String serialNumber) {
+ super(context);
+ mSerialNumber = serialNumber;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return !TextUtils.isEmpty(mSerialNumber);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER);
+ if (pref != null) {
+ pref.setSummary(mSerialNumber);
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SERIAL_NUMBER;
+ }
+}
diff --git a/com/android/settingslib/drawer/TileUtils.java b/com/android/settingslib/drawer/TileUtils.java
index 9b75c00a..35ba6ae9 100644
--- a/com/android/settingslib/drawer/TileUtils.java
+++ b/com/android/settingslib/drawer/TileUtils.java
@@ -26,7 +26,6 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -320,6 +319,15 @@ public class TileUtils {
Context context, UserHandle user, Intent intent,
Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon) {
+ getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
+ usePriority, checkCategory, forceTintExternalIcon, false /* shouldUpdateTiles */);
+ }
+
+ public static void getTilesForIntent(
+ Context context, UserHandle user, Intent intent,
+ Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
+ boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon,
+ boolean shouldUpdateTiles) {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
@@ -357,9 +365,11 @@ public class TileUtils {
updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
pm, providerMap, forceTintExternalIcon);
if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
-
addedCache.put(key, tile);
+ } else if (shouldUpdateTiles) {
+ updateSummaryAndTitle(context, providerMap, tile);
}
+
if (!tile.userHandle.contains(user)) {
tile.userHandle.add(user);
}
@@ -380,7 +390,6 @@ public class TileUtils {
String summary = null;
String keyHint = null;
boolean isIconTintable = false;
- RemoteViews remoteViews = null;
// Get the activity's meta-data
try {
@@ -428,7 +437,8 @@ public class TileUtils {
}
if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
- remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
+ tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
+ updateSummaryAndTitle(context, providerMap, tile);
}
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
@@ -462,7 +472,6 @@ public class TileUtils {
// Suggest a key for this tile
tile.key = keyHint;
tile.isIconTintable = isIconTintable;
- tile.remoteViews = remoteViews;
return true;
}
@@ -470,6 +479,26 @@ public class TileUtils {
return false;
}
+ private static void updateSummaryAndTitle(
+ Context context, Map<String, IContentProvider> providerMap, Tile tile) {
+ if (tile == null || tile.metaData == null
+ || !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
+ return;
+ }
+
+ String uriString = tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI);
+ Bundle bundle = getBundleFromUri(context, uriString, providerMap);
+ String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY);
+ String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE);
+ if (overrideSummary != null) {
+ tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
+ }
+
+ if (overrideTitle != null) {
+ tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle);
+ }
+ }
+
/**
* Gets the icon package name and resource id from content provider.
* @param context context
@@ -535,37 +564,6 @@ public class TileUtils {
}
}
- public static void updateTileUsingSummaryUri(Context context, final Tile tile) {
- if (tile == null || tile.metaData == null ||
- !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
- return;
- }
-
- new AsyncTask<Void, Void, Bundle>() {
- @Override
- protected Bundle doInBackground(Void... params) {
- return getBundleFromUri(context,
- tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI), new HashMap<>());
- }
-
- @Override
- protected void onPostExecute(Bundle bundle) {
- if (bundle == null) {
- return;
- }
- final String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY);
- final String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE);
-
- if (overrideSummary != null) {
- tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
- }
- if (overrideTitle != null) {
- tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle);
- }
- }
- }.execute();
- }
-
private static String getString(Bundle bundle, String key) {
return bundle == null ? null : bundle.getString(key);
}
diff --git a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index b7fd4048..3c5ac8df 100644
--- a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -73,7 +73,7 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable {
final Drawable deviceDrawable = context.getDrawable(resId);
final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context,
- R.color.meter_background_color, batteryLevel);
+ context.getColor(R.color.meter_background_color), batteryLevel);
final int pad = context.getResources().getDimensionPixelSize(R.dimen.bt_battery_padding);
batteryDrawable.setPadding(pad, pad, pad, pad);
@@ -107,6 +107,8 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable {
@VisibleForTesting
static class BatteryMeterDrawable extends BatteryMeterDrawableBase {
private final float mAspectRatio;
+ @VisibleForTesting
+ int mFrameColor;
public BatteryMeterDrawable(Context context, int frameColor, int batteryLevel) {
super(context, frameColor);
@@ -118,6 +120,7 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable {
final int tintColor = Utils.getColorAttr(context, android.R.attr.colorControlNormal);
setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN));
setBatteryLevel(batteryLevel);
+ mFrameColor = frameColor;
}
@Override
diff --git a/com/android/settingslib/suggestions/SuggestionParser.java b/com/android/settingslib/suggestions/SuggestionParser.java
index 00f32b28..56b84415 100644
--- a/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/com/android/settingslib/suggestions/SuggestionParser.java
@@ -195,7 +195,7 @@ public class SuggestionParser {
intent.setPackage(category.pkg);
}
TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- mAddCache, null, suggestions, true, false, false);
+ mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
if (!category.multiple && suggestions.size() > (countBefore + 1)) {
// If there are too many, remove them all and only re-add the one with the highest
diff --git a/com/android/settingslib/wifi/WifiTracker.java b/com/android/settingslib/wifi/WifiTracker.java
index 664dcfcb..12455d85 100644
--- a/com/android/settingslib/wifi/WifiTracker.java
+++ b/com/android/settingslib/wifi/WifiTracker.java
@@ -24,7 +24,6 @@ import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkKey;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
@@ -36,10 +35,14 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.provider.Settings;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +50,12 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,7 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* Tracks saved or available wifi networks and their state.
*/
-public class WifiTracker {
+public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy {
/**
* Default maximum age in millis of cached scored networks in
* {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation.
@@ -80,7 +87,7 @@ public class WifiTracker {
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
+ // TODO(b/36733768): Remove flag includeSaved
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
@@ -96,9 +103,9 @@ public class WifiTracker {
private final WifiListener mListener;
private final boolean mIncludeSaved;
private final boolean mIncludeScans;
- private final boolean mIncludePasspoints;
- @VisibleForTesting final MainHandler mMainHandler;
- @VisibleForTesting final WorkHandler mWorkHandler;
+ @VisibleForTesting MainHandler mMainHandler;
+ @VisibleForTesting WorkHandler mWorkHandler;
+ private HandlerThread mWorkThread;
private WifiTrackerNetworkCallback mNetworkCallback;
@@ -142,7 +149,7 @@ public class WifiTracker {
private WifiInfo mLastInfo;
private final NetworkScoreManager mNetworkScoreManager;
- private final WifiNetworkScoreCache mScoreCache;
+ private WifiNetworkScoreCache mScoreCache;
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
@@ -169,51 +176,43 @@ public class WifiTracker {
return filter;
}
+ /**
+ * Use the lifecycle constructor below whenever possible
+ */
+ @Deprecated
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, null, includeSaved, includeScans);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, false);
+ this(context, wifiListener, includeSaved, includeScans,
+ context.getSystemService(WifiManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ newIntentFilter());
}
public WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, null, includeSaved, includeScans, includePasspoints);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
+ @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
+ this(context, wifiListener, includeSaved, includeScans,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
- Looper.myLooper(), newIntentFilter());
+ newIntentFilter());
+ lifecycle.addObserver(this);
}
@VisibleForTesting
- WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints,
- WifiManager wifiManager, ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager, Looper currentLooper,
- IntentFilter filter) {
+ WifiTracker(Context context, WifiListener wifiListener,
+ boolean includeSaved, boolean includeScans,
+ WifiManager wifiManager, ConnectivityManager connectivityManager,
+ NetworkScoreManager networkScoreManager,
+ IntentFilter filter) {
if (!includeSaved && !includeScans) {
throw new IllegalArgumentException("Must include either saved or scans");
}
mContext = context;
- if (currentLooper == null) {
- // When we aren't on a looper thread, default to the main.
- currentLooper = Looper.getMainLooper();
- }
- mMainHandler = new MainHandler(currentLooper);
- mWorkHandler = new WorkHandler(
- workerLooper != null ? workerLooper : currentLooper);
+ mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
mIncludeSaved = includeSaved;
mIncludeScans = includeScans;
- mIncludePasspoints = includePasspoints;
mListener = wifiListener;
mConnectivityManager = connectivityManager;
@@ -229,7 +228,22 @@ public class WifiTracker {
mNetworkScoreManager = networkScoreManager;
- mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
+ final HandlerThread workThread = new HandlerThread(TAG
+ + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ workThread.start();
+ setWorkThread(workThread);
+ }
+
+ /**
+ * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * @param workThread substitute Handler thread, for testing purposes only
+ */
+ @VisibleForTesting
+ void setWorkThread(HandlerThread workThread) {
+ mWorkThread = workThread;
+ mWorkHandler = new WorkHandler(workThread.getLooper());
+ mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
@Override
public void networkCacheUpdated(List<ScoredNetwork> networks) {
synchronized (mLock) {
@@ -244,6 +258,11 @@ public class WifiTracker {
});
}
+ @Override
+ public void onDestroy() {
+ mWorkThread.quit();
+ }
+
/** Synchronously update the list of access points with the latest information. */
@MainThread
public void forceUpdate() {
@@ -312,8 +331,9 @@ public class WifiTracker {
* <p>Registers listeners and starts scanning for wifi networks. If this is not called
* then forceUpdate() must be called to populate getAccessPoints().
*/
+ @Override
@MainThread
- public void startTracking() {
+ public void onStart() {
synchronized (mLock) {
registerScoreCache();
@@ -361,15 +381,16 @@ public class WifiTracker {
/**
* Stop tracking wifi networks and scores.
*
- * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
+ * <p>This should always be called when done with a WifiTracker (if onStart was called) to
* ensure proper cleanup and prevent any further callbacks from occurring.
*
* <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
* {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
* is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
*/
+ @Override
@MainThread
- public void stopTracking() {
+ public void onStop() {
synchronized (mLock) {
if (mRegistered) {
mContext.unregisterReceiver(mReceiver);
@@ -769,9 +790,8 @@ public class WifiTracker {
}
public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
- boolean includeScans, boolean includePasspoints) {
- WifiTracker tracker = new WifiTracker(context,
- null, null, includeSaved, includeScans, includePasspoints);
+ boolean includeScans) {
+ WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
tracker.forceUpdate();
tracker.copyAndNotifyListeners(false /*notifyListeners*/);
return tracker.getAccessPoints();
diff --git a/com/android/settingslib/wifi/WifiTrackerFactory.java b/com/android/settingslib/wifi/WifiTrackerFactory.java
index 79cee046..8b5863ae 100644
--- a/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -16,8 +16,10 @@
package com.android.settingslib.wifi;
import android.content.Context;
-import android.os.Looper;
import android.support.annotation.Keep;
+import android.support.annotation.NonNull;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* Factory method used to inject WifiTracker instances.
@@ -31,12 +33,11 @@ public class WifiTrackerFactory {
}
public static WifiTracker create(
- Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
+ Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle,
+ boolean includeSaved, boolean includeScans) {
if(sTestingWifiTracker != null) {
return sTestingWifiTracker;
}
- return new WifiTracker(
- context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints);
+ return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans);
}
}
diff --git a/com/android/settingslib/wrapper/PackageManagerWrapper.java b/com/android/settingslib/wrapper/PackageManagerWrapper.java
index cd62bc36..b1f3f3ce 100644
--- a/com/android/settingslib/wrapper/PackageManagerWrapper.java
+++ b/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -60,6 +60,13 @@ public class PackageManagerWrapper {
}
/**
+ * Calls {@code PackageManager.getInstalledPackagesAsUser}
+ */
+ public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+ return mPm.getInstalledPackagesAsUser(flags, userId);
+ }
+
+ /**
* Calls {@code PackageManager.hasSystemFeature()}.
*
* @see android.content.pm.PackageManager#hasSystemFeature
@@ -132,11 +139,11 @@ public class PackageManagerWrapper {
/**
* Gets information about a particular package from the package manager.
+ *
* @param packageName The name of the package we would like information about.
- * @param i additional options flags. see javadoc for
- * {@link PackageManager#getPackageInfo(String, int)}
+ * @param i additional options flags. see javadoc for
+ * {@link PackageManager#getPackageInfo(String, int)}
* @return The PackageInfo for the requested package
- * @throws NameNotFoundException
*/
public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
return mPm.getPackageInfo(packageName, i);
@@ -144,6 +151,7 @@ public class PackageManagerWrapper {
/**
* Retrieves the icon associated with this particular set of ApplicationInfo
+ *
* @param info The ApplicationInfo to retrieve the icon for
* @return The icon as a drawable.
*/
@@ -154,6 +162,7 @@ public class PackageManagerWrapper {
/**
* Retrieves the label associated with the particular set of ApplicationInfo
+ *
* @param app The ApplicationInfo to retrieve the label for
* @return the label as a CharSequence
*/
@@ -190,4 +199,48 @@ public class PackageManagerWrapper {
throws PackageManager.NameNotFoundException {
return mPm.getPackageUidAsUser(pkg, userId);
}
+
+ /**
+ * Calls {@code PackageManager.setApplicationEnabledSetting}
+ */
+ public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+ mPm.setApplicationEnabledSetting(packageName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationEnabledSetting}
+ */
+ public int getApplicationEnabledSetting(String packageName) {
+ return mPm.getApplicationEnabledSetting(packageName);
+ }
+
+ /**
+ * Calls {@code PackageManager.setComponentEnabledSetting}
+ */
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
+ mPm.setComponentEnabledSetting(componentName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationInfo}
+ */
+ public ApplicationInfo getApplicationInfo(String packageName, int flags)
+ throws NameNotFoundException {
+ return mPm.getApplicationInfo(packageName, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationLabel}
+ */
+ public CharSequence getApplicationLabel(ApplicationInfo info) {
+ return mPm.getApplicationLabel(info);
+ }
+
+ /**
+ * Calls {@code PackageManager.queryBroadcastReceivers}
+ */
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ return mPm.queryBroadcastReceivers(intent, flags);
+ }
}
+
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/LinkAccessibilityHelper.java b/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
index 1e663d6a..1fb3a373 100644
--- a/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
+++ b/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
@@ -19,6 +19,8 @@ package com.android.setupwizardlib.util;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
@@ -38,12 +40,11 @@ import java.util.List;
/**
* An accessibility delegate that allows {@link android.text.style.ClickableSpan} to be focused and
* clicked by accessibility services.
- * <p>
- * <strong>Note: </strong> From Android O on, there is native support for ClickableSpan
- * accessibility, so this class is not needed (and indeed has no effect.)
- * </p>
*
- * <p />Sample usage:
+ * <p><strong>Note:</strong> This class is a no-op on Android O or above since there is native
+ * support for ClickableSpan accessibility.
+ *
+ * <p>Sample usage:
* <pre>
* LinkAccessibilityHelper mAccessibilityHelper;
*
@@ -68,294 +69,255 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat {
private static final String TAG = "LinkAccessibilityHelper";
- private final TextView mView;
- private final Rect mTempRect = new Rect();
- private final ExploreByTouchHelper mExploreByTouchHelper;
+ private final AccessibilityDelegateCompat mDelegate;
public LinkAccessibilityHelper(TextView view) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
- // Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy
- mExploreByTouchHelper = new ExploreByTouchHelper(view) {
- @Override
- protected int getVirtualViewAt(float x, float y) {
- return LinkAccessibilityHelper.this.getVirtualViewAt(x, y);
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- LinkAccessibilityHelper.this.getVisibleVirtualViews(virtualViewIds);
- }
-
- @Override
- protected void onPopulateEventForVirtualView(int virtualViewId,
- AccessibilityEvent event) {
- LinkAccessibilityHelper
- .this.onPopulateEventForVirtualView(virtualViewId, event);
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat infoCompat) {
- LinkAccessibilityHelper
- .this.onPopulateNodeForVirtualView(virtualViewId, infoCompat);
-
- }
+ this(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ // Platform support was added in O. This helper will be no-op
+ ? new AccessibilityDelegateCompat()
+ // Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy
+ : new PreOLinkAccessibilityHelper(view));
+ }
- @Override
- protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
- Bundle arguments) {
- return LinkAccessibilityHelper.this
- .onPerformActionForVirtualView(virtualViewId, action, arguments);
- }
- };
- } else {
- mExploreByTouchHelper = null;
- }
- mView = view;
+ @VisibleForTesting
+ LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) {
+ mDelegate = delegate;
}
@Override
public void sendAccessibilityEvent(View host, int eventType) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.sendAccessibilityEvent(host, eventType);
- } else {
- super.sendAccessibilityEvent(host, eventType);
- }
+ mDelegate.sendAccessibilityEvent(host, eventType);
}
@Override
public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.sendAccessibilityEventUnchecked(host, event);
- } else {
- super.sendAccessibilityEventUnchecked(host, event);
- }
+ mDelegate.sendAccessibilityEventUnchecked(host, event);
}
@Override
public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.dispatchPopulateAccessibilityEvent(host, event)
- : super.dispatchPopulateAccessibilityEvent(host, event);
+ return mDelegate.dispatchPopulateAccessibilityEvent(host, event);
}
@Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onPopulateAccessibilityEvent(host, event);
- } else {
- super.onPopulateAccessibilityEvent(host, event);
- }
+ mDelegate.onPopulateAccessibilityEvent(host, event);
}
@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onInitializeAccessibilityEvent(host, event);
- } else {
- super.onInitializeAccessibilityEvent(host, event);
- }
+ mDelegate.onInitializeAccessibilityEvent(host, event);
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onInitializeAccessibilityNodeInfo(host, info);
- } else {
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
+ mDelegate.onInitializeAccessibilityNodeInfo(host, info);
}
@Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
AccessibilityEvent event) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.onRequestSendAccessibilityEvent(host, child, event)
- : super.onRequestSendAccessibilityEvent(host, child, event);
+ return mDelegate.onRequestSendAccessibilityEvent(host, child, event);
}
@Override
public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.getAccessibilityNodeProvider(host)
- : super.getAccessibilityNodeProvider(host);
+ return mDelegate.getAccessibilityNodeProvider(host);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.performAccessibilityAction(host, action, args)
- : super.performAccessibilityAction(host, action, args);
+ return mDelegate.performAccessibilityAction(host, action, args);
}
/**
- * Delegated to {@link ExploreByTouchHelper}
+ * Dispatches hover event to the virtual view hierarchy. This method should be called in
+ * {@link View#dispatchHoverEvent(MotionEvent)}.
+ *
+ * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
*/
public final boolean dispatchHoverEvent(MotionEvent event) {
- return (mExploreByTouchHelper != null) ? mExploreByTouchHelper.dispatchHoverEvent(event)
- : false;
+ return mDelegate instanceof ExploreByTouchHelper
+ && ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event);
}
- protected int getVirtualViewAt(float x, float y) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- final int offset = getOffsetForPosition(mView, x, y);
- ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (linkSpans.length == 1) {
- ClickableSpan linkSpan = linkSpans[0];
- return spannedText.getSpanStart(linkSpan);
- }
+ @VisibleForTesting
+ static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper {
+
+ private final Rect mTempRect = new Rect();
+ private final TextView mView;
+
+ PreOLinkAccessibilityHelper(TextView view) {
+ super(view);
+ mView = view;
}
- return ExploreByTouchHelper.INVALID_ID;
- }
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
- ClickableSpan.class);
- for (ClickableSpan span : linkSpans) {
- virtualViewIds.add(spannedText.getSpanStart(span));
+ protected int getVirtualViewAt(float x, float y) {
+ final CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ final Spanned spannedText = (Spanned) text;
+ final int offset = getOffsetForPosition(mView, x, y);
+ ClickableSpan[] linkSpans =
+ spannedText.getSpans(offset, offset, ClickableSpan.class);
+ if (linkSpans.length == 1) {
+ ClickableSpan linkSpan = linkSpans[0];
+ return spannedText.getSpanStart(linkSpan);
+ }
}
+ return ExploreByTouchHelper.INVALID_ID;
}
- }
- protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- event.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- event.setContentDescription(mView.getText());
+ protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+ final CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ final Spanned spannedText = (Spanned) text;
+ ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
+ ClickableSpan.class);
+ for (ClickableSpan span : linkSpans) {
+ virtualViewIds.add(spannedText.getSpanStart(span));
+ }
+ }
}
- }
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat info) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- info.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- info.setContentDescription(mView.getText());
- }
- info.setFocusable(true);
- info.setClickable(true);
- getBoundsForSpan(span, mTempRect);
- if (mTempRect.isEmpty()) {
- Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
- mTempRect.set(0, 0, 1, 1);
+ protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+ final ClickableSpan span = getSpanForOffset(virtualViewId);
+ if (span != null) {
+ event.setContentDescription(getTextForSpan(span));
+ } else {
+ Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ event.setContentDescription(mView.getText());
+ }
}
- info.setBoundsInParent(mTempRect);
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- }
- protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
- Bundle arguments) {
- if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
- ClickableSpan span = getSpanForOffset(virtualViewId);
+ protected void onPopulateNodeForVirtualView(
+ int virtualViewId,
+ AccessibilityNodeInfoCompat info) {
+ final ClickableSpan span = getSpanForOffset(virtualViewId);
if (span != null) {
- span.onClick(mView);
- return true;
+ info.setContentDescription(getTextForSpan(span));
} else {
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ info.setContentDescription(mView.getText());
}
+ info.setFocusable(true);
+ info.setClickable(true);
+ getBoundsForSpan(span, mTempRect);
+ if (mTempRect.isEmpty()) {
+ Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
+ mTempRect.set(0, 0, 1, 1);
+ }
+ info.setBoundsInParent(mTempRect);
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
}
- return false;
- }
- private ClickableSpan getSpanForOffset(int offset) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (spans.length == 1) {
- return spans[0];
+ protected boolean onPerformActionForVirtualView(
+ int virtualViewId,
+ int action,
+ Bundle arguments) {
+ if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
+ ClickableSpan span = getSpanForOffset(virtualViewId);
+ if (span != null) {
+ span.onClick(mView);
+ return true;
+ } else {
+ Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ }
}
+ return false;
}
- return null;
- }
- private CharSequence getTextForSpan(ClickableSpan span) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- return spannedText.subSequence(spannedText.getSpanStart(span),
- spannedText.getSpanEnd(span));
+ private ClickableSpan getSpanForOffset(int offset) {
+ CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ Spanned spannedText = (Spanned) text;
+ ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
+ if (spans.length == 1) {
+ return spans[0];
+ }
+ }
+ return null;
}
- return text;
- }
- // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the
- // section on the first line.
- private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
- CharSequence text = mView.getText();
- outRect.setEmpty();
- if (text instanceof Spanned) {
- final Layout layout = mView.getLayout();
- if (layout != null) {
+ private CharSequence getTextForSpan(ClickableSpan span) {
+ CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
Spanned spannedText = (Spanned) text;
- final int spanStart = spannedText.getSpanStart(span);
- final int spanEnd = spannedText.getSpanEnd(span);
- final float xStart = layout.getPrimaryHorizontal(spanStart);
- final float xEnd = layout.getPrimaryHorizontal(spanEnd);
- final int lineStart = layout.getLineForOffset(spanStart);
- final int lineEnd = layout.getLineForOffset(spanEnd);
- layout.getLineBounds(lineStart, outRect);
- if (lineEnd == lineStart) {
- // If the span is on a single line, adjust both the left and right bounds
- // so outrect is exactly bounding the span.
- outRect.left = (int) Math.min(xStart, xEnd);
- outRect.right = (int) Math.max(xStart, xEnd);
- } else {
- // If the span wraps across multiple lines, only use the first line (as returned
- // by layout.getLineBounds above), and adjust the "start" of outrect to where
- // the span starts, leaving the "end" of outrect at the end of the line.
- // ("start" being left for LTR, and right for RTL)
- if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
- outRect.right = (int) xStart;
+ return spannedText.subSequence(
+ spannedText.getSpanStart(span),
+ spannedText.getSpanEnd(span));
+ }
+ return text;
+ }
+
+ // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for
+ // the section on the first line.
+ private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
+ CharSequence text = mView.getText();
+ outRect.setEmpty();
+ if (text instanceof Spanned) {
+ final Layout layout = mView.getLayout();
+ if (layout != null) {
+ Spanned spannedText = (Spanned) text;
+ final int spanStart = spannedText.getSpanStart(span);
+ final int spanEnd = spannedText.getSpanEnd(span);
+ final float xStart = layout.getPrimaryHorizontal(spanStart);
+ final float xEnd = layout.getPrimaryHorizontal(spanEnd);
+ final int lineStart = layout.getLineForOffset(spanStart);
+ final int lineEnd = layout.getLineForOffset(spanEnd);
+ layout.getLineBounds(lineStart, outRect);
+ if (lineEnd == lineStart) {
+ // If the span is on a single line, adjust both the left and right bounds
+ // so outrect is exactly bounding the span.
+ outRect.left = (int) Math.min(xStart, xEnd);
+ outRect.right = (int) Math.max(xStart, xEnd);
} else {
- outRect.left = (int) xStart;
+ // If the span wraps across multiple lines, only use the first line (as
+ // returned by layout.getLineBounds above), and adjust the "start" of
+ // outrect to where the span starts, leaving the "end" of outrect at the end
+ // of the line. ("start" being left for LTR, and right for RTL)
+ if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
+ outRect.right = (int) xStart;
+ } else {
+ outRect.left = (int) xStart;
+ }
}
- }
- // Offset for padding
- outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
+ // Offset for padding
+ outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
+ }
}
+ return outRect;
}
- return outRect;
- }
- // Compat implementation of TextView#getOffsetForPosition().
+ // Compat implementation of TextView#getOffsetForPosition().
- private static int getOffsetForPosition(TextView view, float x, float y) {
- if (view.getLayout() == null) return -1;
- final int line = getLineAtCoordinate(view, y);
- return getOffsetAtCoordinate(view, line, x);
- }
+ private static int getOffsetForPosition(TextView view, float x, float y) {
+ if (view.getLayout() == null) return -1;
+ final int line = getLineAtCoordinate(view, y);
+ return getOffsetAtCoordinate(view, line, x);
+ }
- private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
- x -= view.getTotalPaddingLeft();
- // Clamp the position to inside of the view.
- x = Math.max(0.0f, x);
- x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
- x += view.getScrollX();
- return x;
- }
+ private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
+ x -= view.getTotalPaddingLeft();
+ // Clamp the position to inside of the view.
+ x = Math.max(0.0f, x);
+ x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
+ x += view.getScrollX();
+ return x;
+ }
- private static int getLineAtCoordinate(TextView view, float y) {
- y -= view.getTotalPaddingTop();
- // Clamp the position to inside of the view.
- y = Math.max(0.0f, y);
- y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
- y += view.getScrollY();
- return view.getLayout().getLineForVertical((int) y);
- }
+ private static int getLineAtCoordinate(TextView view, float y) {
+ y -= view.getTotalPaddingTop();
+ // Clamp the position to inside of the view.
+ y = Math.max(0.0f, y);
+ y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
+ y += view.getScrollY();
+ return view.getLayout().getLineForVertical((int) y);
+ }
- private static int getOffsetAtCoordinate(TextView view, int line, float x) {
- x = convertToLocalHorizontalCoordinate(view, x);
- return view.getLayout().getOffsetForHorizontal(line, x);
+ private static int getOffsetAtCoordinate(TextView view, int line, float x) {
+ x = convertToLocalHorizontalCoordinate(view, x);
+ return view.getLayout().getOffsetForHorizontal(line, x);
+ }
}
}
diff --git a/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
index 844e73e9..6228e6fc 100644
--- a/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
+++ b/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
@@ -14,29 +14,35 @@
* limitations under the License.
*/
-package com.android.setupwizardlib.test;
+package com.android.setupwizardlib.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.text.BidiFormatter;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import android.support.v4.widget.ExploreByTouchHelper;
import android.text.SpannableStringBuilder;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.setupwizardlib.span.LinkSpan;
-import com.android.setupwizardlib.util.LinkAccessibilityHelper;
+import com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,13 +58,12 @@ public class LinkAccessibilityHelperTest {
private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
private TextView mTextView;
- private TestLinkAccessibilityHelper mHelper;
+ private TestPreOLinkAccessibilityHelper mHelper;
private DisplayMetrics mDisplayMetrics;
@Test
public void testGetVirtualViewAt() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
assertEquals("Virtual view ID should be 1", 1, virtualViewId);
@@ -66,7 +71,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testGetVirtualViewAtHost() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
assertEquals("Virtual view ID should be INVALID_ID",
@@ -75,7 +79,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testGetVisibleVirtualViews() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
List<Integer> virtualViewIds = new ArrayList<>();
mHelper.getVisibleVirtualViews(virtualViewIds);
@@ -86,7 +89,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateEventForVirtualView() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityEvent event = AccessibilityEvent.obtain();
mHelper.onPopulateEventForVirtualView(1, event);
@@ -100,7 +102,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateEventForVirtualViewHost() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityEvent event = AccessibilityEvent.obtain();
mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
@@ -113,7 +114,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateNodeForVirtualView() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
mHelper.onPopulateNodeForVirtualView(1, info);
@@ -132,7 +132,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testNullLayout() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
// Setting the padding will cause the layout to be null-ed out.
mTextView.setPadding(1, 1, 1, 1);
@@ -150,7 +149,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testRtlLayout() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
initTextView(ssb);
@@ -170,7 +168,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testMultilineLink() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
SpannableStringBuilder ssb = new SpannableStringBuilder(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+ "Praesent accumsan efficitur eros eu porttitor.");
@@ -192,7 +189,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testRtlMultilineLink() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+ "דפים המחשב מיזמים ב.";
@@ -216,7 +212,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testBidiMultilineLink() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+ "דפים המחשב מיזמים ב.";
@@ -243,6 +238,70 @@ public class LinkAccessibilityHelperTest {
info.recycle();
}
+ @Test
+ public void testMethodDelegation() {
+ initTextView();
+ ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
+ LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
+
+ AccessibilityEvent accessibilityEvent =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+ helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
+ verify(delegate).sendAccessibilityEvent(
+ same(mTextView),
+ eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
+
+ helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
+ verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
+
+ helper.performAccessibilityAction(
+ mTextView,
+ AccessibilityActionCompat.ACTION_CLICK.getId(),
+ Bundle.EMPTY);
+ verify(delegate).performAccessibilityAction(
+ same(mTextView),
+ eq(AccessibilityActionCompat.ACTION_CLICK.getId()),
+ eq(Bundle.EMPTY));
+
+ helper.dispatchPopulateAccessibilityEvent(
+ mTextView,
+ accessibilityEvent);
+ verify(delegate).dispatchPopulateAccessibilityEvent(
+ same(mTextView),
+ same(accessibilityEvent));
+
+ MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0);
+ helper.dispatchHoverEvent(motionEvent);
+ verify(delegate).dispatchHoverEvent(eq(motionEvent));
+
+ helper.getAccessibilityNodeProvider(mTextView);
+ verify(delegate).getAccessibilityNodeProvider(same(mTextView));
+
+ helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
+ verify(delegate).onInitializeAccessibilityEvent(
+ same(mTextView),
+ eq(accessibilityEvent));
+
+ AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
+ helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
+ verify(delegate).onInitializeAccessibilityNodeInfo(
+ same(mTextView),
+ same(accessibilityNodeInfo));
+
+ helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
+ verify(delegate).onPopulateAccessibilityEvent(
+ same(mTextView),
+ same(accessibilityEvent));
+
+ FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
+ helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
+ verify(delegate).onRequestSendAccessibilityEvent(
+ same(parent),
+ same(mTextView),
+ same(accessibilityEvent));
+ }
+
private void initTextView() {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
@@ -254,7 +313,7 @@ public class LinkAccessibilityHelperTest {
mTextView.setSingleLine(false);
mTextView.setText(text);
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
- mHelper = new TestLinkAccessibilityHelper(mTextView);
+ mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500),
View.MeasureSpec.EXACTLY);
@@ -270,9 +329,9 @@ public class LinkAccessibilityHelperTest {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
}
- private static class TestLinkAccessibilityHelper extends LinkAccessibilityHelper {
+ public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
- TestLinkAccessibilityHelper(TextView view) {
+ TestPreOLinkAccessibilityHelper(TextView view) {
super(view);
}
diff --git a/com/android/setupwizardlib/util/PartnerTest.java b/com/android/setupwizardlib/util/PartnerTest.java
index f47eef18..aeb678fa 100644
--- a/com/android/setupwizardlib/util/PartnerTest.java
+++ b/com/android/setupwizardlib/util/PartnerTest.java
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build.VERSION;
@@ -40,20 +41,25 @@ import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.util.Partner.ResourceEntry;
+import com.android.setupwizardlib.util.PartnerTest.ShadowApplicationPackageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
-import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowResources;
import java.util.Arrays;
import java.util.Collections;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(
+ constants = BuildConfig.class,
+ sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK },
+ shadows = ShadowApplicationPackageManager.class)
public class PartnerTest {
private static final String ACTION_PARTNER_CUSTOMIZATION =
@@ -62,7 +68,7 @@ public class PartnerTest {
private Context mContext;
private Resources mPartnerResources;
- private TestPackageManager mPackageManager;
+ private ShadowApplicationPackageManager mPackageManager;
@Before
public void setUp() throws Exception {
@@ -71,8 +77,9 @@ public class PartnerTest {
mContext = spy(application);
mPartnerResources = spy(ShadowResources.getSystem());
- mPackageManager = new TestPackageManager();
- RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+ mPackageManager =
+ (ShadowApplicationPackageManager) Shadows.shadowOf(application.getPackageManager());
+ mPackageManager.partnerResources = mPartnerResources;
}
@Test
@@ -173,13 +180,18 @@ public class PartnerTest {
return info;
}
- private class TestPackageManager extends DefaultPackageManager {
+ @Implements(className = "android.app.ApplicationPackageManager")
+ public static class ShadowApplicationPackageManager extends
+ org.robolectric.shadows.ShadowApplicationPackageManager {
+ public Resources partnerResources;
+
+ @Implementation
@Override
public Resources getResourcesForApplication(ApplicationInfo app)
throws NameNotFoundException {
if (app != null && "test.partner.package".equals(app.packageName)) {
- return mPartnerResources;
+ return partnerResources;
} else {
return super.getResourcesForApplication(app);
}
diff --git a/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
index ffa228d5..ddf59ca4 100644
--- a/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
+++ b/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
@@ -48,7 +48,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
-import org.robolectric.internal.Shadow;
+import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowMediaPlayer;
import org.robolectric.util.ReflectionHelpers;
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/BatteryMeterView.java b/com/android/systemui/BatteryMeterView.java
index 2b31967c..2fe66a14 100644
--- a/com/android/systemui/BatteryMeterView.java
+++ b/com/android/systemui/BatteryMeterView.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import android.animation.ArgbEvaluator;
@@ -52,6 +54,7 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils.DisableStateTracker;
import java.text.NumberFormat;
@@ -101,6 +104,9 @@ public class BatteryMeterView extends LinearLayout implements
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+ addOnAttachStateChangeListener(
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
+
mSlotBattery = context.getString(
com.android.internal.R.string.status_bar_battery);
mBatteryIconView = new ImageView(context);
diff --git a/com/android/systemui/assist/AssistManager.java b/com/android/systemui/assist/AssistManager.java
index c5eebccb..8a8bafaf 100644
--- a/com/android/systemui/assist/AssistManager.java
+++ b/com/android/systemui/assist/AssistManager.java
@@ -61,6 +61,7 @@ public class AssistManager implements ConfigurationChangedReceiver {
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
protected final AssistUtils mAssistUtils;
+ private final boolean mShouldEnableOrb;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -96,6 +97,7 @@ public class AssistManager implements ConfigurationChangedReceiver {
| ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
| ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
onConfigurationChanged(context.getResources().getConfiguration());
+ mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic();
}
protected void registerVoiceInteractionSessionListener() {
@@ -179,7 +181,9 @@ public class AssistManager implements ConfigurationChangedReceiver {
private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
maybeSwapSearchIcon(assistComponent, isService);
- mView.show(true /* show */, true /* animate */);
+ if (mShouldEnableOrb) {
+ mView.show(true /* show */, true /* animate */);
+ }
}
private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
diff --git a/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 5c99961e..debda210 100644
--- a/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -16,8 +16,13 @@
package com.android.systemui.doze;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
@@ -34,6 +39,10 @@ import java.util.Arrays;
public class AlwaysOnDisplayPolicy {
public static final String TAG = "AlwaysOnDisplayPolicy";
+ private static final long DEFAULT_PROX_SCREEN_OFF_DELAY_MS = 10 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_TRIGGER_MS = 2 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_PERIOD_MS = 5 * DateUtils.SECOND_IN_MILLIS;
+
static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
@@ -46,7 +55,7 @@ public class AlwaysOnDisplayPolicy {
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_SCREEN_BRIGHTNESS_ARRAY
*/
- public final int[] screenBrightnessArray;
+ public int[] screenBrightnessArray;
/**
* Integer array to map ambient brightness type to dimming scrim.
@@ -54,7 +63,7 @@ public class AlwaysOnDisplayPolicy {
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_DIMMING_SCRIM_ARRAY
*/
- public final int[] dimmingScrimArray;
+ public int[] dimmingScrimArray;
/**
* Delay time(ms) from covering the prox to turning off the screen.
@@ -62,7 +71,7 @@ public class AlwaysOnDisplayPolicy {
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_SCREEN_OFF_DELAY_MS
*/
- public final long proxScreenOffDelayMs;
+ public long proxScreenOffDelayMs;
/**
* The threshold time(ms) to trigger the cooldown timer, which will
@@ -71,7 +80,7 @@ public class AlwaysOnDisplayPolicy {
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_TRIGGER_MS
*/
- public final long proxCooldownTriggerMs;
+ public long proxCooldownTriggerMs;
/**
* The period(ms) to turning off the prox sensor if
@@ -80,43 +89,78 @@ public class AlwaysOnDisplayPolicy {
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_PERIOD_MS
*/
- public final long proxCooldownPeriodMs;
+ public long proxCooldownPeriodMs;
private final KeyValueListParser mParser;
+ private final Context mContext;
+ private SettingsObserver mSettingsObserver;
public AlwaysOnDisplayPolicy(Context context) {
- final Resources resources = context.getResources();
+ mContext = context;
mParser = new KeyValueListParser(',');
-
- final String value = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
-
- try {
- mParser.setString(value);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Bad AOD constants");
- }
-
- proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
- 10 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
- 2 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
- 5 * DateUtils.SECOND_IN_MILLIS);
- screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness));
- dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ mSettingsObserver = new SettingsObserver(context.getMainThreadHandler());
+ mSettingsObserver.observe();
}
private int[] parseIntArray(final String key, final int[] defaultArray) {
final String value = mParser.getString(key, null);
if (value != null) {
- return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
- Integer::parseInt).toArray();
+ try {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } catch (NumberFormatException e) {
+ return defaultArray;
+ }
} else {
return defaultArray;
}
}
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI
+ = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,
+ false, this, UserHandle.USER_ALL);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (uri == null || ALWAYS_ON_DISPLAY_CONSTANTS_URI.equals(uri)) {
+ final Resources resources = mContext.getResources();
+ final String value = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad AOD constants");
+ }
+
+ proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
+ DEFAULT_PROX_SCREEN_OFF_DELAY_MS);
+ proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
+ DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
+ proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
+ DEFAULT_PROX_COOLDOWN_PERIOD_MS);
+ screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_brightness));
+ dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+ }
+ }
}
diff --git a/com/android/systemui/doze/DozePauser.java b/com/android/systemui/doze/DozePauser.java
index 76a19021..58f14483 100644
--- a/com/android/systemui/doze/DozePauser.java
+++ b/com/android/systemui/doze/DozePauser.java
@@ -28,20 +28,21 @@ public class DozePauser implements DozeMachine.Part {
public static final String TAG = DozePauser.class.getSimpleName();
private final AlarmTimeout mPauseTimeout;
private final DozeMachine mMachine;
- private final long mTimeoutMs;
+ private final AlwaysOnDisplayPolicy mPolicy;
public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
AlwaysOnDisplayPolicy policy) {
mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
- mTimeoutMs = policy.proxScreenOffDelayMs;
+ mPolicy = policy;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
- mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mPauseTimeout.schedule(mPolicy.proxScreenOffDelayMs,
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
break;
default:
mPauseTimeout.cancel();
diff --git a/com/android/systemui/doze/DozeScreenBrightness.java b/com/android/systemui/doze/DozeScreenBrightness.java
index 03407e2b..4bb4e79c 100644
--- a/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/com/android/systemui/doze/DozeScreenBrightness.java
@@ -22,6 +22,7 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
+import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
@@ -94,9 +95,14 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
@Override
public void onSensorChanged(SensorEvent event) {
- if (mRegistered) {
- mLastSensorValue = (int) event.values[0];
- updateBrightnessAndReady();
+ Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
+ try {
+ if (mRegistered) {
+ mLastSensorValue = (int) event.values[0];
+ updateBrightnessAndReady();
+ }
+ } finally {
+ Trace.endSection();
}
}
diff --git a/com/android/systemui/globalactions/GlobalActionsDialog.java b/com/android/systemui/globalactions/GlobalActionsDialog.java
index 4cbbbd6c..189badfc 100644
--- a/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1280,7 +1280,23 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn
mGradientDrawable.setScreenSize(displaySize.x, displaySize.y);
GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ?
WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
- mGradientDrawable.setColors(colors, false);
+ updateColors(colors, false /* animate */);
+ }
+
+ /**
+ * Updates background and system bars according to current GradientColors.
+ * @param colors Colors and hints to use.
+ * @param animate Interpolates gradient if true, just sets otherwise.
+ */
+ private void updateColors(GradientColors colors, boolean animate) {
+ mGradientDrawable.setColors(colors, animate);
+ View decorView = getWindow().getDecorView();
+ if (colors.supportsDarkText()) {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
+ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ } else {
+ decorView.setSystemUiVisibility(0);
+ }
}
@Override
@@ -1350,11 +1366,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn
public void onColorsChanged(ColorExtractor extractor, int which) {
if (mKeyguardShowing) {
if ((WallpaperManager.FLAG_LOCK & which) != 0) {
- mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_LOCK));
+ updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
+ true /* animate */);
}
} else {
if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
- mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM));
+ updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
+ true /* animate */);
}
}
}
diff --git a/com/android/systemui/keyguard/KeyguardViewMediator.java b/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3eb68f52..28adca97 100644
--- a/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
@@ -239,6 +240,9 @@ public class KeyguardViewMediator extends SystemUI {
// answer whether the input should be restricted)
private boolean mShowing;
+ // display id of the secondary display on which we have put a keyguard window
+ private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+
/** Cached value of #isInputRestricted */
private boolean mInputRestricted;
@@ -646,6 +650,13 @@ public class KeyguardViewMediator extends SystemUI {
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
+
+ @Override
+ public void onSecondaryDisplayShowingChanged(int displayId) {
+ synchronized (KeyguardViewMediator.this) {
+ setShowingLocked(mShowing, displayId, false);
+ }
+ }
};
public void userActivity() {
@@ -670,7 +681,7 @@ public class KeyguardViewMediator extends SystemUI {
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiver(mBroadcastReceiver, filter);
- mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
+ mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -685,7 +696,8 @@ public class KeyguardViewMediator extends SystemUI {
com.android.keyguard.R.bool.config_enableKeyguardService)) {
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ KeyguardUpdateMonitor.getCurrentUser()),
+ mSecondaryDisplayShowing, true /* forceCallbacks */);
}
mStatusBarKeyguardViewManager =
@@ -1694,10 +1706,10 @@ public class KeyguardViewMediator extends SystemUI {
playSound(mTrustedSoundId);
}
- private void updateActivityLockScreenState(boolean showing) {
+ private void updateActivityLockScreenState(boolean showing, int secondaryDisplayShowing) {
mUiOffloadThread.submit(() -> {
try {
- ActivityManager.getService().setLockScreenShown(showing);
+ ActivityManager.getService().setLockScreenShown(showing, secondaryDisplayShowing);
} catch (RemoteException e) {
}
});
@@ -2060,30 +2072,39 @@ public class KeyguardViewMediator extends SystemUI {
}
private void setShowingLocked(boolean showing) {
- setShowingLocked(showing, false /* forceCallbacks */);
+ setShowingLocked(showing, mSecondaryDisplayShowing, false /* forceCallbacks */);
}
- private void setShowingLocked(boolean showing, boolean forceCallbacks) {
- if (showing != mShowing || forceCallbacks) {
+ private void setShowingLocked(
+ boolean showing, int secondaryDisplayShowing, boolean forceCallbacks) {
+ final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks;
+ if (notifyDefaultDisplayCallbacks || secondaryDisplayShowing != mSecondaryDisplayShowing) {
mShowing = showing;
- int size = mKeyguardStateCallbacks.size();
- for (int i = size - 1; i >= 0; i--) {
- IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
- try {
- callback.onShowingStateChanged(showing);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onShowingStateChanged", e);
- if (e instanceof DeadObjectException) {
- mKeyguardStateCallbacks.remove(callback);
- }
+ mSecondaryDisplayShowing = secondaryDisplayShowing;
+ if (notifyDefaultDisplayCallbacks) {
+ notifyDefaultDisplayCallbacks(showing);
+ }
+ updateActivityLockScreenState(showing, secondaryDisplayShowing);
+ }
+ }
+
+ private void notifyDefaultDisplayCallbacks(boolean showing) {
+ int size = mKeyguardStateCallbacks.size();
+ for (int i = size - 1; i >= 0; i--) {
+ IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
+ try {
+ callback.onShowingStateChanged(showing);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onShowingStateChanged", e);
+ if (e instanceof DeadObjectException) {
+ mKeyguardStateCallbacks.remove(callback);
}
}
- updateInputRestrictedLocked();
- mUiOffloadThread.submit(() -> {
- mTrustManager.reportKeyguardShowingChanged();
- });
- updateActivityLockScreenState(showing);
}
+ updateInputRestrictedLocked();
+ mUiOffloadThread.submit(() -> {
+ mTrustManager.reportKeyguardShowingChanged();
+ });
}
private void notifyTrustedChangedLocked(boolean trusted) {
diff --git a/com/android/systemui/media/NotificationPlayer.java b/com/android/systemui/media/NotificationPlayer.java
index 50720e9f..b5c0d538 100644
--- a/com/android/systemui/media/NotificationPlayer.java
+++ b/com/android/systemui/media/NotificationPlayer.java
@@ -29,6 +29,8 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.LinkedList;
/**
@@ -57,8 +59,12 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
}
}
- private LinkedList<Command> mCmdQueue = new LinkedList();
+ private final LinkedList<Command> mCmdQueue = new LinkedList<Command>();
+ private final Object mCompletionHandlingLock = new Object();
+ @GuardedBy("mCompletionHandlingLock")
+ private CreationAndCompletionThread mCompletionThread;
+ @GuardedBy("mCompletionHandlingLock")
private Looper mLooper;
/*
@@ -76,7 +82,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
public void run() {
Looper.prepare();
+ // ok to modify mLooper as here we are
+ // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
+ if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
@@ -97,7 +106,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
- synchronized(mQueueAudioFocusLock) {
+ synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
if (DEBUG) Log.d(mTag, "requesting AudioFocus");
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
@@ -129,7 +138,9 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
+ if (DEBUG) { Log.d(mTag, "player.start"); }
if (mPlayer != null) {
+ if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
mPlayer.release();
}
mPlayer = player;
@@ -148,7 +159,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
// is playing, let it continue until we're done, so there
// is less of a glitch.
try {
- if (DEBUG) Log.d(mTag, "Starting playback");
+ if (DEBUG) { Log.d(mTag, "startSound()"); }
//-----------------------------------
// This is were we deviate from the AsyncPlayer implementation and create the
// MediaPlayer in a new thread with which we're synchronized
@@ -158,10 +169,11 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
// matters
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+ if (DEBUG) { Log.d(mTag, "in startSound quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = new CreationAndCompletionThread(cmd);
- synchronized(mCompletionThread) {
+ synchronized (mCompletionThread) {
mCompletionThread.start();
mCompletionThread.wait();
}
@@ -209,13 +221,18 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
mPlayer = null;
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
+ if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
+ synchronized (mCompletionHandlingLock) {
+ if ((mLooper != null) &&
+ (mLooper.getThread().getState() != Thread.State.TERMINATED))
+ {
+ if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
+ mLooper.quit();
+ }
}
} else {
Log.w(mTag, "STOP command without a player");
@@ -250,9 +267,11 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
}
// if there are no more sounds to play, end the Looper to listen for media completion
synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- synchronized(mCompletionHandlingLock) {
- if(mLooper != null) {
+ synchronized(mCompletionHandlingLock) {
+ if (DEBUG) { Log.d(mTag, "onCompletion queue size=" + mCmdQueue.size()); }
+ if ((mCmdQueue.size() == 0)) {
+ if (mLooper != null) {
+ if (DEBUG) { Log.d(mTag, "in onCompletion quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = null;
@@ -269,13 +288,20 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
}
private String mTag;
+
+ @GuardedBy("mCmdQueue")
private CmdThread mThread;
- private CreationAndCompletionThread mCompletionThread;
- private final Object mCompletionHandlingLock = new Object();
+
private MediaPlayer mPlayer;
+
+
+ @GuardedBy("mCmdQueue")
private PowerManager.WakeLock mWakeLock;
+
private final Object mQueueAudioFocusLock = new Object();
- private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock
+ @GuardedBy("mQueueAudioFocusLock")
+ private AudioManager mAudioManagerWithAudioFocus;
+
private int mNotificationRampTimeMs = 0;
// The current state according to the caller. Reality lags behind
@@ -311,6 +337,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
*/
@Deprecated
public void play(Context context, Uri uri, boolean looping, int stream) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -339,6 +366,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
*/
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
@@ -357,6 +385,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
* at this point. Calling this multiple times has no ill effects.
*/
public void stop() {
+ if (DEBUG) { Log.d(mTag, "stop"); }
synchronized (mCmdQueue) {
// This check allows stop to be called multiple times without starting
// a thread that ends up doing nothing.
@@ -370,6 +399,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
}
}
+ @GuardedBy("mCmdQueue")
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);
if (mThread == null) {
@@ -393,22 +423,26 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener
* @hide
*/
public void setUsesWakeLock(Context context) {
- if (mWakeLock != null || mThread != null) {
- // if either of these has happened, we've already played something.
- // and our releases will be out of sync.
- throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
- + " mThread=" + mThread);
+ synchronized (mCmdQueue) {
+ if (mWakeLock != null || mThread != null) {
+ // if either of these has happened, we've already played something.
+ // and our releases will be out of sync.
+ throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
+ + " mThread=" + mThread);
+ }
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
+ @GuardedBy("mCmdQueue")
private void acquireWakeLock() {
if (mWakeLock != null) {
mWakeLock.acquire();
}
}
+ @GuardedBy("mCmdQueue")
private void releaseWakeLock() {
if (mWakeLock != null) {
mWakeLock.release();
diff --git a/com/android/systemui/pip/phone/PipManager.java b/com/android/systemui/pip/phone/PipManager.java
index b3f992db..f8996aae 100644
--- a/com/android/systemui/pip/phone/PipManager.java
+++ b/com/android/systemui/pip/phone/PipManager.java
@@ -16,7 +16,8 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
@@ -30,6 +31,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
import android.view.IWindowManager;
@@ -70,11 +72,11 @@ public class PipManager implements BasePipManager {
*/
TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
- mNotificationController.onActivityPinned(packageName,
+ mNotificationController.onActivityPinned(packageName, userId,
true /* deferUntilAnimationEnds */);
SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
@@ -82,13 +84,15 @@ public class PipManager implements BasePipManager {
@Override
public void onActivityUnpinned() {
- ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
- mActivityManager);
- mMenuController.onActivityUnpinned(topPipActivity);
- mTouchHandler.onActivityUnpinned(topPipActivity);
- mNotificationController.onActivityUnpinned(topPipActivity);
-
- SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
+ final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(
+ mContext, mActivityManager);
+ final ComponentName topActivity = topPipActivityInfo.first;
+ final int userId = topActivity != null ? topPipActivityInfo.second : 0;
+ mMenuController.onActivityUnpinned();
+ mTouchHandler.onActivityUnpinned(topActivity);
+ mNotificationController.onActivityUnpinned(topActivity, userId);
+
+ SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null);
}
@Override
@@ -196,7 +200,8 @@ public class PipManager implements BasePipManager {
public final void onBusEvent(ExpandPipEvent event) {
if (event.clearThumbnailWindows) {
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null && stackInfo.taskIds != null) {
SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext);
for (int taskId : stackInfo.taskIds) {
diff --git a/com/android/systemui/pip/phone/PipMediaController.java b/com/android/systemui/pip/phone/PipMediaController.java
index b3a0794f..174a7ef1 100644
--- a/com/android/systemui/pip/phone/PipMediaController.java
+++ b/com/android/systemui/pip/phone/PipMediaController.java
@@ -230,7 +230,7 @@ public class PipMediaController {
private void resolveActiveMediaController(List<MediaController> controllers) {
if (controllers != null) {
final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
- mActivityManager);
+ mActivityManager).first;
if (topActivity != null) {
for (int i = 0; i < controllers.size(); i++) {
final MediaController controller = controllers.get(i);
diff --git a/com/android/systemui/pip/phone/PipMenuActivityController.java b/com/android/systemui/pip/phone/PipMenuActivityController.java
index 34666fb3..9fb201b8 100644
--- a/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -16,7 +16,8 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
@@ -223,7 +224,7 @@ public class PipMenuActivityController {
}
}
- public void onActivityUnpinned(ComponentName topPipActivity) {
+ public void onActivityUnpinned() {
hideMenu();
setStartActivityRequested(false);
}
@@ -383,7 +384,8 @@ public class PipMenuActivityController {
private void startMenuActivity(int menuState, Rect stackBounds, Rect movementBounds,
boolean allowMenuTimeout, boolean willResizeMenu) {
try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
pinnedStackInfo.taskIds.length > 0) {
Intent intent = new Intent(mContext, PipMenuActivity.class);
@@ -421,7 +423,8 @@ public class PipMenuActivityController {
// Fetch the pinned stack bounds
Rect stackBounds = null;
try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null) {
stackBounds = pinnedStackInfo.bounds;
}
diff --git a/com/android/systemui/pip/phone/PipMotionHelper.java b/com/android/systemui/pip/phone/PipMotionHelper.java
index cebb22f0..21a836c0 100644
--- a/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,8 +16,10 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_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_PINNED;
import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
@@ -121,7 +123,8 @@ public class PipMotionHelper implements Handler.Callback {
void synchronizePinnedStackBounds() {
cancelAnimations();
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo =
+ mActivityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null) {
mBounds.set(stackInfo.bounds);
}
@@ -158,13 +161,7 @@ public class PipMotionHelper implements Handler.Callback {
mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
- if (skipAnimation) {
- mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
- } else {
- mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
- true /* allowResizeInDockedMode */, true /* preserveWindows */,
- true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
- }
+ mActivityManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
} catch (RemoteException e) {
Log.e(TAG, "Error expanding PiP activity", e);
}
@@ -182,7 +179,7 @@ public class PipMotionHelper implements Handler.Callback {
mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
- mActivityManager.removeStack(PINNED_STACK_ID);
+ mActivityManager.removeStacksInWindowingModes(new int[]{ WINDOWING_MODE_PINNED });
} catch (RemoteException e) {
Log.e(TAG, "Failed to remove PiP", e);
}
@@ -529,14 +526,15 @@ public class PipMotionHelper implements Handler.Callback {
Rect toBounds = (Rect) args.arg1;
int duration = args.argi1;
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo == null) {
// In the case where we've already re-expanded or dismissed the PiP, then
// just skip the resize
return true;
}
- mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+ mActivityManager.resizeStack(stackInfo.stackId, toBounds,
false /* allowResizeInDockedMode */, true /* preserveWindows */,
true /* animate */, duration);
mBounds.set(toBounds);
diff --git a/com/android/systemui/pip/phone/PipNotificationController.java b/com/android/systemui/pip/phone/PipNotificationController.java
index 696fdbc8..6d083e9d 100644
--- a/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/com/android/systemui/pip/phone/PipNotificationController.java
@@ -35,10 +35,15 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.graphics.drawable.Icon;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
import android.util.Log;
+import android.util.Pair;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
@@ -57,22 +62,29 @@ public class PipNotificationController {
private IActivityManager mActivityManager;
private AppOpsManager mAppOpsManager;
private NotificationManager mNotificationManager;
+ private IconDrawableFactory mIconDrawableFactory;
private PipMotionHelper mMotionHelper;
// Used when building a deferred notification
private String mDeferredNotificationPackageName;
+ private int mDeferredNotificationUserId;
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
@Override
public void onOpChanged(String op, String packageName) {
try {
// Dismiss the PiP once the user disables the app ops setting for that package
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
- packageName, 0);
- if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName)
- != MODE_ALLOWED) {
- mMotionHelper.dismissPip();
+ final Pair<ComponentName, Integer> topPipActivityInfo =
+ PipUtils.getTopPinnedActivity(mContext, mActivityManager);
+ if (topPipActivityInfo.first != null) {
+ final ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
+ if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
+ mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
+ packageName) != MODE_ALLOWED) {
+ mMotionHelper.dismissPip();
+ }
}
} catch (NameNotFoundException e) {
// Unregister the listener if the package can't be found
@@ -88,16 +100,18 @@ public class PipNotificationController {
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mNotificationManager = NotificationManager.from(context);
mMotionHelper = motionHelper;
+ mIconDrawableFactory = IconDrawableFactory.newInstance(context);
}
- public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
+ public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) {
// Clear any existing notification
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
if (deferUntilAnimationEnds) {
mDeferredNotificationPackageName = packageName;
+ mDeferredNotificationUserId = userId;
} else {
- showNotificationForApp(mDeferredNotificationPackageName);
+ showNotificationForApp(packageName, userId);
}
// Register for changes to the app ops setting for this package while it is in PiP
@@ -106,22 +120,25 @@ public class PipNotificationController {
public void onPinnedStackAnimationEnded() {
if (mDeferredNotificationPackageName != null) {
- showNotificationForApp(mDeferredNotificationPackageName);
+ showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId);
mDeferredNotificationPackageName = null;
+ mDeferredNotificationUserId = 0;
}
}
- public void onActivityUnpinned(ComponentName topPipActivity) {
+ public void onActivityUnpinned(ComponentName topPipActivity, int userId) {
// Unregister for changes to the previously PiP'ed package
unregisterAppOpsListener();
// Reset the deferred notification package
mDeferredNotificationPackageName = null;
+ mDeferredNotificationUserId = 0;
if (topPipActivity != null) {
// onActivityUnpinned() is only called after the transition is complete, so we don't
// need to defer until the animation ends to update the notification
- onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
+ onActivityPinned(topPipActivity.getPackageName(), userId,
+ false /* deferUntilAnimationEnds */);
} else {
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
}
@@ -130,20 +147,27 @@ public class PipNotificationController {
/**
* Builds and shows the notification for the given app.
*/
- private void showNotificationForApp(String packageName) {
+ private void showNotificationForApp(String packageName, int userId) {
// Build a new notification
- final Notification.Builder builder =
- new Notification.Builder(mContext, NotificationChannels.GENERAL)
- .setLocalOnly(true)
- .setOngoing(true)
- .setSmallIcon(R.drawable.pip_notification_icon)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- if (updateNotificationForApp(builder, packageName)) {
- SystemUI.overrideNotificationAppName(mContext, builder);
-
- // Show the new notification
- mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ try {
+ final UserHandle user = UserHandle.of(userId);
+ final Context userContext = mContext.createPackageContextAsUser(
+ mContext.getPackageName(), 0, user);
+ final Notification.Builder builder =
+ new Notification.Builder(userContext, NotificationChannels.GENERAL)
+ .setLocalOnly(true)
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.pip_notification_icon)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ if (updateNotificationForApp(builder, packageName, user)) {
+ SystemUI.overrideNotificationAppName(mContext, builder);
+
+ // Show the new notification
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not show notification for application", e);
}
}
@@ -151,33 +175,33 @@ public class PipNotificationController {
* Updates the notification builder with app-specific information, returning whether it was
* successful.
*/
- private boolean updateNotificationForApp(Notification.Builder builder, String packageName) {
+ private boolean updateNotificationForApp(Notification.Builder builder, String packageName,
+ UserHandle user) throws NameNotFoundException {
final PackageManager pm = mContext.getPackageManager();
final ApplicationInfo appInfo;
try {
- appInfo = pm.getApplicationInfo(packageName, 0);
+ appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier());
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not update notification for application", e);
return false;
}
if (appInfo != null) {
- final String appName = pm.getApplicationLabel(appInfo).toString();
+ final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user)
+ .toString();
final String message = mContext.getString(R.string.pip_notification_message, appName);
final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
Uri.fromParts("package", packageName, null));
+ settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- final Icon appIcon = appInfo.icon != 0
- ? Icon.createWithResource(packageName, appInfo.icon)
- : Icon.createWithResource(Resources.getSystem(),
- com.android.internal.R.drawable.sym_def_app_icon);
+ final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo);
builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName))
.setContentText(message)
- .setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(),
- settingsIntent, FLAG_CANCEL_CURRENT))
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(),
+ settingsIntent, FLAG_CANCEL_CURRENT, null, user))
.setStyle(new Notification.BigTextStyle().bigText(message))
- .setLargeIcon(appIcon);
+ .setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap());
return true;
}
return false;
@@ -191,4 +215,17 @@ public class PipNotificationController {
private void unregisterAppOpsListener() {
mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
}
+
+ /**
+ * Bakes a drawable into a bitmap.
+ */
+ private Bitmap createBitmap(Drawable d) {
+ Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
+ Config.ARGB_8888);
+ Canvas c = new Canvas(bitmap);
+ d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ d.draw(c);
+ c.setBitmap(null);
+ return bitmap;
+ }
}
diff --git a/com/android/systemui/pip/phone/PipUtils.java b/com/android/systemui/pip/phone/PipUtils.java
index a8cdd1bd..2f53de96 100644
--- a/com/android/systemui/pip/phone/PipUtils.java
+++ b/com/android/systemui/pip/phone/PipUtils.java
@@ -16,7 +16,8 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
@@ -24,33 +25,35 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
public class PipUtils {
private static final String TAG = "PipUtils";
/**
- * @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if
- * none exists.
+ * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
+ * The component name may be null if no such activity exists.
*/
- public static ComponentName getTopPinnedActivity(Context context,
+ public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context,
IActivityManager activityManager) {
try {
final String sysUiPackageName = context.getPackageName();
- final StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID);
+ final StackInfo pinnedStackInfo =
+ activityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
pinnedStackInfo.taskIds.length > 0) {
for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) {
ComponentName cn = ComponentName.unflattenFromString(
pinnedStackInfo.taskNames[i]);
if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
- return cn;
+ return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]);
}
}
}
} catch (RemoteException e) {
Log.w(TAG, "Unable to get pinned stack.");
}
- return null;
+ return new Pair<>(null, 0);
}
}
diff --git a/com/android/systemui/pip/tv/PipManager.java b/com/android/systemui/pip/tv/PipManager.java
index e8c12952..e0445c16 100644
--- a/com/android/systemui/pip/tv/PipManager.java
+++ b/com/android/systemui/pip/tv/PipManager.java
@@ -53,7 +53,9 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.PINNED_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_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
/**
@@ -121,6 +123,7 @@ public class PipManager implements BasePipManager {
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
+ private int mPinnedStackId = INVALID_STACK_ID;
private ComponentName mPipComponentName;
private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
@@ -336,9 +339,11 @@ public class PipManager implements BasePipManager {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
if (removePipStack) {
try {
- mActivityManager.removeStack(PINNED_STACK_ID);
+ mActivityManager.removeStack(mPinnedStackId);
} catch (RemoteException e) {
Log.e(TAG, "removeStack failed", e);
+ } finally {
+ mPinnedStackId = INVALID_STACK_ID;
}
}
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -424,7 +429,7 @@ public class PipManager implements BasePipManager {
}
try {
int animationDurationMs = -1;
- mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+ mActivityManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
true, true, true, animationDurationMs);
} catch (RemoteException e) {
Log.e(TAG, "resizeStack failed", e);
@@ -502,7 +507,8 @@ public class PipManager implements BasePipManager {
private StackInfo getPinnedStackInfo() {
StackInfo stackInfo = null;
try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
} catch (RemoteException e) {
Log.e(TAG, "getStackInfo failed", e);
}
@@ -654,7 +660,7 @@ public class PipManager implements BasePipManager {
}
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
if (!checkCurrentUserId(mContext, DEBUG)) {
return;
@@ -665,6 +671,7 @@ public class PipManager implements BasePipManager {
return;
}
if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+ mPinnedStackId = stackInfo.stackId;
mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
mPipComponentName = ComponentName.unflattenFromString(
stackInfo.taskNames[stackInfo.taskNames.length - 1]);
diff --git a/com/android/systemui/qs/AlphaControlledSignalTileView.java b/com/android/systemui/qs/AlphaControlledSignalTileView.java
new file mode 100644
index 00000000..2c7ec70a
--- /dev/null
+++ b/com/android/systemui/qs/AlphaControlledSignalTileView.java
@@ -0,0 +1,86 @@
+/*
+ * 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.qs;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import com.android.systemui.qs.tileimpl.SlashImageView;
+
+
+/**
+ * Creates AlphaControlledSlashImageView instead of SlashImageView
+ */
+public class AlphaControlledSignalTileView extends SignalTileView {
+ public AlphaControlledSignalTileView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected SlashImageView createSlashImageView(Context context) {
+ return new AlphaControlledSlashImageView(context);
+ }
+
+ /**
+ * Creates AlphaControlledSlashDrawable instead of regular SlashDrawables
+ */
+ public static class AlphaControlledSlashImageView extends SlashImageView {
+ public AlphaControlledSlashImageView(Context context) {
+ super(context);
+ }
+
+ public void setFinalImageTintList(ColorStateList tint) {
+ super.setImageTintList(tint);
+ final SlashDrawable slash = getSlash();
+ if (slash != null) {
+ ((AlphaControlledSlashDrawable)slash).setFinalTintList(tint);
+ }
+ }
+
+ @Override
+ protected void ensureSlashDrawable() {
+ if (getSlash() == null) {
+ final SlashDrawable slash = new AlphaControlledSlashDrawable(getDrawable());
+ setSlash(slash);
+ slash.setAnimationEnabled(getAnimationEnabled());
+ setImageViewDrawable(slash);
+ }
+ }
+ }
+
+ /**
+ * SlashDrawable that disobeys orders to change its drawable's tint except when you tell
+ * it not to disobey. The slash still will animate its alpha.
+ */
+ public static class AlphaControlledSlashDrawable extends SlashDrawable {
+ AlphaControlledSlashDrawable(Drawable d) {
+ super(d);
+ }
+
+ @Override
+ protected void setDrawableTintList(ColorStateList tint) {
+ }
+
+ /**
+ * Set a target tint list instead of
+ */
+ public void setFinalTintList(ColorStateList tint) {
+ super.setDrawableTintList(tint);
+ }
+ }
+}
+
diff --git a/com/android/systemui/qs/SignalTileView.java b/com/android/systemui/qs/SignalTileView.java
index b300e4a3..9ee40ccf 100644
--- a/com/android/systemui/qs/SignalTileView.java
+++ b/com/android/systemui/qs/SignalTileView.java
@@ -63,13 +63,17 @@ public class SignalTileView extends QSIconViewImpl {
@Override
protected View createIcon() {
mIconFrame = new FrameLayout(mContext);
- mSignal = new SlashImageView(mContext);
+ mSignal = createSlashImageView(mContext);
mIconFrame.addView(mSignal);
mOverlay = new ImageView(mContext);
mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
return mIconFrame;
}
+ protected SlashImageView createSlashImageView(Context context) {
+ return new SlashImageView(context);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/com/android/systemui/qs/SlashDrawable.java b/com/android/systemui/qs/SlashDrawable.java
index c3561489..a9b2376e 100644
--- a/com/android/systemui/qs/SlashDrawable.java
+++ b/com/android/systemui/qs/SlashDrawable.java
@@ -197,11 +197,15 @@ public class SlashDrawable extends Drawable {
public void setTintList(@Nullable ColorStateList tint) {
mTintList = tint;
super.setTintList(tint);
- mDrawable.setTintList(tint);
+ setDrawableTintList(tint);
mPaint.setColor(tint.getDefaultColor());
invalidateSelf();
}
+ protected void setDrawableTintList(@Nullable ColorStateList tint) {
+ mDrawable.setTintList(tint);
+ }
+
@Override
public void setTintMode(@NonNull Mode tintMode) {
mTintMode = tintMode;
diff --git a/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 8074cb9b..e8c8b907 100644
--- a/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -33,6 +33,7 @@ import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
import java.util.Objects;
public class QSIconViewImpl extends QSIconView {
@@ -138,7 +139,12 @@ public class QSIconViewImpl extends QSIconView {
animateGrayScale(mTint, color, iv);
mTint = color;
} else {
- setTint(iv, color);
+ if (iv instanceof AlphaControlledSlashImageView) {
+ ((AlphaControlledSlashImageView)iv)
+ .setFinalImageTintList(ColorStateList.valueOf(color));
+ } else {
+ setTint(iv, color);
+ }
mTint = color;
}
}
@@ -149,6 +155,10 @@ public class QSIconViewImpl extends QSIconView {
}
public static void animateGrayScale(int fromColor, int toColor, ImageView iv) {
+ if (iv instanceof AlphaControlledSlashImageView) {
+ ((AlphaControlledSlashImageView)iv)
+ .setFinalImageTintList(ColorStateList.valueOf(toColor));
+ }
if (ValueAnimator.areAnimatorsEnabled()) {
final float fromAlpha = Color.alpha(fromColor);
final float toAlpha = Color.alpha(toColor);
diff --git a/com/android/systemui/qs/tileimpl/SlashImageView.java b/com/android/systemui/qs/tileimpl/SlashImageView.java
index 97e9c3df..63d6f82c 100644
--- a/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -34,7 +34,15 @@ public class SlashImageView extends ImageView {
super(context);
}
- private void ensureSlashDrawable() {
+ protected SlashDrawable getSlash() {
+ return mSlash;
+ }
+
+ protected void setSlash(SlashDrawable slash) {
+ mSlash = slash;
+ }
+
+ protected void ensureSlashDrawable() {
if (mSlash == null) {
mSlash = new SlashDrawable(getDrawable());
mSlash.setAnimationEnabled(mAnimationEnabled);
@@ -56,10 +64,18 @@ public class SlashImageView extends ImageView {
}
}
+ protected void setImageViewDrawable(SlashDrawable slash) {
+ super.setImageDrawable(slash);
+ }
+
public void setAnimationEnabled(boolean enabled) {
mAnimationEnabled = enabled;
}
+ public boolean getAnimationEnabled() {
+ return mAnimationEnabled;
+ }
+
private void setSlashState(@NonNull SlashState slashState) {
ensureSlashDrawable();
mSlash.setRotation(slashState.rotation);
diff --git a/com/android/systemui/qs/tiles/BluetoothTile.java b/com/android/systemui/qs/tiles/BluetoothTile.java
index 8d62f2aa..81b8622c 100644
--- a/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -34,10 +35,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.R.drawable;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -135,11 +134,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
- R.drawable.ic_qs_bluetooth_connected, batteryLevel,
- mContext.getResources().getFraction(
- R.fraction.bt_battery_scale_fraction, 1, 1));
- state.icon = new DrawableIcon(drawable);
+ state.icon = new BluetoothBatteryDrawable(batteryLevel);
}
}
state.contentDescription = mContext.getString(
@@ -215,6 +210,22 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return new BluetoothDetailAdapter();
}
+ private class BluetoothBatteryDrawable extends Icon {
+ private int mLevel;
+
+ BluetoothBatteryDrawable(int level) {
+ mLevel = level;
+ }
+
+ @Override
+ public Drawable getDrawable(Context context) {
+ return createLayerDrawable(context,
+ R.drawable.ic_qs_bluetooth_connected, mLevel,
+ context.getResources().getFraction(
+ R.fraction.bt_battery_scale_fraction, 1, 1));
+ }
+ }
+
protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
// We probably won't ever have space in the UI for more than 20 devices, so don't
// get info for them.
diff --git a/com/android/systemui/qs/tiles/CellularTile.java b/com/android/systemui/qs/tiles/CellularTile.java
index 2e389ba1..0ce3e6aa 100644
--- a/com/android/systemui/qs/tiles/CellularTile.java
+++ b/com/android/systemui/qs/tiles/CellularTile.java
@@ -266,7 +266,7 @@ public class CellularTile extends QSTileImpl<SignalState> {
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mInfo.noSim = show;
if (mInfo.noSim) {
// Make sure signal gets cleared out when no sims.
diff --git a/com/android/systemui/qs/tiles/WifiTile.java b/com/android/systemui/qs/tiles/WifiTile.java
index 33b15121..23702736 100644
--- a/com/android/systemui/qs/tiles/WifiTile.java
+++ b/com/android/systemui/qs/tiles/WifiTile.java
@@ -37,10 +37,10 @@ import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
@@ -104,7 +104,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
@Override
public QSIconView createTileView(Context context) {
- return new SignalTileView(context);
+ return new AlphaControlledSignalTileView(context);
}
@Override
diff --git a/com/android/systemui/recents/Recents.java b/com/android/systemui/recents/Recents.java
index 406bcac0..283ac0c4 100644
--- a/com/android/systemui/recents/Recents.java
+++ b/com/android/systemui/recents/Recents.java
@@ -16,6 +16,9 @@
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.ACTIVITY_TYPE_UNDEFINED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
import android.app.ActivityManager;
@@ -437,9 +440,12 @@ public class Recents extends SystemUI
int currentUser = sSystemServicesProxy.getCurrentUser();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
+ final int activityType = runningTask != null
+ ? runningTask.configuration.windowConfiguration.getActivityType()
+ : ACTIVITY_TYPE_UNDEFINED;
boolean screenPinningActive = ssp.isScreenPinningActive();
- boolean isRunningTaskInHomeOrRecentsStack = runningTask != null &&
- ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
+ boolean isRunningTaskInHomeOrRecentsStack =
+ activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
if (runningTask.supportsSplitScreenMultiWindow) {
diff --git a/com/android/systemui/recents/RecentsActivity.java b/com/android/systemui/recents/RecentsActivity.java
index f5455568..86b77900 100644
--- a/com/android/systemui/recents/RecentsActivity.java
+++ b/com/android/systemui/recents/RecentsActivity.java
@@ -358,6 +358,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+ if (Recents.getConfiguration().isLowRamDevice) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
diff --git a/com/android/systemui/recents/RecentsImpl.java b/com/android/systemui/recents/RecentsImpl.java
index aecf95fc..3e2a5f3f 100644
--- a/com/android/systemui/recents/RecentsImpl.java
+++ b/com/android/systemui/recents/RecentsImpl.java
@@ -16,9 +16,9 @@
package com.android.systemui.recents;
-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.isHomeOrRecentsStack;
+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;
@@ -173,7 +173,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
}
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
// Check this is for the right user
if (!checkCurrentUserId(mContext, false /* debug */)) {
return;
@@ -533,7 +533,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
if (runningTask == null) return;
// Find the task in the recents list
- boolean isRunningTaskInHomeStack = SystemServicesProxy.isHomeStack(runningTask.stackId);
+ boolean isRunningTaskInHomeStack =
+ runningTask.configuration.windowConfiguration.getActivityType()
+ == ACTIVITY_TYPE_HOME;
ArrayList<Task> tasks = focusedStack.getStackTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
@@ -565,8 +567,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Launch the task
ssp.startActivityFromRecents(
- mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
- null /* resultListener */);
+ mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */);
}
/**
@@ -584,9 +585,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Return early if there is no running task (can't determine affiliated tasks in this case)
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
+ final int activityType = runningTask.configuration.windowConfiguration.getActivityType();
if (runningTask == null) return;
// Return early if the running task is in the home/recents stack (optimization)
- if (isHomeOrRecentsStack(runningTask.stackId)) return;
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
// Find the task in the recents list
ArrayList<Task> tasks = focusedStack.getStackTasks();
@@ -639,8 +641,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
// Launch the task
ssp.startActivityFromRecents(
- mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID,
- null /* resultListener */);
+ mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */);
}
public void showNextAffiliatedTask() {
@@ -872,7 +873,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
Rect windowOverrideRect) {
final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
- if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ 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();
diff --git a/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
index e02fb147..e4a4f592 100644
--- a/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
+++ b/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,5 +22,15 @@ import com.android.systemui.recents.events.EventBus;
* This is sent when the stack action button should be hidden.
*/
public class HideStackActionButtonEvent extends EventBus.Event {
- // Simple event
+
+ // Whether or not to translate the stack action button when hiding it
+ public final boolean translate;
+
+ public HideStackActionButtonEvent() {
+ this(true);
+ }
+
+ public HideStackActionButtonEvent(boolean translate) {
+ this.translate = translate;
+ }
}
diff --git a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
index 3db106e7..862a1eee 100644
--- a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
+++ b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
@@ -16,6 +16,9 @@
package com.android.systemui.recents.events.activity;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
import android.graphics.Rect;
import com.android.systemui.recents.events.EventBus;
@@ -30,15 +33,23 @@ public class LaunchTaskEvent extends EventBus.Event {
public final TaskView taskView;
public final Task task;
public final Rect targetTaskBounds;
- public final int targetTaskStack;
+ public final int targetWindowingMode;
+ public final int targetActivityType;
public final boolean screenPinningRequested;
- public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, int targetTaskStack,
+ public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds,
boolean screenPinningRequested) {
+ this(taskView, task, targetTaskBounds, screenPinningRequested,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds,
+ boolean screenPinningRequested, int windowingMode, int activityType) {
this.taskView = taskView;
this.task = task;
this.targetTaskBounds = targetTaskBounds;
- this.targetTaskStack = targetTaskStack;
+ this.targetWindowingMode = windowingMode;
+ this.targetActivityType = activityType;
this.screenPinningRequested = screenPinningRequested;
}
diff --git a/com/android/systemui/recents/misc/SystemServicesProxy.java b/com/android/systemui/recents/misc/SystemServicesProxy.java
index 71777822..bddf9a59 100644
--- a/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,13 +16,15 @@
package com.android.systemui.recents.misc;
-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.HOME_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.RECENTS_STACK_ID;
+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_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.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import android.annotation.NonNull;
@@ -34,6 +36,7 @@ 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;
@@ -172,7 +175,7 @@ public class SystemServicesProxy {
public void onTaskStackChangedBackground() { }
public void onTaskStackChanged() { }
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
- public void onActivityPinned(String packageName, int taskId) { }
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
public void onActivityUnpinned() { }
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
public void onPinnedStackAnimationStarted() { }
@@ -227,9 +230,11 @@ public class SystemServicesProxy {
}
@Override
- public void onActivityPinned(String packageName, int taskId) throws RemoteException {
+ 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, taskId, 0, packageName).sendToTarget();
+ mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
+ new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
}
@Override
@@ -481,16 +486,22 @@ public class SystemServicesProxy {
public ActivityManager.RunningTaskInfo getRunningTask() {
// Note: The set of running tasks from the system is ordered by recency
List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(10);
- if (tasks != null && !tasks.isEmpty()) {
- // Find the first task in a valid stack, we ignore everything from the Recents and PiP
- // stacks
- for (int i = 0; i < tasks.size(); i++) {
- ActivityManager.RunningTaskInfo task = tasks.get(i);
- int stackId = task.stackId;
- if (stackId != RECENTS_STACK_ID && stackId != PINNED_STACK_ID) {
- return task;
- }
+ if (tasks == null || tasks.isEmpty()) {
+ return null;
+ }
+
+ // Find the first task in a valid stack, we ignore everything from the Recents and PiP
+ // stacks
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ final WindowConfiguration winConfig = task.configuration.windowConfiguration;
+ if (winConfig.getActivityType() == ACTIVITY_TYPE_RECENTS) {
+ continue;
+ }
+ if (winConfig.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ continue;
}
+ return task;
}
return null;
}
@@ -517,12 +528,17 @@ public class SystemServicesProxy {
ActivityManager.StackInfo fullscreenStackInfo = null;
ActivityManager.StackInfo recentsStackInfo = null;
for (int i = 0; i < stackInfos.size(); i++) {
- StackInfo stackInfo = stackInfos.get(i);
- if (stackInfo.stackId == HOME_STACK_ID) {
+ final StackInfo stackInfo = stackInfos.get(i);
+ final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
+ final int activityType = winConfig.getActivityType();
+ final int windowingMode = winConfig.getWindowingMode();
+ if (activityType == ACTIVITY_TYPE_HOME) {
homeStackInfo = stackInfo;
- } else if (stackInfo.stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (activityType == ACTIVITY_TYPE_STANDARD
+ && (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
fullscreenStackInfo = stackInfo;
- } else if (stackInfo.stackId == RECENTS_STACK_ID) {
+ } else if (activityType == ACTIVITY_TYPE_RECENTS) {
recentsStackInfo = stackInfo;
}
}
@@ -576,7 +592,7 @@ public class SystemServicesProxy {
try {
final ActivityOptions options = ActivityOptions.makeBasic();
options.setDockCreateMode(createMode);
- options.setLaunchStackId(DOCKED_STACK_ID);
+ options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
mIam.startActivityFromRecents(taskId, options.toBundle());
return true;
} catch (Exception e) {
@@ -601,34 +617,6 @@ public class SystemServicesProxy {
}
/**
- * Returns whether the given stack id is the home stack id.
- */
- public static boolean isHomeStack(int stackId) {
- return stackId == HOME_STACK_ID;
- }
-
- /**
- * Returns whether the given stack id is the pinned stack id.
- */
- public static boolean isPinnedStack(int stackId){
- return stackId == PINNED_STACK_ID;
- }
-
- /**
- * Returns whether the given stack id is the docked stack id.
- */
- public static boolean isDockedStack(int stackId) {
- return stackId == DOCKED_STACK_ID;
- }
-
- /**
- * Returns whether the given stack id is the freeform workspace stack id.
- */
- public static boolean isFreeformStack(int stackId) {
- return stackId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- /**
* @return whether there are any docked tasks for the current user.
*/
public boolean hasDockedTask() {
@@ -636,7 +624,8 @@ public class SystemServicesProxy {
ActivityManager.StackInfo stackInfo = null;
try {
- stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+ stackInfo =
+ mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -737,14 +726,12 @@ public class SystemServicesProxy {
}
}
- /**
- * Moves a task into another stack.
- */
- public void moveTaskToStack(int taskId, int stackId) {
+ /** Set the task's windowing mode. */
+ public void setTaskWindowingMode(int taskId, int windowingMode) {
if (mIam == null) return;
try {
- mIam.positionTaskInStack(taskId, stackId, 0);
+ mIam.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
} catch (RemoteException | IllegalArgumentException e) {
e.printStackTrace();
}
@@ -1101,9 +1088,10 @@ public class SystemServicesProxy {
try {
// Use the recents stack bounds, fallback to fullscreen stack if it is null
- ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID);
+ ActivityManager.StackInfo stackInfo =
+ mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (stackInfo == null) {
- stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
+ stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
}
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
@@ -1120,25 +1108,34 @@ public class SystemServicesProxy {
opts != null ? opts.toBundle() : null, UserHandle.CURRENT));
}
+ public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
+ ActivityOptions options,
+ @Nullable final StartActivityFromRecentsResultListener resultListener) {
+ startActivityFromRecents(context, taskKey, taskName, options,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED, resultListener);
+ }
+
/** Starts an activity from recents. */
public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
- ActivityOptions options, int stackId,
+ ActivityOptions options, int windowingMode, int activityType,
@Nullable final StartActivityFromRecentsResultListener resultListener) {
if (mIam == null) {
return;
}
- if (taskKey.stackId == DOCKED_STACK_ID) {
+ if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// We show non-visible docked tasks in Recents, but we always want to launch
// them in the fullscreen stack.
if (options == null) {
options = ActivityOptions.makeBasic();
}
- options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
- } else if (stackId != INVALID_STACK_ID) {
+ options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ } else if (windowingMode != WINDOWING_MODE_UNDEFINED
+ || activityType != ACTIVITY_TYPE_UNDEFINED) {
if (options == null) {
options = ActivityOptions.makeBasic();
}
- options.setLaunchStackId(stackId);
+ options.setLaunchWindowingMode(windowingMode);
+ options.setLaunchActivityType(activityType);
}
final ActivityOptions finalOptions = options;
@@ -1307,6 +1304,20 @@ public class SystemServicesProxy {
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;
@@ -1342,8 +1353,10 @@ public class SystemServicesProxy {
break;
}
case ON_ACTIVITY_PINNED: {
+ final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1);
+ mTaskStackListeners.get(i).onActivityPinned(
+ info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
}
break;
}
diff --git a/com/android/systemui/recents/model/HighResThumbnailLoader.java b/com/android/systemui/recents/model/HighResThumbnailLoader.java
index 48fa6c3c..6414ea1e 100644
--- a/com/android/systemui/recents/model/HighResThumbnailLoader.java
+++ b/com/android/systemui/recents/model/HighResThumbnailLoader.java
@@ -187,7 +187,7 @@ public class HighResThumbnailLoader implements TaskCallbacks {
}
@Override
- public void onTaskStackIdChanged() {
+ public void onTaskWindowingModeChanged() {
}
private final Runnable mLoader = new Runnable() {
diff --git a/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 8d31730d..d5e03135 100644
--- a/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -16,6 +16,8 @@
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;
@@ -155,12 +157,13 @@ public class RecentsTaskLoadPlan {
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
// Compose the task key
- Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+ 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 = SystemServicesProxy.isFreeformStack(t.stackId);
+ boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
boolean isStackTask;
if (Recents.getConfiguration().isGridEnabled) {
// When grid layout is enabled, we only show the first
diff --git a/com/android/systemui/recents/model/Task.java b/com/android/systemui/recents/model/Task.java
index 9e6bf854..abdb5cb8 100644
--- a/com/android/systemui/recents/model/Task.java
+++ b/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.model;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
@@ -46,8 +48,8 @@ public class Task {
public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
- /* Notifies when a task's stack id has changed. */
- public void onTaskStackIdChanged();
+ /* Notifies when a task's windowing mode has changed. */
+ public void onTaskWindowingModeChanged();
}
/* The Task Key represents the unique primary key for the task */
@@ -55,7 +57,7 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public final int id;
@ViewDebug.ExportedProperty(category="recents")
- public int stackId;
+ public int windowingMode;
@ViewDebug.ExportedProperty(category="recents")
public final Intent baseIntent;
@ViewDebug.ExportedProperty(category="recents")
@@ -67,10 +69,10 @@ public class Task {
private int mHashCode;
- public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+ public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime,
long lastActiveTime) {
this.id = id;
- this.stackId = stackId;
+ this.windowingMode = windowingMode;
this.baseIntent = intent;
this.userId = userId;
this.firstActiveTime = firstActiveTime;
@@ -78,8 +80,8 @@ public class Task {
updateHashCode();
}
- public void setStackId(int stackId) {
- this.stackId = stackId;
+ public void setWindowingMode(int windowingMode) {
+ this.windowingMode = windowingMode;
updateHashCode();
}
@@ -93,7 +95,9 @@ public class Task {
return false;
}
TaskKey otherKey = (TaskKey) o;
- return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
+ return id == otherKey.id
+ && windowingMode == otherKey.windowingMode
+ && userId == otherKey.userId;
}
@Override
@@ -103,12 +107,12 @@ public class Task {
@Override
public String toString() {
- return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" +
- lastActiveTime;
+ return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
+ + " lastActiveTime=" + lastActiveTime;
}
private void updateHashCode() {
- mHashCode = Objects.hash(id, stackId, userId);
+ mHashCode = Objects.hash(id, windowingMode, userId);
}
}
@@ -277,14 +281,12 @@ public class Task {
this.group = group;
}
- /**
- * Updates the stack id of this task.
- */
- public void setStackId(int stackId) {
- key.setStackId(stackId);
+ /** Updates the task's windowing mode. */
+ public void setWindowingMode(int windowingMode) {
+ key.setWindowingMode(windowingMode);
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskStackIdChanged();
+ mCallbacks.get(i).onTaskWindowingModeChanged();
}
}
@@ -293,7 +295,7 @@ public class Task {
*/
public boolean isFreeformTask() {
SystemServicesProxy ssp = Recents.getSystemServices();
- return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
+ return ssp.hasFreeformWorkspaceSupport() && key.windowingMode == WINDOWING_MODE_FREEFORM;
}
/** Notifies the callback listeners that this task has been loaded */
diff --git a/com/android/systemui/recents/model/TaskKeyCache.java b/com/android/systemui/recents/model/TaskKeyCache.java
index be99f930..247a6542 100644
--- a/com/android/systemui/recents/model/TaskKeyCache.java
+++ b/com/android/systemui/recents/model/TaskKeyCache.java
@@ -45,7 +45,7 @@ public abstract class TaskKeyCache<V> {
final V getAndInvalidateIfModified(Task.TaskKey key) {
Task.TaskKey lastKey = mKeys.get(key.id);
if (lastKey != null) {
- if ((lastKey.stackId != key.stackId) ||
+ if ((lastKey.windowingMode != key.windowingMode) ||
(lastKey.lastActiveTime != key.lastActiveTime)) {
// The task has updated (been made active since the last time it was put into the
// LRU cache) or the stack id for the task has changed, invalidate that cache item
diff --git a/com/android/systemui/recents/model/TaskStack.java b/com/android/systemui/recents/model/TaskStack.java
index 6e3be09b..fdae917c 100644
--- a/com/android/systemui/recents/model/TaskStack.java
+++ b/com/android/systemui/recents/model/TaskStack.java
@@ -18,8 +18,8 @@ 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.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+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;
@@ -115,7 +115,7 @@ class FilteredTaskList {
/**
* Moves the given task.
*/
- public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
+ public void setTaskWindowingMode(Task task, int insertIndex, int windowingMode) {
int taskIndex = indexOf(task);
if (taskIndex != insertIndex) {
mTasks.remove(taskIndex);
@@ -127,7 +127,7 @@ class FilteredTaskList {
// Update the stack id now, after we've moved the task, and before we update the
// filtered tasks
- task.setStackId(newStackId);
+ task.setWindowingMode(windowingMode);
updateFilteredTasks();
}
@@ -590,17 +590,15 @@ public class TaskStack {
mCb = cb;
}
- /**
- * Moves the given task to either the front of the freeform workspace or the stack.
- */
- public void moveTaskToStack(Task task, int newStackId) {
+ /** 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() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
+ if (!task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FREEFORM)) {
// Insert freeform tasks at the front
- mStackTaskList.moveTaskToStack(task, taskCount, newStackId);
- } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
+ 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--) {
@@ -609,7 +607,7 @@ public class TaskStack {
break;
}
}
- mStackTaskList.moveTaskToStack(task, insertIndex, newStackId);
+ mStackTaskList.setTaskWindowingMode(task, insertIndex, windowingMode);
}
}
diff --git a/com/android/systemui/recents/views/RecentsTransitionHelper.java b/com/android/systemui/recents/views/RecentsTransitionHelper.java
index b2675d7a..ee05d81c 100644
--- a/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,14 +16,17 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-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.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;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
@@ -107,7 +110,7 @@ public class RecentsTransitionHelper {
*/
public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
final TaskStackView stackView, final TaskView taskView,
- final boolean screenPinningRequested, final int destinationStack) {
+ final boolean screenPinningRequested, final int windowingMode, final int activityType) {
final ActivityOptions.OnAnimationStartedListener animStartedListener;
final AppTransitionAnimationSpecsFuture transitionFuture;
@@ -116,8 +119,8 @@ public class RecentsTransitionHelper {
// Fetch window rect here already in order not to be blocked on lock contention in WM
// when the future calls it.
final Rect windowRect = Recents.getSystemServices().getWindowRect();
- transitionFuture = getAppTransitionFuture(
- () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect));
+ transitionFuture = getAppTransitionFuture(() -> composeAnimationSpecs(
+ task, stackView, windowingMode, activityType, windowRect));
animStartedListener = new OnAnimationStartedListener() {
private boolean mHandled;
@@ -180,7 +183,8 @@ public class RecentsTransitionHelper {
if (taskView == null) {
// If there is no task view, then we do not need to worry about animating out occluding
// task views, and we can launch immediately
- startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ windowingMode, activityType);
} else {
LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
screenPinningRequested);
@@ -189,13 +193,14 @@ public class RecentsTransitionHelper {
@Override
public void run() {
startTaskActivity(stack, task, taskView, opts, transitionFuture,
- destinationStack);
+ windowingMode, activityType);
}
});
EventBus.getDefault().send(launchStartedEvent);
} else {
EventBus.getDefault().send(launchStartedEvent);
- startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ windowingMode, activityType);
}
}
Recents.getSystemServices().sendCloseSystemWindows(
@@ -224,13 +229,13 @@ public class RecentsTransitionHelper {
*
* @param taskView this is the {@link TaskView} that we are launching from. This can be null if
* we are toggling recents and the launch-to task is now offscreen.
- * @param destinationStack id of the stack to put the task into.
*/
private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
- int destinationStack) {
+ int windowingMode, int activityType) {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(mContext, task.key, task.title, opts, destinationStack,
+ ssp.startActivityFromRecents(mContext, task.key, task.title, opts, windowingMode,
+ activityType,
succeeded -> {
if (succeeded) {
// Keep track of the index of the task launch
@@ -310,11 +315,9 @@ public class RecentsTransitionHelper {
* Composes the animation specs for all the tasks in the target stack.
*/
private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
- final TaskStackView stackView, final int destinationStack, Rect windowRect) {
- // Ensure we have a valid target stack id
- final int targetStackId = destinationStack != INVALID_STACK_ID ?
- destinationStack : task.key.stackId;
- if (!StackId.useAnimationSpecForAppTransition(targetStackId)) {
+ final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
+ if (activityType == ACTIVITY_TYPE_RECENTS || activityType == ACTIVITY_TYPE_HOME
+ || windowingMode == WINDOWING_MODE_PINNED) {
return null;
}
@@ -329,9 +332,12 @@ public class RecentsTransitionHelper {
List<AppTransitionAnimationSpec> specs = new ArrayList<>();
// TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
- // check for INVALID_STACK_ID
- if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID || targetStackId == DOCKED_STACK_ID
- || targetStackId == ASSISTANT_STACK_ID || targetStackId == INVALID_STACK_ID) {
+ // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ || activityType == ACTIVITY_TYPE_ASSISTANT
+ || windowingMode == WINDOWING_MODE_UNDEFINED) {
if (taskView == null) {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
@@ -353,7 +359,7 @@ public class RecentsTransitionHelper {
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
Task t = tasks.get(i);
- if (t.isFreeformTask() || targetStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ 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
diff --git a/com/android/systemui/recents/views/RecentsView.java b/com/android/systemui/recents/views/RecentsView.java
index c44cd728..c7edb9ae 100644
--- a/com/android/systemui/recents/views/RecentsView.java
+++ b/com/android/systemui/recents/views/RecentsView.java
@@ -174,8 +174,6 @@ public class RecentsView extends FrameLayout {
? R.layout.recents_low_ram_stack_action_button
: R.layout.recents_stack_action_button,
this, false);
- mStackActionButton.setOnClickListener(
- v -> EventBus.getDefault().send(new DismissAllTaskViewsEvent()));
mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
@@ -205,10 +203,6 @@ public class RecentsView extends FrameLayout {
mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
mStackButtonShadowColor);
}
- if (Recents.getConfiguration().isLowRamDevice) {
- int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor);
- mStackActionButton.setBackgroundColor(bgColor);
- }
}
// Let's also require dark status and nav bars if the text is dark
@@ -338,8 +332,7 @@ public class RecentsView extends FrameLayout {
Task task = mTaskStackView.getFocusedTask();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
- EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
- INVALID_STACK_ID, false));
+ EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
if (logEvent != 0) {
MetricsLogger.action(getContext(), logEvent,
@@ -363,32 +356,13 @@ public class RecentsView extends FrameLayout {
Task task = getStack().getLaunchTarget();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
- EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
- INVALID_STACK_ID, false));
+ EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
return true;
}
}
return false;
}
- /** Launches a given task. */
- public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
- if (mTaskStackView != null) {
- // Iterate the stack views and try and find the given task.
- List<TaskView> taskViews = mTaskStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskView tv = taskViews.get(j);
- if (tv.getTask() == task) {
- EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds,
- destinationStack, false));
- return true;
- }
- }
- }
- return false;
- }
-
/**
* Hides the task stack and shows the empty view.
*/
@@ -570,9 +544,10 @@ public class RecentsView extends FrameLayout {
public final void onBusEvent(LaunchTaskEvent event) {
mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
- event.taskView, event.screenPinningRequested, event.targetTaskStack);
+ event.taskView, event.screenPinningRequested, event.targetWindowingMode,
+ event.targetActivityType);
if (Recents.getConfiguration().isLowRamDevice) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
}
}
@@ -580,7 +555,7 @@ public class RecentsView extends FrameLayout {
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Hide the stack action button
- hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
@@ -741,13 +716,10 @@ public class RecentsView extends FrameLayout {
animateBackgroundScrim(getOpaqueScrimAlpha(),
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
- if (Recents.getConfiguration().isLowRamDevice && mEmptyView.getVisibility() != View.VISIBLE) {
- showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, false /* translate */);
- }
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
public final void onBusEvent(DismissAllTaskViewsEvent event) {
@@ -795,7 +767,8 @@ public class RecentsView extends FrameLayout {
mStackActionButton.setVisibility(View.VISIBLE);
mStackActionButton.setAlpha(0f);
if (translate) {
- mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
+ (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
} else {
mStackActionButton.setTranslationY(0f);
}
@@ -841,8 +814,8 @@ public class RecentsView extends FrameLayout {
if (mStackActionButton.getVisibility() == View.VISIBLE) {
if (translate) {
- mStackActionButton.animate()
- .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
+ * (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
}
mStackActionButton.animate()
.alpha(0f)
@@ -954,7 +927,7 @@ public class RecentsView extends FrameLayout {
/**
* @return the bounds of the stack action button.
*/
- private Rect getStackActionButtonBoundsFromStackLayout() {
+ Rect getStackActionButtonBoundsFromStackLayout() {
Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
int left, top;
if (Recents.getConfiguration().isLowRamDevice) {
@@ -976,6 +949,16 @@ public class RecentsView extends FrameLayout {
return actionButtonRect;
}
+ View getStackActionButton() {
+ return mStackActionButton;
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ mTouchHandler.cancelStackActionButtonClick();
+ }
+
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/RecentsViewTouchHandler.java b/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 46619c21..b6b24bcd 100644
--- a/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -23,6 +23,7 @@ import android.graphics.Rect;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
@@ -31,6 +32,8 @@ 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;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -99,8 +102,7 @@ public class RecentsViewTouchHandler {
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- handleTouchEvent(ev);
- return mDragRequested;
+ return handleTouchEvent(ev) || mDragRequested;
}
/** Handles touch events once we have intercepted them */
@@ -183,22 +185,47 @@ public class RecentsViewTouchHandler {
}
}
+ void cancelStackActionButtonClick() {
+ mRv.getStackActionButton().setPressed(false);
+ }
+
+ private boolean isWithinStackActionButton(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
+ mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
+ }
+
+ private void changeStackActionButtonDrawableHotspot(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
+ }
+
/**
* Handles dragging touch events
*/
- private void handleTouchEvent(MotionEvent ev) {
+ private boolean handleTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ boolean consumed = false;
+ float evX = ev.getX();
+ float evY = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
- mDownPos.set((int) ev.getX(), (int) ev.getY());
+ mDownPos.set((int) evX, (int) evY);
mDeviceId = ev.getDeviceId();
+
+ if (isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ mRv.getStackActionButton().setPressed(true);
+ }
break;
case MotionEvent.ACTION_MOVE: {
- float evX = ev.getX();
- float evY = ev.getY();
float x = evX - mTaskViewOffset.x;
float y = evY - mTaskViewOffset.y;
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ }
+
if (mDragRequested) {
if (!mIsDragging) {
mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
@@ -232,9 +259,7 @@ public class RecentsViewTouchHandler {
EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
currentDropTarget));
}
-
}
-
mTaskView.setTranslationX(x);
mTaskView.setTranslationY(y);
}
@@ -242,6 +267,11 @@ public class RecentsViewTouchHandler {
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ EventBus.getDefault().send(new DismissAllTaskViewsEvent());
+ consumed = true;
+ }
+ cancelStackActionButtonClick();
if (mDragRequested) {
boolean cancelled = action == MotionEvent.ACTION_CANCEL;
if (cancelled) {
@@ -254,5 +284,6 @@ public class RecentsViewTouchHandler {
mDeviceId = -1;
}
}
+ return consumed;
}
}
diff --git a/com/android/systemui/recents/views/TaskStackView.java b/com/android/systemui/recents/views/TaskStackView.java
index 8899e307..3160ee0e 100644
--- a/com/android/systemui/recents/views/TaskStackView.java
+++ b/com/android/systemui/recents/views/TaskStackView.java
@@ -16,9 +16,8 @@
package com.android.systemui.recents.views;
-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.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -1487,7 +1486,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Task frontTask = tasks.get(tasks.size() - 1);
if (frontTask != null && frontTask.isFreeformTask()) {
EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
- frontTask, null, INVALID_STACK_ID, false));
+ frontTask, null, false));
return true;
}
}
@@ -1768,8 +1767,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// In grid layout, the stack action button always remains visible.
- if (mEnterAnimationComplete && !useGridLayout() &&
- !Recents.getConfiguration().isLowRamDevice) {
+ if (mEnterAnimationComplete && !useGridLayout()) {
+ if (Recents.getConfiguration().isLowRamDevice) {
+ // Show stack button when user drags down to show older tasks on low ram devices
+ if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
+ && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
+ // Going up
+ EventBus.getDefault().send(
+ new ShowStackActionButtonEvent(true /* translate */));
+ }
+ return;
+ }
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) {
@@ -1956,6 +1964,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Remove the task from the stack
mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+ if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) {
+ EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
+ }
MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
event.task.key.getComponent().toString());
@@ -2082,18 +2093,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
boolean isFreeformTask = event.task.isFreeformTask();
- boolean hasChangedStacks =
+ boolean hasChangedWindowingMode =
(!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
(isFreeformTask && event.dropTarget == mStackDropTarget);
- if (hasChangedStacks) {
+ 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.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FREEFORM);
} else if (event.dropTarget == mStackDropTarget) {
- mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FULLSCREEN);
}
updateLayoutAlgorithm(true /* boundScroll */);
@@ -2102,7 +2113,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void run() {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
+ ssp.setTaskWindowingMode(event.task.key.id, event.task.key.windowingMode);
}
});
}
@@ -2369,12 +2380,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void run() {
EventBus.getDefault().send(new LaunchTaskEvent(
getChildViewForTask(task), task, null,
- INVALID_STACK_ID, false /* screenPinningRequested */));
+ false /* screenPinningRequested */));
}
});
} else {
- EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task),
- task, null, INVALID_STACK_ID, false /* screenPinningRequested */));
+ EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), task, null,
+ false /* screenPinningRequested */));
}
}
diff --git a/com/android/systemui/recents/views/TaskView.java b/com/android/systemui/recents/views/TaskView.java
index ceeebd96..9d639647 100644
--- a/com/android/systemui/recents/views/TaskView.java
+++ b/com/android/systemui/recents/views/TaskView.java
@@ -647,7 +647,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
}
@Override
- public void onTaskStackIdChanged() {
+ public void onTaskWindowingModeChanged() {
// Force rebind the header, the thumbnail does not change due to stack changes
mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mHeaderView.onTaskDataLoaded();
@@ -674,8 +674,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
mActionButtonView.setTranslationZ(0f);
screenPinningRequested = true;
}
- EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID,
- screenPinningRequested));
+ EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested));
MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
mTask.key.getComponent().toString());
diff --git a/com/android/systemui/recents/views/TaskViewHeader.java b/com/android/systemui/recents/views/TaskViewHeader.java
index ae922fcc..198ecae2 100644
--- a/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/com/android/systemui/recents/views/TaskViewHeader.java
@@ -16,6 +16,11 @@
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;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
@@ -57,10 +62,6 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
-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;
-
/* The task bar view */
public class TaskViewHeader extends FrameLayout
implements View.OnClickListener, View.OnLongClickListener {
@@ -172,7 +173,7 @@ public class TaskViewHeader extends FrameLayout
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
int mDisabledTaskBarBackgroundColor;
- int mMoveTaskTargetStackId = INVALID_STACK_ID;
+ int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
// Header background
private HighlightColorDrawable mBackground;
@@ -485,12 +486,12 @@ public class TaskViewHeader extends FrameLayout
// current task
if (mMoveTaskButton != null) {
if (t.isFreeformTask()) {
- mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ mTaskWindowingMode = WINDOWING_MODE_FULLSCREEN;
mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
? mLightFullscreenIcon
: mDarkFullscreenIcon);
} else {
- mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
+ mTaskWindowingMode = WINDOWING_MODE_FREEFORM;
mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
? mLightFreeformIcon
: mDarkFreeformIcon);
@@ -621,8 +622,8 @@ public class TaskViewHeader extends FrameLayout
Constants.Metrics.DismissSourceHeaderButton);
} else if (v == mMoveTaskButton) {
TaskView tv = Utilities.findParent(this, TaskView.class);
- EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null,
- mMoveTaskTargetStackId, false));
+ EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false,
+ mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED));
} else if (v == mAppInfoView) {
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
} else if (v == mAppIconView) {
diff --git a/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
index bcf4f17d..2d7cfb1a 100644
--- a/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
+++ b/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
@@ -26,8 +26,8 @@ import com.android.systemui.recents.views.TaskViewThumbnail;
public class GridTaskViewThumbnail extends TaskViewThumbnail {
- private Path mThumbnailOutline;
- private Path mRestBackgroundOutline;
+ private final Path mThumbnailOutline = new Path();
+ private final Path mRestBackgroundOutline = new Path();
// True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
// be updated.
private boolean mUpdateThumbnailOutline = true;
@@ -77,47 +77,38 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail {
(int) (mThumbnailRect.width() * mThumbnailScale));
final int thumbnailHeight = Math.min(viewHeight,
(int) (mThumbnailRect.height() * mThumbnailScale));
- // Draw the thumbnail, we only round the bottom corners:
- //
- // outerLeft outerRight
- // <-----------------------> mRestBackgroundOutline
- // _________________________ (thumbnailWidth < viewWidth)
- // |_______________________| outerTop A ____ B
- // | | ↑ | |
- // | | | | |
- // | | | | |
- // | | | | | C
- // \_______________________/ ↓ |__/
- // mCornerRadius outerBottom E D
- //
- // mRestBackgroundOutline (thumbnailHeight < viewHeight)
- // A _________________________ B
- // | | C
- // F \_______________________/
- // E D
- final int outerLeft = 0;
- final int outerTop = 0;
- final int outerRight = outerLeft + thumbnailWidth;
- final int outerBottom = outerTop + thumbnailHeight;
- mThumbnailOutline = new Path();
- mThumbnailOutline.moveTo(outerLeft, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerBottom - mCornerRadius);
- mThumbnailOutline.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius,
- outerRight, outerBottom, 0, 90, false);
- mThumbnailOutline.lineTo(outerLeft + mCornerRadius, outerBottom);
- mThumbnailOutline.arcTo(outerLeft, outerBottom - 2 * mCornerRadius,
- outerLeft + 2 * mCornerRadius, outerBottom, 90, 90, false);
- mThumbnailOutline.lineTo(outerLeft, outerTop);
- mThumbnailOutline.close();
if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+ // Draw the thumbnail, we only round the bottom corners:
+ //
+ // outerLeft outerRight
+ // <-----------------------> mRestBackgroundOutline
+ // _________________________ (thumbnailWidth < viewWidth)
+ // |_______________________| outerTop A ____ B
+ // | | ↑ | |
+ // | | | | |
+ // | | | | |
+ // | | | | | C
+ // \_______________________/ ↓ |__/
+ // mCornerRadius outerBottom E D
+ //
+ // mRestBackgroundOutline (thumbnailHeight < viewHeight)
+ // A _________________________ B
+ // | | C
+ // F \_______________________/
+ // E D
+ final int outerLeft = 0;
+ final int outerTop = 0;
+ final int outerRight = outerLeft + thumbnailWidth;
+ final int outerBottom = outerTop + thumbnailHeight;
+ createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline);
+
if (thumbnailWidth < viewWidth) {
final int l = Math.max(0, outerRight - mCornerRadius);
final int r = outerRight;
final int t = outerTop;
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -133,7 +124,7 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail {
final int r = outerRight;
final int t = Math.max(0, thumbnailHeight - mCornerRadius);
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -145,9 +136,26 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail {
mRestBackgroundOutline.lineTo(l, t); // A
mRestBackgroundOutline.close();
}
+ } else {
+ createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline);
}
}
+ private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom,
+ Path outPath) {
+ outPath.reset();
+ outPath.moveTo(outerLeft, outerTop);
+ outPath.lineTo(outerRight, outerTop);
+ outPath.lineTo(outerRight, outerBottom - mCornerRadius);
+ outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight,
+ outerBottom, 0, 90, false);
+ outPath.lineTo(outerLeft + mCornerRadius, outerBottom);
+ outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius,
+ outerBottom, 90, 90, false);
+ outPath.lineTo(outerLeft, outerTop);
+ outPath.close();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
final int titleHeight = getResources().getDimensionPixelSize(
diff --git a/com/android/systemui/stackdivider/DividerView.java b/com/android/systemui/stackdivider/DividerView.java
index 6bfef20c..7bcef574 100644
--- a/com/android/systemui/stackdivider/DividerView.java
+++ b/com/android/systemui/stackdivider/DividerView.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+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.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -23,7 +26,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -36,8 +38,6 @@ import android.util.AttributeSet;
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -62,8 +62,8 @@ import com.android.internal.policy.DockedDividerUtils;
import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
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.activity.UndockingTaskEvent;
@@ -93,7 +93,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1;
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
- private static final boolean SWAPPING_ENABLED = false;
/**
* How much the background gets scaled when we are in the minimized dock state.
@@ -153,7 +152,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private boolean mEntranceAnimationRunning;
private boolean mExitAnimationRunning;
private int mExitStartPosition;
- private GestureDetector mGestureDetector;
private boolean mDockedStackMinimized;
private boolean mHomeStackResizable;
private boolean mAdjustedForIme;
@@ -295,21 +293,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
landscape ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
mHandle.setAccessibilityDelegate(mHandleDelegate);
- mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- if (SWAPPING_ENABLED) {
- updateDockSide();
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (mDockSide != WindowManager.DOCKED_INVALID
- && !ssp.isRecentsActivityVisible()) {
- mWindowManagerProxy.swapTasks();
- return true;
- }
- }
- return false;
- }
- });
}
@Override
@@ -478,7 +461,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
@Override
public boolean onTouch(View v, MotionEvent event) {
convertToScreenCoordinates(event);
- mGestureDetector.onTouchEvent(event);
final int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
@@ -679,7 +661,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} else {
mWindowManagerProxy.maximizeDockedStack();
}
- mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
+ mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
}
private void liftBackground() {
@@ -1015,8 +997,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
- getStackIdForDismissTarget(closestDismissTarget),
- dimFraction);
+ getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
}
private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1150,13 +1131,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
- private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
+ private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
|| (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
&& dockSideBottomRight(mDockSide))) {
- return StackId.DOCKED_STACK_ID;
+ return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
} else {
- return StackId.RECENTS_STACK_ID;
+ return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
}
@@ -1210,6 +1191,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
+ public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ }
+
public final void onBusEvent(DockedTopTaskEvent event) {
if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
mState.growAfterRecentsDrawn = false;
diff --git a/com/android/systemui/stackdivider/WindowManagerProxy.java b/com/android/systemui/stackdivider/WindowManagerProxy.java
index c2451264..85a60624 100644
--- a/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,7 +16,6 @@
package com.android.systemui.stackdivider;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.view.WindowManager.DOCKED_INVALID;
import android.app.ActivityManager;
@@ -56,7 +55,7 @@ public class WindowManagerProxy {
private final Rect mTouchableRegion = new Rect();
private boolean mDimLayerVisible;
- private int mDimLayerTargetStack;
+ private int mDimLayerTargetWindowingMode;
private float mDimLayerAlpha;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@@ -88,8 +87,7 @@ public class WindowManagerProxy {
@Override
public void run() {
try {
- ActivityManager.getService().moveTasksToFullscreenStack(
- DOCKED_STACK_ID, false /* onTop */);
+ ActivityManager.getService().dismissSplitScreenMode(false /* onTop */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to remove stack: " + e);
}
@@ -100,8 +98,7 @@ public class WindowManagerProxy {
@Override
public void run() {
try {
- ActivityManager.getService().resizeStack(
- DOCKED_STACK_ID, null, true, true, false, -1);
+ ActivityManager.getService().dismissSplitScreenMode(true /* onTop */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
@@ -113,18 +110,7 @@ public class WindowManagerProxy {
public void run() {
try {
WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
- mDimLayerTargetStack, mDimLayerAlpha);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mSwapRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityManager.getService().swapDockedAndFullscreenStack();
+ mDimLayerTargetWindowingMode, mDimLayerAlpha);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
@@ -211,17 +197,13 @@ public class WindowManagerProxy {
return DOCKED_INVALID;
}
- public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
mDimLayerVisible = visible;
- mDimLayerTargetStack = targetStackId;
+ mDimLayerTargetWindowingMode = targetWindowingMode;
mDimLayerAlpha = alpha;
mExecutor.execute(mDimLayerRunnable);
}
- public void swapTasks() {
- mExecutor.execute(mSwapRunnable);
- }
-
public void setTouchRegion(Rect region) {
synchronized (mDockedRect) {
mTouchableRegion.set(region);
diff --git a/com/android/systemui/statusbar/ExpandableNotificationRow.java b/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7fe7f399..966e7899 100644
--- a/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -64,10 +64,10 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.NotificationInflater;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -436,9 +436,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else {
minHeight = mNotificationMinHeight;
}
- NotificationViewWrapper collapsedWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED);
- minHeight += collapsedWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
boolean headsUpCustom = layout.getHeadsUpChild() != null &&
layout.getHeadsUpChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
@@ -450,11 +447,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else {
headsUpheight = mMaxHeadsUpHeight;
}
- NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP);
- if (headsUpWrapper != null) {
- headsUpheight += headsUpWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
- }
layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
mNotificationAmbientHeight);
}
@@ -2024,14 +2016,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public int getMinHeight() {
- if (mGuts != null && mGuts.isExposed()) {
+ public int getMinHeight(boolean ignoreTemporaryStates) {
+ if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
return mGuts.getIntrinsicHeight();
- } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
+ } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
+ && mHeadsUpManager.isTrackingHeadsUp()) {
return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
return mChildrenContainer.getMinHeight();
- } else if (isHeadsUpAllowed() && mIsHeadsUp) {
+ } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
return mHeadsUpHeight;
}
NotificationContentView showingLayout = getShowingLayout();
diff --git a/com/android/systemui/statusbar/ExpandableView.java b/com/android/systemui/statusbar/ExpandableView.java
index efe5e0c2..aac9af8a 100644
--- a/com/android/systemui/statusbar/ExpandableView.java
+++ b/com/android/systemui/statusbar/ExpandableView.java
@@ -151,9 +151,21 @@ public abstract class ExpandableView extends FrameLayout {
}
/**
- * @return The minimum content height of this notification.
+ * @return The minimum content height of this notification. This also respects the temporary
+ * states of the view.
*/
public int getMinHeight() {
+ return getMinHeight(false /* ignoreTemporaryStates */);
+ }
+
+ /**
+ * Get the minimum height of this view.
+ *
+ * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up.
+ *
+ * @return The minimum height that this view needs.
+ */
+ public int getMinHeight(boolean ignoreTemporaryStates) {
return getHeight();
}
diff --git a/com/android/systemui/statusbar/KeyboardShortcuts.java b/com/android/systemui/statusbar/KeyboardShortcuts.java
index d370a633..2d16d220 100644
--- a/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -433,24 +433,27 @@ public final class KeyboardShortcuts {
// Assist.
final AssistUtils assistUtils = new AssistUtils(mContext);
final ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId);
- PackageInfo assistPackageInfo = null;
- try {
- assistPackageInfo = mPackageManager.getPackageInfo(
- assistComponent.getPackageName(), 0, userId);
- } catch (RemoteException e) {
- Log.e(TAG, "PackageManagerService is dead");
- }
+ // Not all devices have an assist component.
+ if (assistComponent != null) {
+ PackageInfo assistPackageInfo = null;
+ try {
+ assistPackageInfo = mPackageManager.getPackageInfo(
+ assistComponent.getPackageName(), 0, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManagerService is dead");
+ }
- if (assistPackageInfo != null) {
- final Icon assistIcon = Icon.createWithResource(
- assistPackageInfo.applicationInfo.packageName,
- assistPackageInfo.applicationInfo.icon);
+ if (assistPackageInfo != null) {
+ final Icon assistIcon = Icon.createWithResource(
+ assistPackageInfo.applicationInfo.packageName,
+ assistPackageInfo.applicationInfo.icon);
- keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
- assistIcon,
- KeyEvent.KEYCODE_UNKNOWN,
- KeyEvent.META_META_ON));
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
+ mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
+ assistIcon,
+ KeyEvent.KEYCODE_UNKNOWN,
+ KeyEvent.META_META_ON));
+ }
}
// Browser.
diff --git a/com/android/systemui/statusbar/NotificationData.java b/com/android/systemui/statusbar/NotificationData.java
index ddc7dd06..d0417b59 100644
--- a/com/android/systemui/statusbar/NotificationData.java
+++ b/com/android/systemui/statusbar/NotificationData.java
@@ -292,8 +292,8 @@ public class NotificationData {
if (mRankingMap != null) {
// RankingMap as received from NoMan
- mRankingMap.getRanking(a.key, mRankingA);
- mRankingMap.getRanking(b.key, mRankingB);
+ getRanking(a.key, mRankingA);
+ getRanking(b.key, mRankingB);
aImportance = mRankingA.getImportance();
bImportance = mRankingB.getImportance();
aRank = mRankingA.getRank();
@@ -381,7 +381,7 @@ public class NotificationData {
public boolean isAmbient(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.isAmbient();
}
return false;
@@ -389,7 +389,7 @@ public class NotificationData {
public int getVisibilityOverride(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getVisibilityOverride();
}
return Ranking.VISIBILITY_NO_OVERRIDE;
@@ -397,7 +397,7 @@ public class NotificationData {
public boolean shouldSuppressScreenOff(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return (mTmpRanking.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0;
}
@@ -406,7 +406,7 @@ public class NotificationData {
public boolean shouldSuppressScreenOn(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return (mTmpRanking.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0;
}
@@ -415,7 +415,7 @@ public class NotificationData {
public int getImportance(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getImportance();
}
return NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -423,7 +423,7 @@ public class NotificationData {
public String getOverrideGroupKey(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getOverrideGroupKey();
}
return null;
@@ -431,7 +431,7 @@ public class NotificationData {
public List<SnoozeCriterion> getSnoozeCriteria(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getSnoozeCriteria();
}
return null;
@@ -439,7 +439,7 @@ public class NotificationData {
public NotificationChannel getChannel(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getChannel();
}
return null;
@@ -452,6 +452,9 @@ public class NotificationData {
final int N = mEntries.size();
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
+ if (!getRanking(entry.key, mTmpRanking)) {
+ continue;
+ }
final StatusBarNotification oldSbn = entry.notification.cloneLight();
final String overrideGroupKey = getOverrideGroupKey(entry.key);
if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
@@ -466,6 +469,19 @@ public class NotificationData {
filterAndSort();
}
+ /**
+ * Get the ranking from the current ranking map.
+ *
+ * @param key the key to look up
+ * @param outRanking the ranking to populate
+ *
+ * @return {@code true} if the ranking was properly obtained.
+ */
+ @VisibleForTesting
+ protected boolean getRanking(String key, Ranking outRanking) {
+ return mRankingMap.getRanking(key, outRanking);
+ }
+
// TODO: This should not be public. Instead the Environment should notify this class when
// anything changed, and this class should call back the UI so it updates itself.
public void filterAndSort() {
@@ -573,7 +589,7 @@ public class NotificationData {
}
private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
- mRankingMap.getRanking(e.key, mTmpRanking);
+ getRanking(e.key, mTmpRanking);
pw.print(indent);
pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
StatusBarNotification n = e.notification;
diff --git a/com/android/systemui/statusbar/NotificationSnooze.java b/com/android/systemui/statusbar/NotificationSnooze.java
index c45ca540..492ab44d 100644
--- a/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/com/android/systemui/statusbar/NotificationSnooze.java
@@ -16,8 +16,13 @@ package com.android.systemui.statusbar;
*/
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -28,12 +33,15 @@ import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
+import android.metrics.LogMaker;
import android.os.Bundle;
+import android.provider.Settings;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
+import android.util.KeyValueListParser;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,11 +59,23 @@ import com.android.systemui.R;
public class NotificationSnooze extends LinearLayout
implements NotificationGuts.GutsContent, View.OnClickListener {
+ private static final String TAG = "NotificationSnooze";
/**
* If this changes more number increases, more assistant action resId's should be defined for
* accessibility purposes, see {@link #setSnoozeOptions(List)}
*/
private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
+ private static final String KEY_DEFAULT_SNOOZE = "default";
+ private static final String KEY_OPTIONS = "options_array";
+ private static final LogMaker OPTIONS_OPEN_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_OPEN);
+ private static final LogMaker OPTIONS_CLOSE_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_CLOSE);
+ private static final LogMaker UNDO_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE)
+ .setType(MetricsEvent.TYPE_ACTION);
private NotificationGuts mGutsContainer;
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
@@ -72,9 +92,31 @@ public class NotificationSnooze extends LinearLayout
private boolean mSnoozing;
private boolean mExpanded;
private AnimatorSet mExpandAnimation;
+ private KeyValueListParser mParser;
+
+ private final static int[] sAccessibilityActions = {
+ R.id.action_snooze_shorter,
+ R.id.action_snooze_short,
+ R.id.action_snooze_long,
+ R.id.action_snooze_longer,
+ };
+
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
public NotificationSnooze(Context context, AttributeSet attrs) {
super(context, attrs);
+ mParser = new KeyValueListParser(',');
+ }
+
+ @VisibleForTesting
+ SnoozeOption getDefaultOption()
+ {
+ return mDefaultOption;
+ }
+
+ @VisibleForTesting
+ void setKeyValueListParser(KeyValueListParser parser) {
+ mParser = parser;
}
@Override
@@ -96,7 +138,13 @@ public class NotificationSnooze extends LinearLayout
mSnoozeOptions = getDefaultSnoozeOptions();
createOptionViews();
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
}
@Override
@@ -136,7 +184,7 @@ public class NotificationSnooze extends LinearLayout
SnoozeOption so = mSnoozeOptions.get(i);
if (so.getAccessibilityAction() != null
&& so.getAccessibilityAction().getId() == action) {
- setSelected(so);
+ setSelected(so, true);
return true;
}
}
@@ -172,17 +220,49 @@ public class NotificationSnooze extends LinearLayout
mSbn = sbn;
}
- private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+ @VisibleForTesting
+ ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+ final Resources resources = getContext().getResources();
ArrayList<SnoozeOption> options = new ArrayList<>();
+ try {
+ final String config = Settings.Global.getString(getContext().getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS);
+ mParser.setString(config);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad snooze constants");
+ }
+
+ final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
+ resources.getInteger(R.integer.config_notification_snooze_time_default));
+ final int[] snoozeTimes = parseIntArray(KEY_OPTIONS,
+ resources.getIntArray(R.array.config_notification_snooze_times));
- options.add(createOption(15 /* minutes */, R.id.action_snooze_15_min));
- options.add(createOption(30 /* minutes */, R.id.action_snooze_30_min));
- mDefaultOption = createOption(60 /* minutes */, R.id.action_snooze_1_hour);
- options.add(mDefaultOption);
- options.add(createOption(60 * 2 /* minutes */, R.id.action_snooze_2_hours));
+ for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
+ int snoozeTime = snoozeTimes[i];
+ SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]);
+ if (i == 0 || snoozeTime == defaultSnooze) {
+ mDefaultOption = option;
+ }
+ options.add(option);
+ }
return options;
}
+ @VisibleForTesting
+ int[] parseIntArray(final String key, final int[] defaultArray) {
+ final String value = mParser.getString(key, null);
+ if (value != null) {
+ try {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } catch (NumberFormatException e) {
+ return defaultArray;
+ }
+ } else {
+ return defaultArray;
+ }
+ }
+
private SnoozeOption createOption(int minutes, int accessibilityActionId) {
Resources res = getResources();
boolean showInHours = minutes >= 60;
@@ -268,12 +348,24 @@ public class NotificationSnooze extends LinearLayout
mExpandAnimation.start();
}
- private void setSelected(SnoozeOption option) {
+ private void setSelected(SnoozeOption option, boolean userAction) {
mSelectedOption = option;
mSelectedOptionText.setText(option.getConfirmation());
showSnoozeOptions(false);
hideSelectedOption();
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ if (userAction) {
+ logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option);
+ }
+ }
+
+ private void logOptionSelection(int category, SnoozeOption option) {
+ int index = mSnoozeOptions.indexOf(option);
+ long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor());
+ mMetricsLogger.write(new LogMaker(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration));
}
@Override
@@ -284,13 +376,15 @@ public class NotificationSnooze extends LinearLayout
final int id = v.getId();
final SnoozeOption tag = (SnoozeOption) v.getTag();
if (tag != null) {
- setSelected(tag);
+ setSelected(tag, true);
} else if (id == R.id.notification_snooze) {
// Toggle snooze options
showSnoozeOptions(!mExpanded);
+ mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG);
} else {
// Undo snooze was selected
undoSnooze(v);
+ mMetricsLogger.write(UNDO_LOG);
}
}
@@ -321,7 +415,7 @@ public class NotificationSnooze extends LinearLayout
@Override
public View getContentView() {
// Reset the view before use
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
return this;
}
@@ -343,7 +437,7 @@ public class NotificationSnooze extends LinearLayout
return true;
} else {
// The view should actually be closed
- setSelected(mSnoozeOptions.get(0));
+ setSelected(mSnoozeOptions.get(0), false);
return false; // Return false here so that guts handles closing the view
}
}
diff --git a/com/android/systemui/statusbar/SignalClusterView.java b/com/android/systemui/statusbar/SignalClusterView.java
index 759d2cf2..a7fb61a1 100644
--- a/com/android/systemui/statusbar/SignalClusterView.java
+++ b/com/android/systemui/statusbar/SignalClusterView.java
@@ -16,14 +16,15 @@
package com.android.systemui.statusbar;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
@@ -50,14 +51,15 @@ import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils.DisableStateTracker;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
// Intimately tied to the design of res/layout/signal_cluster_view.xml
public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
- SecurityController.SecurityControllerCallback, Tunable,
- DarkReceiver {
+ SecurityController.SecurityControllerCallback, Tunable, DarkReceiver {
static final String TAG = "SignalClusterView";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -73,6 +75,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController
private boolean mNoSimsVisible = false;
private boolean mVpnVisible = false;
+ private boolean mSimDetected;
private int mVpnIconId = 0;
private int mLastVpnIconId = -1;
private boolean mEthernetVisible = false;
@@ -148,6 +151,8 @@ public class SignalClusterView extends LinearLayout implements NetworkController
mIconScaleFactor = typedValue.getFloat();
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
+ addOnAttachStateChangeListener(
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
updateActivityEnabled();
}
@@ -327,8 +332,9 @@ public class SignalClusterView extends LinearLayout implements NetworkController
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mNoSimsVisible = show && !mBlockMobile;
+ mSimDetected = simDetected;
apply();
}
@@ -548,6 +554,23 @@ public class SignalClusterView extends LinearLayout implements NetworkController
if (mNoSimsVisible) {
mIconLogger.onIconShown(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.VISIBLE);
+ if (!Objects.equals(mSimDetected, mNoSimsCombo.getTag())) {
+ mNoSimsCombo.setTag(mSimDetected);
+ if (mSimDetected) {
+ SignalDrawable d = new SignalDrawable(mNoSims.getContext());
+ d.setDarkIntensity(0);
+ mNoSims.setImageDrawable(d);
+ mNoSims.setImageLevel(SignalDrawable.getEmptyState(4));
+
+ SignalDrawable dark = new SignalDrawable(mNoSims.getContext());
+ dark.setDarkIntensity(1);
+ mNoSimsDark.setImageDrawable(dark);
+ mNoSimsDark.setImageLevel(SignalDrawable.getEmptyState(4));
+ } else {
+ mNoSims.setImageResource(R.drawable.stat_sys_no_sims);
+ mNoSimsDark.setImageResource(R.drawable.stat_sys_no_sims);
+ }
+ }
} else {
mIconLogger.onIconHidden(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.GONE);
diff --git a/com/android/systemui/statusbar/StatusBarIconView.java b/com/android/systemui/statusbar/StatusBarIconView.java
index 2cff79df..6cfd42fa 100644
--- a/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/com/android/systemui/statusbar/StatusBarIconView.java
@@ -43,6 +43,7 @@ import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
+import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -142,6 +143,7 @@ public class StatusBarIconView extends AnimatedImageView {
private float[] mMatrix;
private ColorMatrixColorFilter mMatrixColorFilter;
private boolean mIsInShelf;
+ private Runnable mLayoutRunnable;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -796,6 +798,24 @@ public class StatusBarIconView extends AnimatedImageView {
}
}
+ /**
+ * This method returns the drawing rect for the view which is different from the regular
+ * drawing rect, since we layout all children at position 0 and usually the translation is
+ * neglected. The standard implementation doesn't account for translation.
+ *
+ * @param outRect The (scrolled) drawing bounds of the view.
+ */
+ @Override
+ public void getDrawingRect(Rect outRect) {
+ super.getDrawingRect(outRect);
+ float translationX = getTranslationX();
+ float translationY = getTranslationY();
+ outRect.left += translationX;
+ outRect.right += translationX;
+ outRect.top += translationY;
+ outRect.bottom += translationY;
+ }
+
public void setIsInShelf(boolean isInShelf) {
mIsInShelf = isInShelf;
}
@@ -804,6 +824,19 @@ public class StatusBarIconView extends AnimatedImageView {
return mIsInShelf;
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mLayoutRunnable != null) {
+ mLayoutRunnable.run();
+ mLayoutRunnable = null;
+ }
+ }
+
+ public void executeOnLayout(Runnable runnable) {
+ mLayoutRunnable = runnable;
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/com/android/systemui/statusbar/car/CarNavigationBarController.java b/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 7e08d560..f5c77f26 100644
--- a/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -15,7 +15,13 @@
*/
package com.android.systemui.statusbar.car;
-import android.app.ActivityManager.StackId;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+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_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -101,7 +107,7 @@ class CarNavigationBarController {
}
}
- public void taskChanged(String packageName, int stackId) {
+ public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
// If the package name belongs to a filter, then highlight appropriate button in
// the navigation bar.
if (mFacetPackageMap.containsKey(packageName)) {
@@ -115,9 +121,11 @@ class CarNavigationBarController {
}
// Set up the persistent docked task if needed.
- if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask()
- && stackId != StackId.HOME_STACK_ID) {
- mStatusBar.startActivityOnStack(mPersistentTaskIntent, StackId.DOCKED_STACK_ID);
+ boolean isHomeTask =
+ taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
+ if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
+ mStatusBar.startActivityOnStack(mPersistentTaskIntent,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
}
}
@@ -375,13 +383,15 @@ class CarNavigationBarController {
// rather than the "preferred/last run" app.
intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
- int stackId = StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+ int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+ int activityType = ACTIVITY_TYPE_UNDEFINED;
if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
- stackId = StackId.HOME_STACK_ID;
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ activityType = ACTIVITY_TYPE_HOME;
}
setCurrentFacet(index);
- mStatusBar.startActivityOnStack(intent, stackId);
+ mStatusBar.startActivityOnStack(intent, windowingMode, activityType);
}
/**
@@ -391,6 +401,7 @@ class CarNavigationBarController {
*/
private void onFacetLongClicked(Intent intent, int index) {
setCurrentFacet(index);
- mStatusBar.startActivityOnStack(intent, StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ mStatusBar.startActivityOnStack(intent,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED);
}
}
diff --git a/com/android/systemui/statusbar/car/CarStatusBar.java b/com/android/systemui/statusbar/car/CarStatusBar.java
index 680f693a..59d3e0a3 100644
--- a/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -314,7 +314,7 @@ public class CarStatusBar extends StatusBar implements
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
- runningTaskInfo.stackId);
+ runningTaskInfo);
}
}
}
@@ -378,9 +378,10 @@ public class CarStatusBar extends StatusBar implements
return result;
}
- public int startActivityOnStack(Intent intent, int stackId) {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(stackId);
+ public int startActivityOnStack(Intent intent, int windowingMode, int activityType) {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(windowingMode);
+ options.setLaunchActivityType(activityType);
return startActivityWithOptions(intent, options.toBundle());
}
diff --git a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 9bfa7a97..bb979ebd 100644
--- a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -25,7 +25,6 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
@@ -48,7 +47,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
private int mContentHeight;
private int mMinHeightHint;
- private boolean mColorized;
protected NotificationTemplateViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
@@ -164,9 +162,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- StatusBarNotification sbn = row.getStatusBarNotification();
- resolveTemplateViews(sbn);
- mColorized = sbn.getNotification().isColorized();
+ resolveTemplateViews(row.getStatusBarNotification());
super.onContentUpdated(row);
}
@@ -269,17 +265,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
updateActionOffset();
}
- @Override
- public int getMinHeightIncrease(boolean useIncreasedCollapsedHeight) {
- if (mColorized) {
- int dimen = useIncreasedCollapsedHeight
- ? R.dimen.notification_height_increase_colorized_increased
- : R.dimen.notification_height_increase_colorized;
- return mRow.getResources().getDimensionPixelSize(dimen);
- }
- return super.getMinHeightIncrease(useIncreasedCollapsedHeight);
- }
-
private void updateActionOffset() {
if (mActionsContainer != null) {
// We should never push the actions higher than they are in the headsup view.
diff --git a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 085bce97..5200d696 100644
--- a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -190,14 +190,4 @@ public abstract class NotificationViewWrapper implements TransformableView {
public boolean disallowSingleClick(float x, float y) {
return false;
}
-
- /**
- * Get the amount that the minheight is allowed to be increased based on this layout.
- *
- * @param increasedHeight is the view allowed to show even bigger, i.e for messaging layouts
- * @return
- */
- public int getMinHeightIncrease(boolean increasedHeight) {
- return 0;
- }
}
diff --git a/com/android/systemui/statusbar/phone/BarTransitions.java b/com/android/systemui/statusbar/phone/BarTransitions.java
index 1f44abea..4bca7971 100644
--- a/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -50,7 +50,7 @@ public class BarTransitions {
public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
public static final int LIGHTS_IN_DURATION = 250;
- public static final int LIGHTS_OUT_DURATION = 750;
+ public static final int LIGHTS_OUT_DURATION = 1500;
public static final int BACKGROUND_DURATION = 200;
private final String mTag;
diff --git a/com/android/systemui/statusbar/phone/DozeScrimController.java b/com/android/systemui/statusbar/phone/DozeScrimController.java
index 021b4518..8afb8490 100644
--- a/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -384,7 +384,7 @@ public class DozeScrimController {
if (mDozeParameters.getAlwaysOn()) {
// Setting power states can happen after we push out the frame. Make sure we
// stay fully opaque until the power state request reaches the lower levels.
- setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 30);
+ setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100);
}
}
};
diff --git a/com/android/systemui/statusbar/phone/LightBarController.java b/com/android/systemui/statusbar/phone/LightBarController.java
index 533771a3..d226fede 100644
--- a/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/com/android/systemui/statusbar/phone/LightBarController.java
@@ -264,8 +264,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
pw.println(" StatusBarTransitionsController:");
mStatusBarIconController.getTransitionsController().dump(fd, pw, args);
pw.println();
- pw.println(" NavigationBarTransitionsController:");
- mNavigationBarController.dump(fd, pw, args);
- pw.println();
+ if (mNavigationBarController != null) {
+ pw.println(" NavigationBarTransitionsController:");
+ mNavigationBarController.dump(fd, pw, args);
+ pw.println();
+ }
}
}
diff --git a/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index cfe0a4a6..6d3bc1df 100644
--- a/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -437,7 +437,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
}
private boolean onNavigationTouch(View v, MotionEvent event) {
- mStatusBar.checkUserAutohide(v, event);
+ mStatusBar.checkUserAutohide(event);
return false;
}
diff --git a/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 41a69b4b..40fe50fb 100644
--- a/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,10 +4,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Icon;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -269,18 +267,26 @@ public class NotificationIconAreaController implements DarkReceiver {
*/
private void applyNotificationIconsTint() {
for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
- StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
- boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
- int color = StatusBarIconView.NO_COLOR;
- boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
- if (colorize) {
- color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+ final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i);
+ if (iv.getWidth() != 0) {
+ updateTintForIcon(iv);
+ } else {
+ iv.executeOnLayout(() -> updateTintForIcon(iv));
}
- v.setStaticDrawableColor(color);
- v.setDecorColor(mIconTint);
}
}
+ private void updateTintForIcon(StatusBarIconView v) {
+ boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
+ int color = StatusBarIconView.NO_COLOR;
+ boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
+ if (colorize) {
+ color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+ }
+ v.setStaticDrawableColor(color);
+ v.setDecorColor(mIconTint);
+ }
+
public void setDark(boolean dark) {
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
diff --git a/com/android/systemui/statusbar/phone/NotificationPanelView.java b/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 078e8189..7b11ace8 100644
--- a/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -509,7 +509,8 @@ public class NotificationPanelView extends PanelView implements
if (row.isRemoved()) {
continue;
}
- availableSpace -= child.getMinHeight() + notificationPadding;
+ availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
+ + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
} else if (availableSpace > -shelfSize) {
@@ -666,7 +667,7 @@ public class NotificationPanelView extends PanelView implements
return false;
}
initDownStates(event);
- if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
diff --git a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 4ae13936..9c837ed8 100644
--- a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+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 android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
@@ -523,12 +527,18 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
mCurrentNotifs.clear();
mUiOffloadThread.submit(() -> {
try {
- int focusedId = ActivityManager.getService().getFocusedStackId();
- if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) {
- checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm);
+ final StackInfo focusedStack = ActivityManager.getService().getFocusedStackInfo();
+ if (focusedStack != null) {
+ final int windowingMode =
+ focusedStack.configuration.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ checkStack(focusedStack, notifs, noMan, pm);
+ }
}
if (mDockedStackExists) {
- checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm);
+ checkStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED,
+ notifs, noMan, pm);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -539,10 +549,19 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
});
}
- private void checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs,
+ private void checkStack(int windowingMode, int activityType,
+ ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) {
+ try {
+ final StackInfo info =
+ ActivityManager.getService().getStackInfo(windowingMode, activityType);
+ checkStack(info, notifs, noMan, pm);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ private void checkStack(StackInfo info, ArraySet<Pair<String, Integer>> notifs,
NotificationManager noMan, IPackageManager pm) {
try {
- StackInfo info = ActivityManager.getService().getStackInfo(stackId);
if (info == null || info.topActivity == null) return;
String pkg = info.topActivity.getPackageName();
if (!hasNotif(notifs, pkg, info.userId)) {
diff --git a/com/android/systemui/statusbar/phone/StatusBar.java b/com/android/systemui/statusbar/phone/StatusBar.java
index efc8d8b9..54be857d 100644
--- a/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -37,7 +38,6 @@ import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.INotificationManager;
import android.app.KeyguardManager;
@@ -69,9 +69,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.PorterDuff;
@@ -105,10 +102,10 @@ import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -127,13 +124,11 @@ import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Interpolator;
import android.widget.DateTimeView;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -259,7 +254,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
@@ -279,11 +273,9 @@ public class StatusBar extends SystemUI implements DemoMode,
= SystemProperties.getBoolean("debug.child_notifs", true);
public static final boolean FORCE_REMOTE_INPUT_HISTORY =
SystemProperties.getBoolean("debug.force_remoteinput_history", false);
- private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
+ private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
- protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
- protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
@@ -339,7 +331,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private static final int STATUS_OR_NAV_TRANSIENT =
View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
- private static final long AUTOHIDE_TIMEOUT_MS = 3000;
+ private static final long AUTOHIDE_TIMEOUT_MS = 2250;
/** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
@@ -365,10 +357,6 @@ public class StatusBar extends SystemUI implements DemoMode,
/** If true, the lockscreen will show a distinct wallpaper */
private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
- /* If true, the device supports freeform window management.
- * This affects the status bar UI. */
- private static final boolean FREEFORM_WINDOW_MANAGEMENT;
-
/**
* How long to wait before auto-dismissing a notification that was kept for remote input, and
* has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
@@ -387,19 +375,14 @@ public class StatusBar extends SystemUI implements DemoMode,
static {
boolean onlyCoreApps;
- boolean freeformWindowManagement;
try {
IPackageManager packageManager =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
onlyCoreApps = packageManager.isOnlyCoreApps();
- freeformWindowManagement = packageManager.hasSystemFeature(
- PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0);
} catch (RemoteException e) {
onlyCoreApps = false;
- freeformWindowManagement = false;
}
ONLY_CORE_APPS = onlyCoreApps;
- FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
}
/**
@@ -410,17 +393,17 @@ public class StatusBar extends SystemUI implements DemoMode,
protected boolean mShowLockscreenNotifications;
protected boolean mAllowLockscreenRemoteInput;
- PhoneStatusBarPolicy mIconPolicy;
+ private PhoneStatusBarPolicy mIconPolicy;
- VolumeComponent mVolumeComponent;
- BrightnessMirrorController mBrightnessMirrorController;
+ private VolumeComponent mVolumeComponent;
+ private BrightnessMirrorController mBrightnessMirrorController;
protected FingerprintUnlockController mFingerprintUnlockController;
- LightBarController mLightBarController;
+ private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
- int mNaturalBarHeight = -1;
+ private int mNaturalBarHeight = -1;
- Point mCurrentDisplaySize = new Point();
+ private final Point mCurrentDisplaySize = new Point();
protected StatusBarWindowView mStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
@@ -431,15 +414,13 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
- int mPixelFormat;
- Object mQueueLock = new Object();
+ private final Object mQueueLock = new Object();
protected StatusBarIconController mIconController;
// expanded notifications
protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
- View mExpandedContents;
- TextView mNotificationPanelDebugText;
+ private TextView mNotificationPanelDebugText;
/**
* {@code true} if notifications not part of a group should by default be rendered in their
@@ -452,12 +433,10 @@ public class StatusBar extends SystemUI implements DemoMode,
private QSPanel mQSPanel;
// top bar
- protected KeyguardStatusBarView mKeyguardStatusBar;
- boolean mLeaveOpenOnKeyguardHide;
+ private KeyguardStatusBarView mKeyguardStatusBar;
+ private boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
- // Keyguard is going away soon.
- private boolean mKeyguardGoingAway;
// Keyguard is actually fading away now.
protected boolean mKeyguardFadingAway;
protected long mKeyguardFadingAwayDelay;
@@ -469,25 +448,19 @@ public class StatusBar extends SystemUI implements DemoMode,
private View mReportRejectedTouch;
- int mMaxAllowedKeyguardNotifications;
-
- boolean mExpandedVisible;
-
- // the tracker view
- int mTrackingPosition; // the position of the top of the tracking view.
+ private int mMaxAllowedKeyguardNotifications;
- // Tracking finger for opening/closing.
- boolean mTracking;
+ private boolean mExpandedVisible;
- int[] mAbsPos = new int[2];
- ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+ private final int[] mAbsPos = new int[2];
+ private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
// for disabling the status bar
- int mDisabled1 = 0;
- int mDisabled2 = 0;
+ private int mDisabled1 = 0;
+ private int mDisabled2 = 0;
// tracking calls to View.setSystemUiVisibility()
- int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
private final Rect mLastFullscreenStackBounds = new Rect();
private final Rect mLastDockedStackBounds = new Rect();
private final Rect mTmpRect = new Rect();
@@ -495,7 +468,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
- DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
// XXX: gesture research
private final GestureRecorder mGestureRec = DEBUG_GESTURES
@@ -507,14 +480,17 @@ public class StatusBar extends SystemUI implements DemoMode,
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
// ensure quick settings is disabled until the current user makes it through the setup wizard
- private boolean mUserSetup = false;
- private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
+ @VisibleForTesting
+ protected boolean mUserSetup = false;
+ private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
final boolean userSetup = mDeviceProvisionedController.isUserSetup(
mDeviceProvisionedController.getCurrentUser());
- if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
- "userSetup=%s mUserSetup=%s", userSetup, mUserSetup));
+ if (MULTIUSER_DEBUG) {
+ Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
+ userSetup, mUserSetup));
+ }
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
@@ -528,7 +504,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- protected H mHandler = createHandler();
+ protected final H mHandler = createHandler();
final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -537,8 +513,6 @@ public class StatusBar extends SystemUI implements DemoMode,
&& Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HEADS_UP_OFF);
- mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
if (wasUsing != mUseHeadsUp) {
if (!mUseHeadsUp) {
@@ -566,26 +540,21 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private boolean mWaitingForKeyguardExit;
protected boolean mDozing;
private boolean mDozingRequested;
protected boolean mScrimSrcModeEnabled;
- public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN;
- public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT;
-
protected BackDropView mBackdrop;
protected ImageView mBackdropFront, mBackdropBack;
- protected PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- protected PorterDuffXfermode mSrcOverXferMode =
+ protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+ protected final PorterDuffXfermode mSrcOverXferMode =
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
private MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
private String mMediaNotificationKey;
private MediaMetadata mMediaMetadata;
- private MediaController.Callback mMediaListener
- = new MediaController.Callback() {
+ private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
super.onPlaybackStateChanged(state);
@@ -607,17 +576,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
- new OnChildLocationsChangedListener() {
- @Override
- public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
- userActivity();
- }
- };
-
- private int mDisabledUnmodified1;
- private int mDisabledUnmodified2;
-
/** Keys of notifications currently visible to the user. */
private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
new ArraySet<>();
@@ -644,15 +602,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mWereIconsJustHidden;
private boolean mBouncerWasShowingWhenHidden;
- public boolean isStartedGoingToSleep() {
- return mStartedGoingToSleep;
- }
-
- /**
- * If set, the device has started going to sleep but isn't fully non-interactive yet.
- */
- protected boolean mStartedGoingToSleep;
-
private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() {
@Override
@@ -686,7 +635,6 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void run() {
mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
- final String mediaKey = getCurrentMediaNotificationKey();
// 1. Loop over mNotificationData entries:
// A. Keep list of visible notifications.
@@ -743,10 +691,10 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mKeyguardRequested;
private boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+ private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
protected NotificationIconAreaController mNotificationIconAreaController;
private boolean mReinflateNotificationsOnUserSwitched;
- private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
+ private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
private String mKeyToRemoveOnGutsClosed;
@@ -769,20 +717,21 @@ public class StatusBar extends SystemUI implements DemoMode,
goToLockedShade(null);
}
};
- private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
- = new HashMap<>();
+ private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
+ mTmpChildOrderMap = new HashMap<>();
private RankingMap mLatestRankingMap;
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
- private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onDreamingStateChanged(boolean dreaming) {
- if (dreaming) {
- maybeEscalateHeadsUp();
- }
- }
- };
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ if (dreaming) {
+ maybeEscalateHeadsUp();
+ }
+ }
+ };
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
@@ -861,10 +810,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mRecents = getComponent(Recents.class);
- final Configuration currentConfig = res.getConfiguration();
- mLocale = currentConfig.locale;
- mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
-
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
@@ -994,9 +939,6 @@ public class StatusBar extends SystemUI implements DemoMode,
Dependency.get(ConfigurationController.class).addCallback(this);
}
- protected void createIconController() {
- }
-
// ================================================================================
// Constructing the view
// ================================================================================
@@ -1012,16 +954,14 @@ public class StatusBar extends SystemUI implements DemoMode,
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
- R.id.notification_panel);
- mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
- R.id.notification_stack_scroller);
+ mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
+ mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
R.id.notification_container_parent));
- mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
+ mKeyguardStatusBar = mStatusBarWindow.findViewById(R.id.keyguard_header);
mNotificationIconAreaController = SystemUIFactory.getInstance()
.createNotificationIconAreaController(context, this);
@@ -1057,10 +997,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationData.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
+ putComponent(HeadsUpManager.class, mHeadsUpManager);
if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
- R.id.header_debug_info);
+ mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
@@ -1074,9 +1014,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// no window manager? good luck with that
}
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.OPAQUE;
-
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1086,11 +1023,10 @@ public class StatusBar extends SystemUI implements DemoMode,
inflateEmptyShadeView();
inflateDismissView();
- mExpandedContents = mStackScroller;
- mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
- mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
- mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
+ mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+ mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
+ mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -1098,8 +1034,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardIndicationController =
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
- (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
- mNotificationPanel.getLockIcon());
+ mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
+ mNotificationPanel.getLockIcon());
mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
@@ -1130,8 +1066,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mNavigationBar.setLightBarController(mLightBarController);
}
- ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
- ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
+ ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
+ ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
@@ -1141,13 +1077,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
});
if (mScrimSrcModeEnabled) {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
- mScrimController.setDrawBehindAsSrc(asSrc);
- mStackScroller.setDrawBackgroundAsSrc(asSrc);
- }
+ Runnable runnable = () -> {
+ boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
+ mScrimController.setDrawBehindAsSrc(asSrc);
+ mStackScroller.setDrawBackgroundAsSrc(asSrc);
};
mBackdrop.setOnVisibilityChangedRunnable(runnable);
runnable.run();
@@ -1169,11 +1102,11 @@ public class StatusBar extends SystemUI implements DemoMode,
if (container != null) {
FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- Dependency.get(ExtensionController.class).newExtension(QS.class)
+ Dependency.get(ExtensionController.class)
+ .newExtension(QS.class)
.withPlugin(QS.class)
- .withFeature(
- PackageManager.FEATURE_AUTOMOTIVE, () -> new CarQSFragment())
- .withDefault(() -> new QSFragment())
+ .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
+ .withDefault(QSFragment::new)
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
@@ -1275,7 +1208,7 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
protected View.OnTouchListener getStatusBarWindowTouchListener() {
return (v, event) -> {
- checkUserAutohide(v, event);
+ checkUserAutohide(event);
checkRemoteInputOutside(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
@@ -1379,8 +1312,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public static SignalClusterView reinflateSignalCluster(View view) {
Context context = view.getContext();
- SignalClusterView signalCluster =
- (SignalClusterView) view.findViewById(R.id.signal_cluster);
+ SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster);
if (signalCluster != null) {
ViewParent parent = signalCluster.getParent();
if (parent instanceof ViewGroup) {
@@ -1420,20 +1352,17 @@ public class StatusBar extends SystemUI implements DemoMode,
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
- mDismissView.setOnButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
- clearAllNotifications();
- }
+ mDismissView.setOnButtonClickListener(v -> {
+ mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+ clearAllNotifications();
});
mStackScroller.setDismissView(mDismissView);
}
protected void createUserSwitcher() {
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
- mKeyguardStatusBar, mNotificationPanel);
+ mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar,
+ mNotificationPanel);
}
protected void inflateStatusBarWindow(Context context) {
@@ -1446,7 +1375,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = mStackScroller.getChildCount();
- final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
+ final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
for (int i = 0; i < numChildren; i++) {
final View child = mStackScroller.getChildAt(i);
@@ -1486,20 +1415,18 @@ public class StatusBar extends SystemUI implements DemoMode,
return;
}
- addPostCollapseAction(new Runnable() {
- @Override
- public void run() {
- mStackScroller.setDismissAllInProgress(false);
- for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
- if (mStackScroller.canChildBeDismissed(rowToRemove)) {
- removeNotification(rowToRemove.getEntry().key, null);
- } else {
- rowToRemove.resetTranslation();
- }
+ addPostCollapseAction(() -> {
+ mStackScroller.setDismissAllInProgress(false);
+ for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
+ if (mStackScroller.canChildBeDismissed(rowToRemove)) {
+ removeNotification(rowToRemove.getEntry().key, null);
+ } else {
+ rowToRemove.resetTranslation();
}
- try {
- mBarService.onClearAllNotifications(mCurrentUserId);
- } catch (Exception ex) { }
+ }
+ try {
+ mBarService.onClearAllNotifications(mCurrentUserId);
+ } catch (Exception ex) {
}
});
@@ -1508,13 +1435,15 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
- Runnable animationFinishAction = new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
- }
+ Runnable animationFinishAction = () -> {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
};
+ if (hideAnimatedList.isEmpty()) {
+ animationFinishAction.run();
+ return;
+ }
+
// let's disable our normal animations
mStackScroller.setDismissAllInProgress(true);
@@ -1631,10 +1560,6 @@ public class StatusBar extends SystemUI implements DemoMode,
SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
}
- public UserHandle getCurrentUserHandle() {
- return new UserHandle(mCurrentUserId);
- }
-
public void addNotification(StatusBarNotification notification, RankingMap ranking)
throws InflationException {
String key = notification.getKey();
@@ -1745,7 +1670,7 @@ public class StatusBar extends SystemUI implements DemoMode,
boolean deferRemoval = false;
abortExistingInflation(key);
if (mHeadsUpManager.isHeadsUp(key)) {
- // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
+ // A cancel() in response to a remote input shouldn't be delayed, as it makes the
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
// some notifications can't disappear before the panel is closed.
@@ -1771,9 +1696,7 @@ public class StatusBar extends SystemUI implements DemoMode,
newHistory = new CharSequence[1];
} else {
newHistory = new CharSequence[oldHistory.length + 1];
- for (int i = 0; i < oldHistory.length; i++) {
- newHistory[i + 1] = oldHistory[i];
- }
+ System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
}
newHistory[0] = String.valueOf(entry.remoteInputText);
b.setRemoteInputHistory(newHistory);
@@ -1832,7 +1755,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mStackScroller.cleanUpViewState(entry.row);
}
// Let's remove the children if this was a summary
- handleGroupSummaryRemoved(key, ranking);
+ handleGroupSummaryRemoved(key);
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -1856,12 +1779,10 @@ public class StatusBar extends SystemUI implements DemoMode,
*
* This also ensures that the animation looks nice and only consists of a single disappear
* animation instead of multiple.
+ * @param key the key of the notification was removed
*
- * @param key the key of the notification was removed
- * @param ranking the current ranking
*/
- private void handleGroupSummaryRemoved(String key,
- RankingMap ranking) {
+ private void handleGroupSummaryRemoved(String key) {
Entry entry = mNotificationData.get(key);
if (entry != null && entry.row != null
&& entry.row.isSummaryWithChildren()) {
@@ -1872,15 +1793,13 @@ public class StatusBar extends SystemUI implements DemoMode,
}
List<ExpandableNotificationRow> notificationChildren =
entry.row.getNotificationChildren();
- ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow row = notificationChildren.get(i);
if ((row.getStatusBarNotification().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- // the child is a forground service notification which we can't remove!
+ // the child is a foreground service notification which we can't remove!
continue;
}
- toRemove.add(row);
row.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
@@ -1900,7 +1819,9 @@ public class StatusBar extends SystemUI implements DemoMode,
final int id = n.getId();
final int userId = n.getUserId();
try {
- mBarService.onNotificationClear(pkg, tag, id, userId);
+ // TODO: record actual dismissal surface
+ mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
+ NotificationStats.DISMISSAL_OTHER);
if (FORCE_REMOTE_INPUT_HISTORY
&& mKeysKeptForRemoteInput.contains(n.getKey())) {
mKeysKeptForRemoteInput.remove(n.getKey());
@@ -1923,19 +1844,14 @@ public class StatusBar extends SystemUI implements DemoMode,
// Do not modify the notifications during collapse.
if (isCollapsing()) {
- addPostCollapseAction(new Runnable() {
- @Override
- public void run() {
- updateNotificationShade();
- }
- });
+ addPostCollapseAction(this::updateNotificationShade);
return;
}
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
Entry ent = activeNotifications.get(i);
if (ent.row.isDismissed() || ent.row.isRemoved()) {
// we don't want to update removed notifications because they could
@@ -1979,7 +1895,8 @@ public class StatusBar extends SystemUI implements DemoMode,
for (ExpandableNotificationRow remove : toRemove) {
if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
- // we are only transfering this notification to its parent, don't generate an animation
+ // we are only transferring this notification to its parent, don't generate an
+ // animation
mStackScroller.setChildTransferInProgress(true);
}
if (remove.isSummaryWithChildren()) {
@@ -1991,7 +1908,7 @@ public class StatusBar extends SystemUI implements DemoMode,
removeNotificationChildren();
- for (int i=0; i<toShow.size(); i++) {
+ for (int i = 0; i < toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
mVisualStabilityManager.notifyViewAddition(v);
@@ -2065,6 +1982,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
+ && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS);
@@ -2101,7 +2019,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- // Finally after removing and adding has been beformed we can apply the order.
+ // Finally after removing and adding has been performed we can apply the order.
orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this);
}
if (orderChanged) {
@@ -2274,10 +2192,11 @@ public class StatusBar extends SystemUI implements DemoMode,
MediaController controller = null;
for (int i = 0; i < N; i++) {
final Entry entry = activeNotifications.get(i);
+
if (isMediaNotification(entry)) {
final MediaSession.Token token =
- entry.notification.getNotification().extras
- .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+ entry.notification.getNotification().extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION);
if (token != null) {
MediaController aController = new MediaController(mContext, token);
if (PlaybackState.STATE_PLAYING ==
@@ -2315,7 +2234,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (entry.notification.getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
+ + entry.notification.getKey());
}
controller = aController;
mediaNotification = entry;
@@ -2366,12 +2285,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private boolean isPlaybackActive(int state) {
- if (state != PlaybackState.STATE_STOPPED
- && state != PlaybackState.STATE_ERROR
- && state != PlaybackState.STATE_NONE) {
- return true;
- }
- return false;
+ return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
+ && state != PlaybackState.STATE_NONE;
}
private void clearCurrentMediaNotification() {
@@ -2396,7 +2311,7 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* Hide the album artwork that is fading out and release its bitmap.
*/
- protected Runnable mHideBackdropFront = new Runnable() {
+ protected final Runnable mHideBackdropFront = new Runnable() {
@Override
public void run() {
if (DEBUG_MEDIA) {
@@ -2548,14 +2463,11 @@ public class StatusBar extends SystemUI implements DemoMode,
.setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.setDuration(300)
.setStartDelay(0)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mBackdrop.setVisibility(View.GONE);
- mBackdropFront.animate().cancel();
- mBackdropBack.setImageDrawable(null);
- mHandler.post(mHideBackdropFront);
- }
+ .withEndAction(() -> {
+ mBackdrop.setVisibility(View.GONE);
+ mBackdropFront.animate().cancel();
+ mBackdropBack.setImageDrawable(null);
+ mHandler.post(mHideBackdropFront);
});
if (mKeyguardFadingAway) {
mBackdrop.animate()
@@ -2586,8 +2498,6 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void disable(int state1, int state2, boolean animate) {
animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
- mDisabledUnmodified1 = state1;
- mDisabledUnmodified2 = state2;
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
mDisabled1 = state1;
@@ -2623,8 +2533,13 @@ public class StatusBar extends SystemUI implements DemoMode,
flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
+ flagdbg.append("> disable2<");
flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
flagdbg.append('>');
Log.d(TAG, flagdbg.toString());
@@ -2651,6 +2566,13 @@ public class StatusBar extends SystemUI implements DemoMode,
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
updateQsExpansionEnabled();
}
+
+ if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ updateQsExpansionEnabled();
+ if ((state1 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ animateCollapsePanels();
+ }
+ }
}
/**
@@ -2738,11 +2660,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// make sure that the window stays small for one frame until the touchableRegion is set.
mNotificationPanel.requestLayout();
mStatusBarWindowManager.setForceWindowCollapsed(true);
- mNotificationPanel.post(new Runnable() {
- @Override
- public void run() {
- mStatusBarWindowManager.setForceWindowCollapsed(false);
- }
+ mNotificationPanel.post(() -> {
+ mStatusBarWindowManager.setForceWindowCollapsed(false);
});
}
} else {
@@ -2754,15 +2673,12 @@ public class StatusBar extends SystemUI implements DemoMode,
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
mHeadsUpManager.setHeadsUpGoingAway(true);
- mStackScroller.runAfterAnimationFinished(new Runnable() {
- @Override
- public void run() {
- if (!mHeadsUpManager.hasPinnedHeadsUp()) {
- mStatusBarWindowManager.setHeadsUpShowing(false);
- mHeadsUpManager.setHeadsUpGoingAway(false);
- }
- removeRemoteInputEntriesKeptUntilCollapsed();
+ mStackScroller.runAfterAnimationFinished(() -> {
+ if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+ mStatusBarWindowManager.setHeadsUpShowing(false);
+ mHeadsUpManager.setHeadsUpGoingAway(false);
}
+ removeRemoteInputEntriesKeptUntilCollapsed();
});
}
}
@@ -3013,7 +2929,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
boolean panelsEnabled() {
- return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
+ return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+ && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+ && !ONLY_CORE_APPS;
}
void makeExpandedVisible(boolean force) {
@@ -3029,7 +2947,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindowManager.setPanelVisible(true);
visibilityChanged(true);
- mWaitingForKeyguardExit = false;
recomputeDisableFlags(!force /* animate */);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -3038,23 +2955,15 @@ public class StatusBar extends SystemUI implements DemoMode,
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
- private final Runnable mAnimateCollapsePanels = new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels();
- }
- };
+ private final Runnable mAnimateCollapsePanels = this::animateCollapsePanels;
public void postAnimateCollapsePanels() {
mHandler.post(mAnimateCollapsePanels);
}
public void postAnimateForceCollapsePanels() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
- }
+ mHandler.post(() -> {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
});
}
@@ -3104,6 +3013,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
+ // TODO(b/62444020): remove when this bug is fixed
+ Log.v(TAG, "mStatusBarWindow: " + mStatusBarWindow + " canPanelBeCollapsed(): "
+ + mNotificationPanel.canPanelBeCollapsed());
if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) {
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -3209,7 +3121,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (SPEW) {
Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
- + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking);
+ + mDisabled1 + " mDisabled2=" + mDisabled2);
} else if (CHATTY) {
if (event.getAction() != MotionEvent.ACTION_MOVE) {
Log.d(TAG, String.format(
@@ -3294,10 +3206,8 @@ public class StatusBar extends SystemUI implements DemoMode,
sbModeChanged = sbMode != -1;
if (sbModeChanged && sbMode != mStatusBarMode) {
- if (sbMode != mStatusBarMode) {
- mStatusBarMode = sbMode;
- checkBarModes();
- }
+ mStatusBarMode = sbMode;
+ checkBarModes();
touchAutoHide();
}
@@ -3321,7 +3231,6 @@ public class StatusBar extends SystemUI implements DemoMode,
} else {
cancelAutohide();
}
- touchAutoDim();
}
protected int computeStatusBarMode(int oldVal, int newVal) {
@@ -3385,12 +3294,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- private final Runnable mCheckBarModes = new Runnable() {
- @Override
- public void run() {
- checkBarModes();
- }
- };
+ private final Runnable mCheckBarModes = this::checkBarModes;
public void setInteracting(int barWindow, boolean interacting) {
final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
@@ -3404,10 +3308,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
// manually dismiss the volume panel when interacting with the nav bar
if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
+ touchAutoDim();
dismissVolumeDialog();
}
checkBarModes();
- touchAutoDim();
}
private void dismissVolumeDialog() {
@@ -3449,7 +3353,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- void checkUserAutohide(View v, MotionEvent event) {
+ void checkUserAutohide(MotionEvent event) {
if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
@@ -3516,9 +3420,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
pw.println("Current Status Bar state:");
- pw.println(" mExpandedVisible=" + mExpandedVisible
- + ", mTrackingPosition=" + mTrackingPosition);
- pw.println(" mTracking=" + mTracking);
+ pw.println(" mExpandedVisible=" + mExpandedVisible);
pw.println(" mDisplayMetrics=" + mDisplayMetrics);
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
pw.println(" mStackScroller: " + viewInfo(mStackScroller)
@@ -3606,16 +3508,12 @@ public class StatusBar extends SystemUI implements DemoMode,
if (false) {
pw.println("see the logcat for a dump of the views we have created.");
// must happen on ui thread
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + getStatusBarHeight());
- mStatusBarView.debug();
- }
- });
+ mHandler.post(() -> {
+ mStatusBarView.getLocationOnScreen(mAbsPos);
+ Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] +
+ ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight());
+ mStatusBarView.debug();
+ });
}
}
@@ -3695,49 +3593,43 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, mCurrentUserId);
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- mAssistManager.hideAssist();
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- int result = ActivityManager.START_CANCELED;
- ActivityOptions options = new ActivityOptions(getActivityOptions());
- options.setDisallowEnterPictureInPictureWhileLaunching(
- disallowEnterPictureInPictureWhileLaunching);
- if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
- // Normally an activity will set it's requested rotation
- // animation on its window. However when launching an activity
- // causes the orientation to change this is too late. In these cases
- // the default animation is used. This doesn't look good for
- // the camera (as it rotates the camera contents out of sync
- // with physical reality). So, we ask the WindowManager to
- // force the crossfade animation if an orientation change
- // happens to occur during the launch.
- options.setRotationAnimationHint(
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
- }
- try {
- result = ActivityManager.getService().startActivityAsUser(
- null, mContext.getBasePackageName(),
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
- options.toBundle(), UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to start activity", e);
- }
- if (callback != null) {
- callback.onActivityStarted(result);
- }
+ Runnable runnable = () -> {
+ mAssistManager.hideAssist();
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ int result = ActivityManager.START_CANCELED;
+ ActivityOptions options = new ActivityOptions(getActivityOptions());
+ options.setDisallowEnterPictureInPictureWhileLaunching(
+ disallowEnterPictureInPictureWhileLaunching);
+ if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the crossfade animation if an orientation change
+ // happens to occur during the launch.
+ options.setRotationAnimationHint(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+ }
+ try {
+ result = ActivityManager.getService().startActivityAsUser(
+ null, mContext.getBasePackageName(),
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+ options.toBundle(), UserHandle.CURRENT.getIdentifier());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to start activity", e);
+ }
+ if (callback != null) {
+ callback.onActivityStarted(result);
}
};
- Runnable cancelRunnable = new Runnable() {
- @Override
- public void run() {
- if (callback != null) {
- callback.onActivityStarted(ActivityManager.START_CANCELED);
- }
+ Runnable cancelRunnable = () -> {
+ if (callback != null) {
+ callback.onActivityStarted(ActivityManager.START_CANCELED);
}
};
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
@@ -3782,7 +3674,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}, cancelAction, afterKeyguardGone);
}
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
@@ -3811,7 +3703,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
@@ -3972,7 +3864,6 @@ public class StatusBar extends SystemUI implements DemoMode,
* The LEDs are turned off when the notification panel is shown, even just a little bit.
* See also StatusBar.setPanelExpanded for another place where we attempt to do this.
*/
- // Old BaseStatusBar.handleVisibileToUserChanged
private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
try {
if (visibleToUser) {
@@ -3997,8 +3888,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// Report all notifications as invisible and turn down the
// reporter.
if (!mCurrentlyVisibleNotifications.isEmpty()) {
- logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(),
- mCurrentlyVisibleNotifications);
+ logNotificationVisibilityChanges(
+ Collections.emptyList(), mCurrentlyVisibleNotifications);
recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
}
mHandler.removeCallbacks(mVisibilityReporter);
@@ -4105,7 +3996,7 @@ public class StatusBar extends SystemUI implements DemoMode,
vib.vibrate(250, VIBRATION_ATTRIBUTES);
}
- Runnable mStartTracing = new Runnable() {
+ final Runnable mStartTracing = new Runnable() {
@Override
public void run() {
vibrate();
@@ -4116,13 +4007,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- Runnable mStopTracing = new Runnable() {
- @Override
- public void run() {
- android.os.Debug.stopMethodTracing();
- Log.d(TAG, "stopTracing");
- vibrate();
- }
+ final Runnable mStopTracing = () -> {
+ android.os.Debug.stopMethodTracing();
+ Log.d(TAG, "stopTracing");
+ vibrate();
};
@Override
@@ -4149,40 +4037,6 @@ public class StatusBar extends SystemUI implements DemoMode,
startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
}
- private static class FastColorDrawable extends Drawable {
- private final int mColor;
-
- public FastColorDrawable(int color) {
- mColor = 0xff000000 | color;
- }
-
- @Override
- public void draw(Canvas canvas) {
- canvas.drawColor(mColor, PorterDuff.Mode.SRC);
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- @Override
- public void setBounds(int left, int top, int right, int bottom) {
- }
-
- @Override
- public void setBounds(Rect bounds) {
- }
- }
-
public void destroy() {
// Begin old BaseStatusBar.destroy().
mContext.unregisterReceiver(mBaseBroadcastReceiver);
@@ -4400,31 +4254,23 @@ public class StatusBar extends SystemUI implements DemoMode,
Runnable endRunnable) {
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
mLaunchTransitionEndRunnable = endRunnable;
- Runnable hideRunnable = new Runnable() {
- @Override
- public void run() {
- mLaunchTransitionFadingAway = true;
- if (beforeFading != null) {
- beforeFading.run();
- }
- mScrimController.forceHideScrims(true /* hide */, false /* animated */);
- updateMediaMetaData(false, true);
- mNotificationPanel.setAlpha(1);
- mStackScroller.setParentNotFullyVisible(true);
- mNotificationPanel.animate()
- .alpha(0)
- .setStartDelay(FADE_KEYGUARD_START_DELAY)
- .setDuration(FADE_KEYGUARD_DURATION)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- onLaunchTransitionFadingEnded();
- }
- });
- mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- }
+ Runnable hideRunnable = () -> {
+ mLaunchTransitionFadingAway = true;
+ if (beforeFading != null) {
+ beforeFading.run();
+ }
+ mScrimController.forceHideScrims(true /* hide */, false /* animated */);
+ updateMediaMetaData(false, true);
+ mNotificationPanel.setAlpha(1);
+ mStackScroller.setParentNotFullyVisible(true);
+ mNotificationPanel.animate()
+ .alpha(0)
+ .setStartDelay(FADE_KEYGUARD_START_DELAY)
+ .setDuration(FADE_KEYGUARD_DURATION)
+ .withLayer()
+ .withEndAction(this::onLaunchTransitionFadingEnded);
+ mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
+ LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
if (mNotificationPanel.isLaunchTransitionRunning()) {
mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
@@ -4553,7 +4399,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
- mKeyguardGoingAway = true;
mKeyguardMonitor.notifyKeyguardGoingAway(true);
mCommandQueue.appTransitionPending(true);
}
@@ -4562,14 +4407,13 @@ public class StatusBar extends SystemUI implements DemoMode,
* Notifies the status bar the Keyguard is fading away with the specified timings.
*
* @param startTime the start time of the animations in uptime millis
- * @param delay the precalculated animation delay in miliseconds
+ * @param delay the precalculated animation delay in milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
mKeyguardFadingAway = true;
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
- mWaitingForKeyguardExit = false;
mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
@@ -4589,14 +4433,9 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
public void finishKeyguardFadingAway() {
mKeyguardFadingAway = false;
- mKeyguardGoingAway = false;
mKeyguardMonitor.notifyKeyguardDoneFading();
}
- public void stopWaitingForKeyguardExit() {
- mWaitingForKeyguardExit = false;
- }
-
private void updatePublicMode() {
final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
final boolean devicePublic = showingKeyguard
@@ -4810,7 +4649,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
protected void showBouncer() {
- mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
mStatusBarKeyguardViewManager.dismiss();
}
@@ -4827,7 +4665,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onActivated(ActivatableNotificationView view) {
- onActivated((View)view);
+ onActivated((View) view);
mStackScroller.setActivatedChild(view);
}
@@ -5022,6 +4860,10 @@ public class StatusBar extends SystemUI implements DemoMode,
* @param expandView The view to expand after going to the shade.
*/
public void goToLockedShade(View expandView) {
+ if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ return;
+ }
+
int userId = mCurrentUserId;
ExpandableNotificationRow row = null;
if (expandView instanceof ExpandableNotificationRow) {
@@ -5129,51 +4971,41 @@ public class StatusBar extends SystemUI implements DemoMode,
updateNotifications();
if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
// Expand notification panel and the notification row, then click on remote input view
- final Runnable clickPendingViewRunnable = new Runnable() {
- @Override
- public void run() {
- final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
- if (pendingWorkRemoteInputView == null) {
+ final Runnable clickPendingViewRunnable = () -> {
+ final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+ if (pendingWorkRemoteInputView == null) {
+ return;
+ }
+
+ // Climb up the hierarchy until we get to the container for this row.
+ ViewParent p = pendingWorkRemoteInputView.getParent();
+ while (!(p instanceof ExpandableNotificationRow)) {
+ if (p == null) {
return;
}
+ p = p.getParent();
+ }
- // Climb up the hierarchy until we get to the container for this row.
- ViewParent p = pendingWorkRemoteInputView.getParent();
- while (!(p instanceof ExpandableNotificationRow)) {
- if (p == null) {
- return;
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
+ ViewParent viewParent = row.getParent();
+ if (viewParent instanceof NotificationStackScrollLayout) {
+ final NotificationStackScrollLayout scrollLayout =
+ (NotificationStackScrollLayout) viewParent;
+ row.makeActionsVisibile();
+ row.post(() -> {
+ final Runnable finishScrollingCallback = () -> {
+ mPendingWorkRemoteInputView.callOnClick();
+ mPendingWorkRemoteInputView = null;
+ scrollLayout.setFinishScrollingCallback(null);
+ };
+ if (scrollLayout.scrollTo(row)) {
+ // It scrolls! So call it when it's finished.
+ scrollLayout.setFinishScrollingCallback(finishScrollingCallback);
+ } else {
+ // It does not scroll, so call it now!
+ finishScrollingCallback.run();
}
- p = p.getParent();
- }
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
- ViewParent viewParent = row.getParent();
- if (viewParent instanceof NotificationStackScrollLayout) {
- final NotificationStackScrollLayout scrollLayout =
- (NotificationStackScrollLayout) viewParent;
- row.makeActionsVisibile();
- row.post(new Runnable() {
- @Override
- public void run() {
- final Runnable finishScrollingCallback = new Runnable() {
- @Override
- public void run() {
- mPendingWorkRemoteInputView.callOnClick();
- mPendingWorkRemoteInputView = null;
- scrollLayout.setFinishScrollingCallback(null);
- }
- };
- if (scrollLayout.scrollTo(row)) {
- // It scrolls! So call it when it's finished.
- scrollLayout.setFinishScrollingCallback(
- finishScrollingCallback);
- } else {
- // It does not scroll, so call it now!
- finishScrollingCallback.run();
- }
- }
- });
- }
+ });
}
};
mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
@@ -5236,7 +5068,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
+ final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
mNotificationPanel.onAffordanceLaunchEnded();
@@ -5259,12 +5091,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// This gets executed before we will show Keyguard, so post it in order that the state
// is correct.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onCameraLaunchGestureDetected(mLastCameraLaunchSource);
- }
- });
+ mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource));
}
updateIsKeyguard();
}
@@ -5290,7 +5117,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurningOn() {
mFalsingManager.onScreenTurningOn();
@@ -5487,7 +5314,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private final class DozeServiceHost implements DozeHost {
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private boolean mAnimateWakeup;
private boolean mIgnoreTouchWhilePulsing;
@@ -5700,7 +5527,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected NotificationData mNotificationData;
protected NotificationStackScrollLayout mStackScroller;
- protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ protected final NotificationGroupManager mGroupManager = new NotificationGroupManager();
protected RemoteInputController mRemoteInputController;
@@ -5710,34 +5537,30 @@ public class StatusBar extends SystemUI implements DemoMode,
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
- protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
+ protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
protected int mCurrentUserId = 0;
- final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+ final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
- protected int mLayoutDirection = -1; // invalid
protected AccessibilityManager mAccessibilityManager;
protected boolean mDeviceInteractive;
protected boolean mVisible;
- protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
- protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
+ protected final ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ protected final ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
/**
* Notifications with keys in this set are not actually around anymore. We kept them around
* when they were canceled in response to a remote input interaction. This allows us to show
* what you replied and allows you to continue typing into it.
*/
- protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+ protected final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
- private Locale mLocale;
-
protected boolean mUseHeadsUp = false;
- protected boolean mHeadsUpTicker = false;
protected boolean mDisableNotificationAlerts = false;
protected DevicePolicyManager mDevicePolicyManager;
@@ -5772,13 +5595,11 @@ public class StatusBar extends SystemUI implements DemoMode,
private NotificationGuts mNotificationGutsExposed;
private MenuItem mGutsMenuItem;
- private KeyboardShortcuts mKeyboardShortcuts;
-
protected NotificationShelf mNotificationShelf;
protected DismissView mDismissView;
protected EmptyShadeView mEmptyShadeView;
- private NotificationClicker mNotificationClicker = new NotificationClicker();
+ private final NotificationClicker mNotificationClicker = new NotificationClicker();
protected AssistManager mAssistManager;
@@ -5838,15 +5659,14 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+ private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
@Override
public boolean onClickHandler(
final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
wakeUpIfDozing(SystemClock.uptimeMillis(), view);
-
- if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
+ if (handleRemoteInput(view, pendingIntent)) {
return true;
}
@@ -5864,33 +5684,29 @@ public class StatusBar extends SystemUI implements DemoMode,
}
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
mContext, pendingIntent.getIntent(), mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
+ dismissKeyguardThenExecute(() -> {
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
- // close the shade if it was open
- if (handled && !mNotificationPanel.isFullyCollapsed()) {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
- visibilityChanged(false);
- mAssistManager.hideAssist();
+ boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
- // Wait for activity start.
- return true;
- } else {
- return false;
- }
+ // close the shade if it was open
+ if (handled && !mNotificationPanel.isFullyCollapsed()) {
+ animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ visibilityChanged(false);
+ mAssistManager.hideAssist();
+ // Wait for activity start.
+ return true;
+ } else {
+ return false;
}
+
}, afterKeyguardGone);
return true;
} else {
@@ -5932,10 +5748,15 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
return super.onClickHandler(view, pendingIntent, fillInIntent,
- StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
}
- private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+ private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
+ if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ // Skip remote input as doing so will expand the notification shade.
+ return true;
+ }
+
Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
RemoteInput[] inputs = null;
if (tag instanceof RemoteInput[]) {
@@ -6050,7 +5871,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
updateCurrentProfilesCache();
- if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
updateLockscreenNotificationSetting();
@@ -6073,8 +5894,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Toast toast = Toast.makeText(mContext,
R.string.managed_profile_foreground_toast,
Toast.LENGTH_SHORT);
- TextView text = (TextView) toast.getView().findViewById(
- android.R.id.message);
+ TextView text = toast.getView().findViewById(android.R.id.message);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
int paddingPx = mContext.getResources().getDimensionPixelSize(
@@ -6150,15 +5970,12 @@ public class StatusBar extends SystemUI implements DemoMode,
return;
}
final RankingMap currentRanking = getCurrentRanking();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (StatusBarNotification sbn : notifications) {
- try {
- addNotification(sbn, currentRanking);
- } catch (InflationException e) {
- handleInflationException(sbn, e);
- }
+ mHandler.post(() -> {
+ for (StatusBarNotification sbn : notifications) {
+ try {
+ addNotification(sbn, currentRanking);
+ } catch (InflationException e) {
+ handleInflationException(sbn, e);
}
}
});
@@ -6169,40 +5986,37 @@ public class StatusBar extends SystemUI implements DemoMode,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- processForRemoteInput(sbn.getNotification());
- String key = sbn.getKey();
- mKeysKeptForRemoteInput.remove(key);
- boolean isUpdate = mNotificationData.get(key) != null;
- // In case we don't allow child notifications, we ignore children of
- // notifications that have a summary, since we're not going to show them
- // anyway. This is true also when the summary is canceled,
- // because children are automatically canceled by NoMan in that case.
- if (!ENABLE_CHILD_NOTIFICATIONS
+ mHandler.post(() -> {
+ processForRemoteInput(sbn.getNotification());
+ String key = sbn.getKey();
+ mKeysKeptForRemoteInput.remove(key);
+ boolean isUpdate = mNotificationData.get(key) != null;
+ // In case we don't allow child notifications, we ignore children of
+ // notifications that have a summary, since we're not going to show them
+ // anyway. This is true also when the summary is canceled,
+ // because children are automatically canceled by NoMan in that case.
+ if (!ENABLE_CHILD_NOTIFICATIONS
&& mGroupManager.isChildInGroupWithSummary(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
- }
+ if (DEBUG) {
+ Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
+ }
- // Remove existing notification to avoid stale data.
- if (isUpdate) {
- removeNotification(key, rankingMap);
- } else {
- mNotificationData.updateRanking(rankingMap);
- }
- return;
+ // Remove existing notification to avoid stale data.
+ if (isUpdate) {
+ removeNotification(key, rankingMap);
+ } else {
+ mNotificationData.updateRanking(rankingMap);
}
- try {
- if (isUpdate) {
- updateNotification(sbn, rankingMap);
- } else {
- addNotification(sbn, rankingMap);
- }
- } catch (InflationException e) {
- handleInflationException(sbn, e);
+ return;
+ }
+ try {
+ if (isUpdate) {
+ updateNotification(sbn, rankingMap);
+ } else {
+ addNotification(sbn, rankingMap);
}
+ } catch (InflationException e) {
+ handleInflationException(sbn, e);
}
});
}
@@ -6250,7 +6064,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
return;
}
- Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+ Log.d(TAG, "disabling lockscreen notifications and alerting the user");
// disable lockscreen notifications until user acts on the banner.
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
@@ -6291,11 +6105,10 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override // NotificationData.Environment
public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
- final int thisUserId = mCurrentUserId;
final int notificationUserId = n.getUserId();
if (DEBUG && MULTIUSER_DEBUG) {
- Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
- n, thisUserId, notificationUserId));
+ Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
+ mCurrentUserId, notificationUserId));
}
return isCurrentProfile(notificationUserId);
}
@@ -6343,21 +6156,15 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private void startNotificationGutsIntent(final Intent intent, final int appUid) {
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(),
- new UserHandle(UserHandle.getUserId(appUid)));
- }
- });
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- return true;
- }
+ dismissKeyguardThenExecute(() -> {
+ AsyncTask.execute(() -> {
+ TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(),
+ new UserHandle(UserHandle.getUserId(appUid)));
+ });
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ return true;
}, false /* afterKeyguardGone */);
}
@@ -6428,7 +6235,7 @@ public class StatusBar extends SystemUI implements DemoMode,
startNotificationGutsIntent(intent, sbn.getUid());
};
final View.OnClickListener onDoneClick = (View v) -> {
- saveAndCloseNotificationMenu(info, row, guts, v);
+ saveAndCloseNotificationMenu(row, guts, v);
};
final NotificationInfo.CheckSaveListener checkSaveListener =
(Runnable saveImportance) -> {
@@ -6445,7 +6252,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- ArraySet<NotificationChannel> channels = new ArraySet<NotificationChannel>();
+ ArraySet<NotificationChannel> channels = new ArraySet<>();
channels.add(row.getEntry().channel);
if (row.isSummaryWithChildren()) {
// If this is a summary, then add in the children notification channels for the
@@ -6473,7 +6280,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- private void saveAndCloseNotificationMenu(NotificationInfo info,
+ private void saveAndCloseNotificationMenu(
ExpandableNotificationRow row, NotificationGuts guts, View done) {
guts.resetFalsingCheck();
int[] rowLocation = new int[2];
@@ -6642,13 +6449,6 @@ public class StatusBar extends SystemUI implements DemoMode,
updateHideIconsForBouncer(true /* animate */);
}
- protected void sendCloseSystemWindows(String reason) {
- try {
- ActivityManager.getService().closeSystemDialogs(reason);
- } catch (RemoteException e) {
- }
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
@@ -6750,18 +6550,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return isLockscreenPublicMode(userId);
}
- public void onNotificationClear(StatusBarNotification notification) {
- try {
- mBarService.onNotificationClear(
- notification.getPackageName(),
- notification.getTag(),
- notification.getId(),
- notification.getUserId());
- } catch (android.os.RemoteException ex) {
- // oh well
- }
- }
-
/**
* Called when the notification panel layouts
*/
@@ -6919,49 +6707,42 @@ public class StatusBar extends SystemUI implements DemoMode,
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
if (!isDeviceProvisioned()) return;
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- new Thread() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- try {
- intent.send(null, 0, null, null, null, null, getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending intent failed: " + e);
+ dismissKeyguardThenExecute(() -> {
+ new Thread(() -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: " + e);
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
- }
- }
- }.start();
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
+ }).start();
- if (!mNotificationPanel.isFullyCollapsed()) {
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
+ if (!mNotificationPanel.isFullyCollapsed()) {
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+ true /* delayed */);
+ visibilityChanged(false);
- return true;
- } else {
- return false;
- }
+ return true;
+ } else {
+ return false;
}
}, afterKeyguardGone);
}
@@ -6999,130 +6780,110 @@ public class StatusBar extends SystemUI implements DemoMode,
// Mark notification for one frame.
row.setJustClicked(true);
- DejankUtils.postAfterTraversal(new Runnable() {
- @Override
- public void run() {
- row.setJustClicked(false);
- }
- });
+ DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
- // Release the HUN notification to the shade.
+ dismissKeyguardThenExecute(() -> {
+ if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
+ // Release the HUN notification to the shade.
- if (isPanelFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.releaseImmediately(notificationKey);
+ if (isPanelFullyCollapsed()) {
+ HeadsUpManager.setIsClickedNotification(row, true);
}
- StatusBarNotification parentToCancel = null;
- if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
- StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
- .getStatusBarNotification();
- if (shouldAutoCancel(summarySbn)) {
- parentToCancel = summarySbn;
- }
+ //
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.releaseImmediately(notificationKey);
+ }
+ StatusBarNotification parentToCancel = null;
+ if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+ StatusBarNotification summarySbn =
+ mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+ if (shouldAutoCancel(summarySbn)) {
+ parentToCancel = summarySbn;
}
- final StatusBarNotification parentToCancelFinal = parentToCancel;
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- if (intent != null) {
- // If we are launching a work activity and require to launch
- // separate work challenge, we defer the activity action and cancel
- // notification until work challenge is unlocked.
- if (intent.isActivity()) {
- final int userId = intent.getCreatorUserHandle()
- .getIdentifier();
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mKeyguardManager.isDeviceLocked(userId)) {
- // TODO(b/28935539): should allow certain activities to
- // bypass work challenge
- if (startWorkChallengeIfNecessary(userId,
- intent.getIntentSender(), notificationKey)) {
- // Show work challenge, do not run PendingIntent and
- // remove notification
- return;
- }
- }
- }
- try {
- intent.send(null, 0, null, null, null, null,
- getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending contentIntent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
+ }
+ final StatusBarNotification parentToCancelFinal = parentToCancel;
+ final Runnable runnable = () -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ if (intent != null) {
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (intent.isActivity()) {
+ final int userId = intent.getCreatorUserHandle().getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ // TODO(b/28935539): should allow certain activities to
+ // bypass work challenge
+ if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(),
+ notificationKey)) {
+ // Show work challenge, do not run PendingIntent and
+ // remove notification
+ return;
}
}
+ }
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending contentIntent failed: " + e);
- try {
- mBarService.onNotificationClick(notificationKey);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- if (parentToCancelFinal != null) {
- // We have to post it to the UI thread for synchronization
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Runnable removeRunnable = new Runnable() {
- @Override
- public void run() {
- performRemoveNotification(parentToCancelFinal);
- }
- };
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- }
- });
- }
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
}
- };
+ }
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- new Thread(runnable).start();
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ if (parentToCancelFinal != null) {
+ // We have to post it to the UI thread for synchronization
+ mHandler.post(() -> {
+ Runnable removeRunnable =
+ () -> performRemoveNotification(parentToCancelFinal);
+ if (isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ });
}
+ };
- if (!mNotificationPanel.isFullyCollapsed()) {
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
+ if (mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded()) {
+ mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ } else {
+ new Thread(runnable).start();
+ }
- return true;
- } else {
- return false;
- }
+ if (!mNotificationPanel.isFullyCollapsed()) {
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+ true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ } else {
+ return false;
}
}, afterKeyguardGone);
}
@@ -7149,10 +6910,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
protected Bundle getActivityOptions() {
- // Anything launched from the notification shade should always go into the
- // fullscreen stack.
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ // Anything launched from the notification shade should always go into the secondary
+ // split-screen windowing mode.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
return options.toBundle();
}
diff --git a/com/android/systemui/statusbar/phone/StatusBarIconController.java b/com/android/systemui/statusbar/phone/StatusBarIconController.java
index c2407652..bcda60eb 100644
--- a/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -14,10 +14,10 @@
package com.android.systemui.statusbar.phone;
-import android.annotation.ColorInt;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -29,11 +29,11 @@ import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.util.Utils.DisableStateTracker;
public interface StatusBarIconController {
@@ -149,6 +149,14 @@ public interface StatusBarIconController {
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
+
+ DisableStateTracker tracker =
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS);
+ mGroup.addOnAttachStateChangeListener(tracker);
+ if (mGroup.isAttachedToWindow()) {
+ // In case we miss the first onAttachedToWindow event
+ tracker.onViewAttachedToWindow(mGroup);
+ }
}
protected void onIconAdded(int index, String slot, boolean blocked,
diff --git a/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 68f8e065..1c3ee758 100644
--- a/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -33,7 +33,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -50,21 +49,17 @@ import java.util.ArrayList;
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
- private final DarkIconDispatcher mDarkIconDispatcher;
-
- private Context mContext;
- private DemoStatusIcons mDemoStatusIcons;
-
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
-
private final ArraySet<String> mIconBlacklist = new ArraySet<>();
private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
+ private Context mContext;
+ private DemoStatusIcons mDemoStatusIcons;
+
public StatusBarIconControllerImpl(Context context) {
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
Dependency.get(ConfigurationController.class).addCallback(this);
- mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mContext = context;
loadDimens();
diff --git a/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index bbce751d..09828dcd 100644
--- a/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -225,7 +225,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mShowing) {
if (mOccluded && !mDozing) {
mStatusBar.hideKeyguard();
- mStatusBar.stopWaitingForKeyguardExit();
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
diff --git a/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index c0a68373..0d21c4ef 100644
--- a/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -20,7 +20,6 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager.ActionListener;
-import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -59,13 +58,19 @@ public class AccessPointControllerImpl
private int mCurrentUser;
- public AccessPointControllerImpl(Context context, Looper bgLooper) {
+ public AccessPointControllerImpl(Context context) {
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mWifiTracker = new WifiTracker(context, this, bgLooper, false, true);
+ mWifiTracker = new WifiTracker(context, this, false, true);
mCurrentUser = ActivityManager.getCurrentUser();
}
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mWifiTracker.onDestroy();
+ }
+
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
@@ -81,7 +86,7 @@ public class AccessPointControllerImpl
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
if (mCallbacks.size() == 1) {
- mWifiTracker.startTracking();
+ mWifiTracker.onStart();
}
}
@@ -91,7 +96,7 @@ public class AccessPointControllerImpl
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
if (mCallbacks.isEmpty()) {
- mWifiTracker.stopTracking();
+ mWifiTracker.onStop();
}
}
diff --git a/com/android/systemui/statusbar/policy/CallbackHandler.java b/com/android/systemui/statusbar/policy/CallbackHandler.java
index a456786d..5159e8d0 100644
--- a/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -71,7 +71,7 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
break;
case MSG_NO_SIM_VISIBLE_CHANGED:
for (SignalCallback signalCluster : mSignalCallbacks) {
- signalCluster.setNoSims(msg.arg1 != 0);
+ signalCluster.setNoSims(msg.arg1 != 0, msg.arg2 != 0);
}
break;
case MSG_ETHERNET_CHANGED:
@@ -144,8 +144,8 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
}
@Override
- public void setNoSims(boolean show) {
- obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, 0).sendToTarget();
+ public void setNoSims(boolean show, boolean simDetected) {
+ obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
}
@Override
diff --git a/com/android/systemui/statusbar/policy/NetworkController.java b/com/android/systemui/statusbar/policy/NetworkController.java
index 2771011c..9eee906b 100644
--- a/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/com/android/systemui/statusbar/policy/NetworkController.java
@@ -52,7 +52,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {}
default void setSubs(List<SubscriptionInfo> subs) {}
- default void setNoSims(boolean show) {}
+ default void setNoSims(boolean show, boolean simDetected) {}
default void setEthernetIndicators(IconState icon) {}
diff --git a/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c217bda9..d24e51c7 100644
--- a/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -58,10 +58,8 @@ import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -116,7 +114,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
// States that don't belong to a subcontroller.
private boolean mAirplaneMode = false;
- private boolean mHasNoSims;
+ private boolean mHasNoSubs;
private Locale mLocale = null;
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
@@ -140,6 +138,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
@VisibleForTesting
ServiceState mLastServiceState;
private boolean mUserSetup;
+ private boolean mSimDetected;
/**
* Construct this controller object and register for updates.
@@ -151,7 +150,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
- new AccessPointControllerImpl(context, bgLooper),
+ new AccessPointControllerImpl(context),
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController);
@@ -363,7 +362,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
cb.setSubs(mCurrentSubscriptions);
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- cb.setNoSims(mHasNoSims);
+ cb.setNoSims(mHasNoSubs, mSimDetected);
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -498,11 +497,25 @@ public class NetworkControllerImpl extends BroadcastReceiver
@VisibleForTesting
protected void updateNoSims() {
- boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
- if (hasNoSims != mHasNoSims) {
- mHasNoSims = hasNoSims;
- mCallbackHandler.setNoSims(mHasNoSims);
+ boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
+ boolean simDetected = hasAnySim();
+ if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) {
+ mHasNoSubs = hasNoSubs;
+ mSimDetected = simDetected;
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
+ }
+ }
+
+ private boolean hasAnySim() {
+ int simCount = mPhone.getSimCount();
+ for (int i = 0; i < simCount; i++) {
+ int state = mPhone.getSimState(i);
+ if (state != TelephonyManager.SIM_STATE_ABSENT
+ && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ return true;
+ }
}
+ return false;
}
@VisibleForTesting
@@ -631,7 +644,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
private void notifyListeners() {
mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- mCallbackHandler.setNoSims(mHasNoSims);
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
/**
@@ -804,6 +817,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
} else {
mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
}
+ String ssid = args.getString("ssid");
+ if (ssid != null) {
+ mDemoWifiState.ssid = ssid;
+ }
mDemoWifiState.enabled = show;
mWifiSignalController.notifyListeners();
}
@@ -822,8 +839,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
}
String nosim = args.getString("nosim");
if (nosim != null) {
- mHasNoSims = nosim.equals("show");
- mCallbackHandler.setNoSims(mHasNoSims);
+ mHasNoSubs = nosim.equals("show");
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
String mobile = args.getString("mobile");
if (mobile != null) {
diff --git a/com/android/systemui/util/Utils.java b/com/android/systemui/util/Utils.java
index f4aebae7..eca61277 100644
--- a/com/android/systemui/util/Utils.java
+++ b/com/android/systemui/util/Utils.java
@@ -14,6 +14,11 @@
package com.android.systemui.util;
+import android.view.View;
+
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.CommandQueue;
+
import java.util.List;
import java.util.function.Consumer;
@@ -28,4 +33,52 @@ public class Utils {
c.accept(list.get(i));
}
}
+
+ /**
+ * Sets the visibility of an UI element according to the DISABLE_* flags in
+ * {@link android.app.StatusBarManager}.
+ */
+ public static class DisableStateTracker implements CommandQueue.Callbacks,
+ View.OnAttachStateChangeListener {
+ private final int mMask1;
+ private final int mMask2;
+ private View mView;
+ private boolean mDisabled;
+
+ public DisableStateTracker(int disableMask, int disable2Mask) {
+ mMask1 = disableMask;
+ mMask2 = disable2Mask;
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mView = v;
+ SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class)
+ .addCallbacks(this);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class)
+ .removeCallbacks(this);
+ mView = null;
+ }
+
+ /**
+ * Sets visibility of this {@link View} given the states passed from
+ * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}.
+ */
+ @Override
+ public void disable(int state1, int state2, boolean animate) {
+ final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
+ if (disabled == mDisabled) return;
+ mDisabled = disabled;
+ mView.setVisibility(disabled ? View.GONE : View.VISIBLE);
+ }
+
+ /** @return {@code true} if and only if this {@link View} is currently disabled */
+ public boolean isDisabled() {
+ return mDisabled;
+ }
+ }
}
diff --git a/com/android/systemui/volume/ZenModePanel.java b/com/android/systemui/volume/ZenModePanel.java
index a3aca6e1..7bb987ca 100644
--- a/com/android/systemui/volume/ZenModePanel.java
+++ b/com/android/systemui/volume/ZenModePanel.java
@@ -524,18 +524,17 @@ public class ZenModePanel extends FrameLayout {
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else if (isForever(c)) {
+
getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else {
if (isAlarm(c)) {
bindGenericCountdown();
-
bindNextAlarm(c);
getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
} else if (isCountdown(c)) {
bindNextAlarm(getTimeUntilNextAlarmCondition());
-
bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
COUNTDOWN_CONDITION_INDEX);
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
@@ -568,8 +567,8 @@ public class ZenModePanel extends FrameLayout {
tag = (ConditionTag) alarmContent.getTag();
boolean showAlarm = tag != null && tag.condition != null;
mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
- showAlarm ? View.VISIBLE : View.GONE);
- alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
+ showAlarm ? View.VISIBLE : View.INVISIBLE);
+ alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.INVISIBLE);
}
private Condition forever() {
diff --git a/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index 7c9aeded..3d5476d0 100644
--- a/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,24 +16,55 @@
package com.android.uiautomator.testrunner;
-import android.app.Instrumentation;
+import android.content.Context;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
+import android.view.inputmethod.InputMethodInfo;
-import com.android.uiautomator.core.InstrumentationUiAutomatorBridge;
+import com.android.internal.view.IInputMethodManager;
import com.android.uiautomator.core.UiDevice;
+import junit.framework.TestCase;
+
+import java.util.List;
+
/**
- * UI Automator test case that is executed on the device.
+ * UI automation test should extend this class. This class provides access
+ * to the following:
+ * {@link UiDevice} instance
+ * {@link Bundle} for command line parameters.
+ * @since API Level 16
* @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
* Android Testing Support Library.
*/
@Deprecated
-public class UiAutomatorTestCase extends InstrumentationTestCase {
+public class UiAutomatorTestCase extends TestCase {
+ private static final String DISABLE_IME = "disable_ime";
+ private static final String DUMMY_IME_PACKAGE = "com.android.testing.dummyime";
+ private UiDevice mUiDevice;
private Bundle mParams;
private IAutomationSupport mAutomationSupport;
+ private boolean mShouldDisableIme = false;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mShouldDisableIme = "true".equals(mParams.getString(DISABLE_IME));
+ if (mShouldDisableIme) {
+ setDummyIme();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mShouldDisableIme) {
+ restoreActiveIme();
+ }
+ super.tearDown();
+ }
/**
* Get current instance of {@link UiDevice}. Works similar to calling the static
@@ -41,7 +72,7 @@ public class UiAutomatorTestCase extends InstrumentationTestCase {
* @since API Level 16
*/
public UiDevice getUiDevice() {
- return UiDevice.getInstance();
+ return mUiDevice;
}
/**
@@ -54,43 +85,34 @@ public class UiAutomatorTestCase extends InstrumentationTestCase {
return mParams;
}
- void setAutomationSupport(IAutomationSupport automationSupport) {
- mAutomationSupport = automationSupport;
- }
-
/**
* Provides support for running tests to report interim status
*
* @return IAutomationSupport
* @since API Level 16
- * @deprecated Use {@link Instrumentation#sendStatus(int, Bundle)} instead
*/
public IAutomationSupport getAutomationSupport() {
- if (mAutomationSupport == null) {
- mAutomationSupport = new InstrumentationAutomationSupport(getInstrumentation());
- }
return mAutomationSupport;
}
/**
- * Initializes this test case.
- *
- * @param params Instrumentation arguments.
+ * package private
+ * @param uiDevice
*/
- void initialize(Bundle params) {
- mParams = params;
+ void setUiDevice(UiDevice uiDevice) {
+ mUiDevice = uiDevice;
+ }
- // check if this is a monkey test mode
- String monkeyVal = mParams.getString("monkey");
- if (monkeyVal != null) {
- // only if the monkey key is specified, we alter the state of monkey
- // else we should leave things as they are.
- getInstrumentation().getUiAutomation().setRunAsMonkey(Boolean.valueOf(monkeyVal));
- }
+ /**
+ * package private
+ * @param params
+ */
+ void setParams(Bundle params) {
+ mParams = params;
+ }
- UiDevice.getInstance().initialize(new InstrumentationUiAutomatorBridge(
- getInstrumentation().getContext(),
- getInstrumentation().getUiAutomation()));
+ void setAutomationSupport(IAutomationSupport automationSupport) {
+ mAutomationSupport = automationSupport;
}
/**
@@ -101,4 +123,28 @@ public class UiAutomatorTestCase extends InstrumentationTestCase {
public void sleep(long ms) {
SystemClock.sleep(ms);
}
+
+ private void setDummyIme() throws RemoteException {
+ IInputMethodManager im = IInputMethodManager.Stub.asInterface(ServiceManager
+ .getService(Context.INPUT_METHOD_SERVICE));
+ List<InputMethodInfo> infos = im.getInputMethodList();
+ String id = null;
+ for (InputMethodInfo info : infos) {
+ if (DUMMY_IME_PACKAGE.equals(info.getComponent().getPackageName())) {
+ id = info.getId();
+ }
+ }
+ if (id == null) {
+ throw new RuntimeException(String.format(
+ "Required testing fixture missing: IME package (%s)", DUMMY_IME_PACKAGE));
+ }
+ im.setInputMethod(null, id);
+ }
+
+ private void restoreActiveIme() throws RemoteException {
+ // TODO: figure out a way to restore active IME
+ // Currently retrieving active IME requires querying secure settings provider, which is hard
+ // to do without a Context; so the caveat here is that to make the post test device usable,
+ // the active IME needs to be manually switched.
+ }
}
diff --git a/com/android/webview/nullwebview/NullWebViewFactoryProvider.java b/com/android/webview/nullwebview/NullWebViewFactoryProvider.java
deleted file mode 100644
index ed12446d..00000000
--- a/com/android/webview/nullwebview/NullWebViewFactoryProvider.java
+++ /dev/null
@@ -1,85 +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.webview.nullwebview;
-
-import android.content.Context;
-import android.webkit.CookieManager;
-import android.webkit.GeolocationPermissions;
-import android.webkit.ServiceWorkerController;
-import android.webkit.TokenBindingService;
-import android.webkit.WebIconDatabase;
-import android.webkit.WebStorage;
-import android.webkit.WebView;
-import android.webkit.WebViewDatabase;
-import android.webkit.WebViewDelegate;
-import android.webkit.WebViewFactoryProvider;
-import android.webkit.WebViewProvider;
-
-public class NullWebViewFactoryProvider implements WebViewFactoryProvider {
-
- public static WebViewFactoryProvider create(WebViewDelegate delegate) {
- return new NullWebViewFactoryProvider(delegate);
- }
-
- public NullWebViewFactoryProvider(WebViewDelegate delegate) {
- }
-
- @Override
- public WebViewFactoryProvider.Statics getStatics() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public GeolocationPermissions getGeolocationPermissions() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public CookieManager getCookieManager() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public TokenBindingService getTokenBindingService() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public ServiceWorkerController getServiceWorkerController() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WebIconDatabase getWebIconDatabase() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WebStorage getWebStorage() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public WebViewDatabase getWebViewDatabase(Context context) {
- throw new UnsupportedOperationException();
- }
-}