summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt147
-rw-r--r--api/system-current.txt148
-rw-r--r--api/test-current.txt147
-rw-r--r--core/java/android/app/Activity.java81
-rw-r--r--core/java/android/app/ActivityManager.java89
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/Notification.java32
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java78
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl10
-rw-r--r--core/java/android/auditing/SecurityLog.java6
-rw-r--r--core/java/android/auditing/SecurityLogTags.logtags2
-rw-r--r--core/java/android/content/Intent.java11
-rw-r--r--core/java/android/content/pm/ActivityInfo.java11
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java4
-rw-r--r--core/java/android/content/pm/PackageParser.java3
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java3
-rw-r--r--core/java/android/net/NetworkScoreManager.java3
-rw-r--r--core/java/android/os/Environment.java89
-rw-r--r--core/java/android/os/Process.java10
-rw-r--r--core/java/android/os/UserHandle.java9
-rw-r--r--core/java/android/os/UserManager.java2
-rw-r--r--core/java/android/os/UserManagerInternal.java9
-rw-r--r--core/java/android/provider/DocumentsContract.java2
-rwxr-xr-xcore/java/android/provider/Settings.java31
-rw-r--r--core/java/android/text/BidiFormatter.java8
-rw-r--r--core/java/android/text/Html.java353
-rw-r--r--core/java/android/view/IWindowSession.aidl2
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/View.java16
-rw-r--r--core/java/android/view/WindowManagerPolicy.java12
-rw-r--r--core/java/android/webkit/WebViewProviderInfo.java34
-rw-r--r--core/java/android/widget/SearchView.java44
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java17
-rw-r--r--core/java/com/android/internal/app/LocaleStore.java26
-rw-r--r--core/java/com/android/internal/policy/DecorView.java33
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl19
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl5
-rw-r--r--core/java/com/android/internal/util/LineBreakBufferedWriter.java2
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java9
-rw-r--r--core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java113
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp10
-rw-r--r--core/jni/android/graphics/FontFamily.cpp109
-rw-r--r--core/jni/android_media_AudioFormat.h5
-rw-r--r--core/jni/android_media_AudioSystem.cpp49
-rw-r--r--core/jni/android_media_AudioTrack.cpp4
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp6
-rw-r--r--core/res/res/layout/floating_popup_container.xml4
-rw-r--r--core/res/res/layout/text_edit_suggestion_container.xml4
-rw-r--r--core/res/res/layout/text_edit_suggestion_container_material.xml4
-rw-r--r--core/res/res/values-watch/themes_device_defaults.xml1
-rw-r--r--core/res/res/values/dimens_material.xml3
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java16
-rw-r--r--graphics/java/android/graphics/FontFamily.java13
-rw-r--r--graphics/java/android/graphics/FontListParser.java92
-rw-r--r--graphics/java/android/graphics/Typeface.java39
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java35
-rw-r--r--libs/androidfw/ResourceTypes.cpp29
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp13
-rw-r--r--libs/hwui/Android.mk37
-rw-r--r--libs/hwui/BakedOpState.h14
-rw-r--r--libs/hwui/DamageAccumulator.cpp4
-rw-r--r--libs/hwui/DeferredDisplayList.h7
-rw-r--r--libs/hwui/DisplayListCanvas.h5
-rw-r--r--libs/hwui/DisplayListOp.h4
-rw-r--r--libs/hwui/FrameBuilder.cpp18
-rw-r--r--libs/hwui/GradientCache.cpp4
-rw-r--r--libs/hwui/LayerBuilder.cpp16
-rw-r--r--libs/hwui/RecordingCanvas.cpp52
-rw-r--r--libs/hwui/RecordingCanvas.h5
-rw-r--r--libs/hwui/Snapshot.h4
-rw-r--r--libs/hwui/Texture.cpp8
-rw-r--r--libs/hwui/hwui_static_deps.mk28
-rw-r--r--libs/hwui/tests/unit/LinearAllocatorTests.cpp13
-rw-r--r--libs/hwui/utils/LinearAllocator.cpp8
-rw-r--r--libs/hwui/utils/LinearAllocator.h40
-rw-r--r--location/java/android/location/GnssMeasurement.java21
-rw-r--r--location/java/android/location/GnssNavigationMessage.java23
-rw-r--r--location/java/android/location/GnssStatus.java29
-rw-r--r--location/java/android/location/GpsStatus.java22
-rw-r--r--location/java/android/location/IGnssStatusListener.aidl4
-rw-r--r--location/java/android/location/LocationManager.java5
-rw-r--r--media/java/android/media/AudioTrack.java47
-rw-r--r--media/java/android/media/MediaMuxer.java4
-rw-r--r--media/java/android/media/audiopolicy/AudioMixingRule.java36
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicyConfig.java14
-rw-r--r--media/java/android/media/tv/TvInputManager.java19
-rw-r--r--media/java/android/media/tv/TvInputService.java14
-rw-r--r--media/java/android/media/tv/TvRecordingClient.java35
-rw-r--r--media/java/android/media/tv/TvTrackInfo.java44
-rw-r--r--media/jni/android_media_MediaCrypto.cpp35
-rw-r--r--media/jni/android_media_MediaDrm.cpp35
-rw-r--r--packages/DocumentsUI/res/layout/fixed_layout.xml5
-rw-r--r--packages/DocumentsUI/res/layout/fragment_directory.xml2
-rw-r--r--packages/DocumentsUI/res/layout/fragment_roots.xml4
-rw-r--r--packages/DocumentsUI/res/layout/single_pane_layout.xml5
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java51
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/NavigationView.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java9
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsList.java63
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java42
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java102
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java50
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java34
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java2
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java19
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java48
-rw-r--r--packages/MtpDocumentsProvider/res/values/strings.xml4
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java58
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java4
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java44
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java6
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java30
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java (renamed from tools/aapt2/util/Comparators.h)26
-rw-r--r--packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java48
-rw-r--r--packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java2
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java231
-rw-r--r--packages/SystemUI/res/layout/qs_paged_tile_layout.xml30
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml11
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml35
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/colors.xml4
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml13
-rw-r--r--packages/SystemUI/res/values/strings.xml16
-rw-r--r--packages/SystemUI/res/xml/tuner_prefs.xml11
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainer.java146
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/Task.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java152
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java159
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java123
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java278
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java124
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java20
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java36
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java2
-rw-r--r--services/core/java/com/android/server/AppOpsService.java24
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java73
-rw-r--r--services/core/java/com/android/server/MountService.java2
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java2
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java18
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java38
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityStartInterceptor.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java35
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java18
-rw-r--r--services/core/java/com/android/server/am/UserController.java37
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java119
-rw-r--r--services/core/java/com/android/server/content/SyncStorageEngine.java16
-rw-r--r--services/core/java/com/android/server/firewall/IntentFirewall.java2
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--services/core/java/com/android/server/job/JobStore.java5
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java25
-rw-r--r--services/core/java/com/android/server/location/GnssStatusListenerHelper.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java88
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java10
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java2
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java13
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java13
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java89
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java963
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java153
-rw-r--r--services/core/java/com/android/server/pm/Settings.java12
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java34
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java121
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java67
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java17
-rw-r--r--services/core/java/com/android/server/wm/DragState.java179
-rw-r--r--services/core/java/com/android/server/wm/Session.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerDebugConfig.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java143
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java2
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecController.cpp2
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp959
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java228
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java4
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java67
-rw-r--r--services/net/java/android/net/ip/IpManager.java296
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java22
-rw-r--r--telecomm/java/android/telecom/Log.java35
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java1
-rw-r--r--telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl22
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java28
-rw-r--r--tests/BatteryWaster/res/layout/battery_waster.xml3
-rw-r--r--tools/aapt/Resource.cpp10
-rw-r--r--tools/aapt2/Android.mk2
-rw-r--r--tools/aapt2/Debug.cpp6
-rw-r--r--tools/aapt2/ResourceParser.cpp46
-rw-r--r--tools/aapt2/ResourceParser.h10
-rw-r--r--tools/aapt2/ResourceParser_test.cpp60
-rw-r--r--tools/aapt2/ResourceTable.cpp128
-rw-r--r--tools/aapt2/ResourceTable.h128
-rw-r--r--tools/aapt2/ResourceTable_test.cpp44
-rw-r--r--tools/aapt2/ValueVisitor.h2
-rw-r--r--tools/aapt2/compile/Compile.cpp11
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp50
-rw-r--r--tools/aapt2/flatten/TableFlattener.cpp8
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp14
-rw-r--r--tools/aapt2/link/AutoVersioner.cpp37
-rw-r--r--tools/aapt2/link/AutoVersioner_test.cpp14
-rw-r--r--tools/aapt2/link/Link.cpp30
-rw-r--r--tools/aapt2/link/Linkers.h2
-rw-r--r--tools/aapt2/link/ProductFilter.cpp118
-rw-r--r--tools/aapt2/link/ProductFilter.h49
-rw-r--r--tools/aapt2/link/ProductFilter_test.cpp136
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp2
-rw-r--r--tools/aapt2/link/TableMerger.cpp39
-rw-r--r--tools/aapt2/process/SymbolTable.cpp10
-rw-r--r--tools/aapt2/proto/TableProtoDeserializer.cpp15
-rw-r--r--tools/aapt2/proto/TableProtoSerializer.cpp17
-rw-r--r--tools/aapt2/proto/TableProtoSerializer_test.cpp23
-rw-r--r--tools/aapt2/test/Builders.h12
-rw-r--r--tools/aapt2/test/Common.h27
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp2
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java4
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java25
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java370
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java5
-rw-r--r--wifi/java/android/net/wifi/RttManager.java2
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java25
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanEventListener.java2
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanManager.java2
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanSession.java2
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanSessionListener.java2
275 files changed, 7689 insertions, 3712 deletions
diff --git a/api/current.txt b/api/current.txt
index db77d3365737..617aac02c35a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8512,6 +8512,7 @@ package android.content {
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19221,7 +19222,6 @@ package android.location {
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19232,6 +19232,7 @@ package android.location {
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19291,7 +19292,6 @@ package android.location {
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19302,6 +19302,7 @@ package android.location {
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19356,17 +19357,17 @@ package android.location {
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19412,8 +19413,8 @@ package android.location {
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22950,10 +22951,8 @@ package android.media.tv {
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23053,6 +23052,8 @@ package android.media.tv {
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -23067,6 +23068,7 @@ package android.media.tv {
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -23086,6 +23088,7 @@ package android.media.tv {
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29062,6 +29065,7 @@ package android.os {
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -29242,6 +29246,7 @@ package android.os {
public final class UserHandle implements android.os.Parcelable {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51474,7 +51479,6 @@ package java.lang.reflect {
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52150,7 +52154,6 @@ package java.net {
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52424,7 +52427,7 @@ package java.net {
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -52966,10 +52969,6 @@ package java.nio {
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -52978,68 +52977,10 @@ package java.nio.channels {
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -53056,9 +52997,7 @@ package java.nio.channels {
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53077,16 +53016,11 @@ package java.nio.channels {
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53165,40 +53099,14 @@ package java.nio.channels {
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53248,10 +53156,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -53329,10 +53233,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53368,10 +53268,6 @@ package java.nio.channels {
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -53418,15 +53314,6 @@ package java.nio.channels.spi {
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56781,13 +56668,11 @@ package java.text {
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 86419ce86261..ec422c026950 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8818,6 +8818,7 @@ package android.content {
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -20401,7 +20402,6 @@ package android.location {
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -20412,6 +20412,7 @@ package android.location {
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -20471,7 +20472,6 @@ package android.location {
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -20482,6 +20482,7 @@ package android.location {
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -20536,17 +20537,17 @@ package android.location {
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -20592,8 +20593,8 @@ package android.location {
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -24649,10 +24650,8 @@ package android.media.tv {
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -24811,6 +24810,8 @@ package android.media.tv {
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
method public void onRecordingStopped(android.net.Uri);
@@ -24852,6 +24853,7 @@ package android.media.tv {
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -24871,6 +24873,7 @@ package android.media.tv {
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -26680,6 +26683,7 @@ package android.net.wifi {
method public boolean reconnect();
method public boolean removeNetwork(int);
method public boolean saveConfiguration();
+ method public boolean setMetered(int, boolean);
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -31355,6 +31359,7 @@ package android.os {
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -31581,6 +31586,7 @@ package android.os {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public int getIdentifier();
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public deprecated boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -54562,7 +54568,6 @@ package java.lang.reflect {
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -55238,7 +55243,6 @@ package java.net {
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -55512,7 +55516,7 @@ package java.net {
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -56054,10 +56058,6 @@ package java.nio {
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -56066,68 +56066,10 @@ package java.nio.channels {
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -56144,9 +56086,7 @@ package java.nio.channels {
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -56165,16 +56105,11 @@ package java.nio.channels {
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -56253,40 +56188,14 @@ package java.nio.channels {
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -56336,10 +56245,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -56417,10 +56322,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -56456,10 +56357,6 @@ package java.nio.channels {
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -56506,15 +56403,6 @@ package java.nio.channels.spi {
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -59869,13 +59757,11 @@ package java.text {
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 675b9938dc83..d202108f5326 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8517,6 +8517,7 @@ package android.content {
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
@@ -19229,7 +19230,6 @@ package android.location {
method public double getElevationUncertaintyInDeg();
method public byte getLossOfLock();
method public byte getMultipathIndicator();
- method public byte getPrn();
method public double getPseudorangeInMeters();
method public double getPseudorangeRateCarrierInMetersPerSec();
method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
@@ -19240,6 +19240,7 @@ package android.location {
method public long getReceivedGpsTowUncertaintyInNs();
method public double getSnrInDb();
method public short getState();
+ method public short getSvid();
method public short getTimeFromLastBitInMs();
method public double getTimeOffsetInNs();
method public boolean hasAzimuthInDeg();
@@ -19299,7 +19300,6 @@ package android.location {
method public void setElevationUncertaintyInDeg(double);
method public void setLossOfLock(byte);
method public void setMultipathIndicator(byte);
- method public void setPrn(byte);
method public void setPseudorangeInMeters(double);
method public void setPseudorangeRateCarrierInMetersPerSec(double);
method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
@@ -19310,6 +19310,7 @@ package android.location {
method public void setReceivedGpsTowUncertaintyInNs(long);
method public void setSnrInDb(double);
method public void setState(short);
+ method public void setSvid(short);
method public void setTimeFromLastBitInMs(short);
method public void setTimeOffsetInNs(double);
method public void setUsedInFix(boolean);
@@ -19364,17 +19365,17 @@ package android.location {
method public int describeContents();
method public byte[] getData();
method public short getMessageId();
- method public byte getPrn();
method public short getStatus();
method public short getSubmessageId();
+ method public short getSvid();
method public byte getType();
method public void reset();
method public void set(android.location.GnssNavigationMessage);
method public void setData(byte[]);
method public void setMessageId(short);
- method public void setPrn(byte);
method public void setStatus(short);
method public void setSubmessageId(short);
+ method public void setSvid(short);
method public void setType(byte);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
@@ -19420,8 +19421,8 @@ package android.location {
method public int getConstellationType(int);
method public float getElevation(int);
method public int getNumSatellites();
- method public int getPrn(int);
method public float getSnr(int);
+ method public int getSvid(int);
method public boolean hasAlmanac(int);
method public boolean hasEphemeris(int);
method public boolean usedInFix(int);
@@ -22959,10 +22960,8 @@ package android.media.tv {
field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
- field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
- field public static final int RECORDING_ERROR_DISCONNECTED = 2; // 0x2
- field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3; // 0x3
- field public static final int RECORDING_ERROR_RESOURCE_BUSY = 4; // 0x4
+ field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; // 0x1
+ field public static final int RECORDING_ERROR_RESOURCE_BUSY = 2; // 0x2
field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
@@ -23062,6 +23061,8 @@ package android.media.tv {
public static abstract class TvRecordingClient.RecordingCallback {
ctor public TvRecordingClient.RecordingCallback();
+ method public void onConnectionFailed(java.lang.String);
+ method public void onDisconnected(java.lang.String);
method public void onError(int);
method public void onRecordingStopped(android.net.Uri);
method public void onTuned();
@@ -23076,6 +23077,7 @@ package android.media.tv {
method public final java.lang.String getId();
method public final java.lang.String getLanguage();
method public final int getType();
+ method public final byte getVideoActiveFormatDescription();
method public final float getVideoFrameRate();
method public final int getVideoHeight();
method public final float getVideoPixelAspectRatio();
@@ -23095,6 +23097,7 @@ package android.media.tv {
method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
@@ -29071,6 +29074,7 @@ package android.os {
method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
method public static final int getUidForName(java.lang.String);
method public static final boolean is64Bit();
+ method public static boolean isApplicationUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method public static final int myTid();
@@ -29252,6 +29256,7 @@ package android.os {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
method public static int getAppId(int);
+ method public static android.os.UserHandle getUserHandleForUid(int);
method public static android.os.UserHandle readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -51491,7 +51496,6 @@ package java.lang.reflect {
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -52167,7 +52171,6 @@ package java.net {
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
- method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
@@ -52441,7 +52444,7 @@ package java.net {
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
+ method protected java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -52983,10 +52986,6 @@ package java.nio {
package java.nio.channels {
- public class AcceptPendingException extends java.lang.IllegalStateException {
- ctor public AcceptPendingException();
- }
-
public class AlreadyBoundException extends java.lang.IllegalStateException {
ctor public AlreadyBoundException();
}
@@ -52995,68 +52994,10 @@ package java.nio.channels {
ctor public AlreadyConnectedException();
}
- public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
- method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- }
-
- public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
- method public abstract void close() throws java.io.IOException;
- }
-
- public abstract class AsynchronousChannelGroup {
- ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public abstract boolean isShutdown();
- method public abstract boolean isTerminated();
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void shutdown();
- method public abstract void shutdownNow() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
- }
-
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
- public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
- method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
- method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- }
-
- public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
- ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
- method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
- method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
- method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
- method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
- method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
- method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
- method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
- method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
- }
-
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -53073,9 +53014,7 @@ package java.nio.channels {
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
- method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
- method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -53094,16 +53033,11 @@ package java.nio.channels {
ctor public ClosedSelectorException();
}
- public abstract interface CompletionHandler {
- method public abstract void completed(V, A);
- method public abstract void failed(java.lang.Throwable, A);
- }
-
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -53182,40 +53116,14 @@ package java.nio.channels {
ctor public IllegalBlockingModeException();
}
- public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
- ctor public IllegalChannelGroupException();
- }
-
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
- public class InterruptedByTimeoutException extends java.io.IOException {
- ctor public InterruptedByTimeoutException();
- }
-
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
public abstract interface NetworkChannel implements java.nio.channels.Channel {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -53265,10 +53173,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ReadPendingException extends java.lang.IllegalStateException {
- ctor public ReadPendingException();
- }
-
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
@@ -53346,10 +53250,6 @@ package java.nio.channels {
method public final int validOps();
}
- public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
- ctor public ShutdownChannelGroupException();
- }
-
public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
@@ -53385,10 +53285,6 @@ package java.nio.channels {
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
- public class WritePendingException extends java.lang.IllegalStateException {
- ctor public WritePendingException();
- }
-
}
package java.nio.channels.spi {
@@ -53435,15 +53331,6 @@ package java.nio.channels.spi {
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
- public abstract class AsynchronousChannelProvider {
- ctor protected AsynchronousChannelProvider();
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
- method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
- }
-
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
@@ -56798,13 +56685,11 @@ package java.text {
method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
- method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
- method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 622012ecf2e2..ea58e292e545 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,8 @@
package android.app;
+import static java.lang.Character.MIN_VALUE;
+
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -26,20 +28,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StyleRes;
-import android.os.PersistableBundle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
-import android.util.ArrayMap;
-import android.util.SuperNotCalledException;
-import android.view.DragEvent;
-import android.view.DropPermissions;
-import android.view.Window.WindowControllerCallback;
-import android.widget.Toolbar;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.app.ToolbarActionBar;
-
import android.annotation.SystemApi;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
@@ -61,7 +49,12 @@ import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -71,6 +64,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.UserHandle;
@@ -78,16 +72,22 @@ import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SuperNotCalledException;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
+import android.view.DropPermissions;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -104,11 +104,17 @@ import android.view.ViewGroup.LayoutParams;
import android.view.ViewManager;
import android.view.ViewRootImpl;
import android.view.Window;
+import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.Toolbar;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import java.io.FileDescriptor;
@@ -119,8 +125,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import static java.lang.Character.MIN_VALUE;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -3974,14 +3978,49 @@ public class Activity extends ContextThemeWrapper
// Get the primary color and update the TaskDescription for this activity
if (theme != null) {
TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int windowBgResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackground, 0);
+ int windowBgFallbackResourceId = a.getResourceId(
+ com.android.internal.R.styleable.Window_windowBackgroundFallback, 0);
int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+ int colorBg = tryExtractColorFromDrawable(DecorView.getResizingBackgroundDrawable(this,
+ windowBgResourceId, windowBgFallbackResourceId));
a.recycle();
if (colorPrimary != 0) {
- ActivityManager.TaskDescription v = new ActivityManager.TaskDescription(null, null,
- colorPrimary);
- setTaskDescription(v);
+ ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setPrimaryColor(colorPrimary);
+ td.setBackgroundColor(colorBg);
+ setTaskDescription(td);
+ }
+ }
+ }
+
+ /**
+ * Attempts to extract the color from a given drawable.
+ *
+ * @return the extracted color or 0 if no color could be extracted.
+ */
+ private int tryExtractColorFromDrawable(Drawable drawable) {
+ if (drawable instanceof ColorDrawable) {
+ return ((ColorDrawable) drawable).getColor();
+ } else if (drawable instanceof InsetDrawable) {
+ return tryExtractColorFromDrawable(((InsetDrawable) drawable).getDrawable());
+ } else if (drawable instanceof ShapeDrawable) {
+ Paint p = ((ShapeDrawable) drawable).getPaint();
+ if (p != null) {
+ return p.getColor();
+ }
+ } else if (drawable instanceof LayerDrawable) {
+ LayerDrawable ld = (LayerDrawable) drawable;
+ int numLayers = ld.getNumberOfLayers();
+ for (int i = 0; i < numLayers; i++) {
+ int color = tryExtractColorFromDrawable(ld.getDrawable(i));
+ if (color != 0) {
+ return color;
+ }
}
}
+ return 0;
}
/**
@@ -5612,8 +5651,8 @@ public class Activity extends ContextThemeWrapper
if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size, true);
- td = new ActivityManager.TaskDescription(taskDescription.getLabel(), icon,
- taskDescription.getPrimaryColor());
+ td = new ActivityManager.TaskDescription(taskDescription);
+ td.setIcon(icon);
} else {
td = taskDescription;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1eb2fe2c89ec..dd73261917e7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -632,6 +632,17 @@ public class ActivityManager {
public static boolean useWindowFrameForBackdrop(int stackId) {
return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if a window from the specified stack with {@param stackId} are normally
+ * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
+ * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
+ * etc.
+ */
+ public static boolean normallyFullscreenWindows(int stackId) {
+ return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
+ && stackId != DOCKED_STACK_ID;
+ }
}
/**
@@ -863,8 +874,10 @@ public class ActivityManager {
public static final String ATTR_TASKDESCRIPTION_PREFIX = "task_description_";
private static final String ATTR_TASKDESCRIPTIONLABEL =
ATTR_TASKDESCRIPTION_PREFIX + "label";
- private static final String ATTR_TASKDESCRIPTIONCOLOR =
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
+ ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
private static final String ATTR_TASKDESCRIPTIONICONFILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
@@ -872,28 +885,21 @@ public class ActivityManager {
private Bitmap mIcon;
private String mIconFilename;
private int mColorPrimary;
+ private int mColorBackground;
/**
* Creates the TaskDescription to the specified values.
*
* @param label A label and description of the current state of this task.
* @param icon An icon that represents the current state of this task.
- * @param colorPrimary A color to override the theme's primary color. This color must be opaque.
+ * @param colorPrimary A color to override the theme's primary color. This color must be
+ * opaque.
*/
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
+ this(label, icon, null, colorPrimary, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
-
- mLabel = label;
- mIcon = icon;
- mColorPrimary = colorPrimary;
- }
-
- /** @hide */
- public TaskDescription(String label, int colorPrimary, String iconFilename) {
- this(label, null, colorPrimary);
- mIconFilename = iconFilename;
}
/**
@@ -903,7 +909,7 @@ public class ActivityManager {
* @param icon An icon that represents the current state of this activity.
*/
public TaskDescription(String label, Bitmap icon) {
- this(label, icon, 0);
+ this(label, icon, null, 0, 0);
}
/**
@@ -912,14 +918,24 @@ public class ActivityManager {
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0);
+ this(label, null, null, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0);
+ this(null, null, null, 0, 0);
+ }
+
+ /** @hide */
+ public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
+ int colorBackground) {
+ mLabel = label;
+ mIcon = icon;
+ mIconFilename = iconFilename;
+ mColorPrimary = colorPrimary;
+ mColorBackground = colorBackground;
}
/**
@@ -928,8 +944,9 @@ public class ActivityManager {
public TaskDescription(TaskDescription td) {
mLabel = td.mLabel;
mIcon = td.mIcon;
- mColorPrimary = td.mColorPrimary;
mIconFilename = td.mIconFilename;
+ mColorPrimary = td.mColorPrimary;
+ mColorBackground = td.mColorBackground;
}
private TaskDescription(Parcel source) {
@@ -957,6 +974,18 @@ public class ActivityManager {
}
/**
+ * Sets the background color for this task description.
+ * @hide
+ */
+ public void setBackgroundColor(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException("A TaskDescription's background color should be opaque");
+ }
+ mColorBackground = backgroundColor;
+ }
+
+ /**
* Sets the icon for this task description.
* @hide
*/
@@ -1005,8 +1034,8 @@ public class ActivityManager {
public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
if (iconFilename != null) {
try {
- return ActivityManagerNative.getDefault().
- getTaskDescriptionIcon(iconFilename, userId);
+ return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
+ userId);
} catch (RemoteException e) {
}
}
@@ -1020,13 +1049,26 @@ public class ActivityManager {
return mColorPrimary;
}
+ /**
+ * @return The background color.
+ * @hide
+ */
+ public int getBackgroundColor() {
+ return mColorBackground;
+ }
+
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel);
}
if (mColorPrimary != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(mColorPrimary));
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY,
+ Integer.toHexString(mColorPrimary));
+ }
+ if (mColorBackground != 0) {
+ out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND,
+ Integer.toHexString(mColorBackground));
}
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename);
@@ -1037,8 +1079,10 @@ public class ActivityManager {
public void restoreFromXml(String attrName, String attrValue) {
if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
setLabel(attrValue);
- } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_PRIMARY.equals(attrName)) {
setPrimaryColor((int) Long.parseLong(attrValue, 16));
+ } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) {
+ setBackgroundColor((int) Long.parseLong(attrValue, 16));
} else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) {
setIconFilename(attrValue);
}
@@ -1064,6 +1108,7 @@ public class ActivityManager {
mIcon.writeToParcel(dest, 0);
}
dest.writeInt(mColorPrimary);
+ dest.writeInt(mColorBackground);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1076,6 +1121,7 @@ public class ActivityManager {
mLabel = source.readInt() > 0 ? source.readString() : null;
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
mColorPrimary = source.readInt();
+ mColorBackground = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1092,7 +1138,8 @@ public class ActivityManager {
@Override
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
- " colorPrimary: " + mColorPrimary;
+ " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
+ " colorBackground: " + mColorBackground;
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0d35cf04b4a6..3c8dfcea7282 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -56,7 +56,7 @@ interface INotificationManager
void setImportance(String pkg, int uid, in Notification.Topic topic, int importance);
int getImportance(String pkg, int uid, in Notification.Topic topic);
int getTopicImportance(String pkg, String topicId);
- boolean doesAppUseTopics(String pkg, int uid);
+ boolean doesUserUseTopics(String pkg, int uid);
boolean hasBannedTopics(String pkg, int uid);
// TODO: Remove this when callers have been migrated to the equivalent
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 55c635360433..35b7c39338fc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3358,13 +3358,9 @@ public class Notification implements Parcelable
return mN.bigContentView;
} else if (mStyle != null) {
result = mStyle.makeBigContentView();
- } else if (mActions.size() == 0) {
- return null;
- }
- if (result == null) {
- result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
- } else {
hideLine1Text(result);
+ } else if (mActions.size() != 0) {
+ result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
adaptNotificationHeaderForBigContentView(result);
return result;
@@ -3384,11 +3380,15 @@ public class Notification implements Parcelable
}
private void hideLine1Text(RemoteViews result) {
- result.setViewVisibility(R.id.text_line_1, View.GONE);
+ if (result != null) {
+ result.setViewVisibility(R.id.text_line_1, View.GONE);
+ }
}
private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
- result.setBoolean(R.id.notification_header, "setExpanded", true);
+ if (result != null) {
+ result.setBoolean(R.id.notification_header, "setExpanded", true);
+ }
}
/**
@@ -4326,6 +4326,15 @@ public class Notification implements Parcelable
return makeMediaBigContentView();
}
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeHeadsUpContentView() {
+ RemoteViews expanded = makeMediaBigContentView();
+ return expanded != null ? expanded : makeMediaContentView();
+ }
+
/** @hide */
@Override
public void addExtras(Bundle extras) {
@@ -4407,6 +4416,13 @@ public class Notification implements Parcelable
private RemoteViews makeMediaBigContentView() {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+ // Dont add an expanded view if there is no more content to be revealed
+ int actionsInCompact = mActionsToShowInCompact == null
+ ? 0
+ : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+ if (mBuilder.mN.mLargeIcon == null && actionCount <= actionsInCompact) {
+ return null;
+ }
RemoteViews big = mBuilder.applyStandardTemplate(
R.layout.notification_template_material_big_media,
false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 83f9357d2909..f53170a77220 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1826,9 +1826,23 @@ public class DevicePolicyManager {
* this method; if it has not, a security exception will be thrown.
*/
public int getCurrentFailedPasswordAttempts() {
+ return getCurrentFailedPasswordAttempts(myUserId());
+ }
+
+ /**
+ * Retrieve the number of times the given user has failed at entering a
+ * password since that last successful password entry.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call this method; if it has
+ * not and it is not the system uid, a security exception will be thrown.
+ *
+ * @hide
+ */
+ public int getCurrentFailedPasswordAttempts(int userHandle) {
if (mService != null) {
try {
- return mService.getCurrentFailedPasswordAttempts(myUserId(), mParentInstance);
+ return mService.getCurrentFailedPasswordAttempts(userHandle, mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -3023,13 +3037,39 @@ public class DevicePolicyManager {
}
/**
+ * @hide
+ */
+ public void reportFailedFingerprintAttempt(int userHandle) {
+ if (mService != null) {
+ try {
+ mService.reportFailedFingerprintAttempt(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ if (mService != null) {
+ try {
+ mService.reportSuccessfulFingerprintAttempt(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
* Should be called when keyguard has been dismissed.
* @hide
*/
- public void reportKeyguardDismissed() {
+ public void reportKeyguardDismissed(int userHandle) {
if (mService != null) {
try {
- mService.reportKeyguardDismissed();
+ mService.reportKeyguardDismissed(userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -3040,10 +3080,10 @@ public class DevicePolicyManager {
* Should be called when keyguard view has been shown to the user.
* @hide
*/
- public void reportKeyguardSecured() {
+ public void reportKeyguardSecured(int userHandle) {
if (mService != null) {
try {
- mService.reportKeyguardSecured();
+ mService.reportKeyguardSecured(userHandle);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -5730,4 +5770,32 @@ public class DevicePolicyManager {
return false;
}
}
+
+ /**
+ * @hide
+ * Returns whether the uninstall for {@code packageName} for the current user is in queue
+ * to be started
+ * @param packageName the package to check for
+ * @return whether the uninstall intent for {@code packageName} is pending
+ */
+ public boolean isUninstallInQueue(String packageName) {
+ try {
+ return mService.isUninstallInQueue(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ * @param packageName the package containing active DAs to be uninstalled
+ */
+ public void uninstallPackageWithActiveAdmins(String packageName) {
+ try {
+ mService.uninstallPackageWithActiveAdmins(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c6a53443b51c..bd6818264448 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -116,9 +116,10 @@ interface IDevicePolicyManager {
int numbers, int symbols, int nonletter, int userHandle);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
-
- void reportKeyguardDismissed();
- void reportKeyguardSecured();
+ void reportFailedFingerprintAttempt(int userHandle);
+ void reportSuccessfulFingerprintAttempt(int userHandle);
+ void reportKeyguardDismissed(int userHandle);
+ void reportKeyguardSecured(int userHandle);
boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
@@ -293,4 +294,7 @@ interface IDevicePolicyManager {
boolean getDeviceLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveDeviceLogs(in ComponentName admin);
ParceledListSlice retrievePreviousDeviceLogs(in ComponentName admin);
+
+ boolean isUninstallInQueue(String packageName);
+ void uninstallPackageWithActiveAdmins(String packageName);
}
diff --git a/core/java/android/auditing/SecurityLog.java b/core/java/android/auditing/SecurityLog.java
index 8d8d2f59c677..f1703d644bb8 100644
--- a/core/java/android/auditing/SecurityLog.java
+++ b/core/java/android/auditing/SecurityLog.java
@@ -77,8 +77,10 @@ public class SecurityLog {
SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
/**
* Indicate that there has been an authentication attempt to dismiss the keyguard. The log entry
- * contains the attempt result (integer, 1 for successful, 0 for unsuccessful), accessible via
- * {@link SecurityEvent#getData()}}
+ * contains the following information about the attempt in order, accessible via
+ * {@link SecurityEvent#getData()}}: attempt result (integer, 1 for successful, 0 for
+ * unsuccessful), strength of auth method (integer, 1 if strong auth method was used,
+ * 0 otherwise)
*/
public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
diff --git a/core/java/android/auditing/SecurityLogTags.logtags b/core/java/android/auditing/SecurityLogTags.logtags
index cf858940058b..ccc37995972c 100644
--- a/core/java/android/auditing/SecurityLogTags.logtags
+++ b/core/java/android/auditing/SecurityLogTags.logtags
@@ -8,5 +8,5 @@ option java_package android.auditing
210004 security_adb_sync_send (path|3)
210005 security_app_process_start (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
210006 security_keyguard_dismissed
-210007 security_keyguard_dismiss_auth_attempt (success|1)
+210007 security_keyguard_dismiss_auth_attempt (success|1),(method_strength|1)
210008 security_keyguard_secured
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f6f856ae12a..b476a25515f5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3028,6 +3028,17 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.MANAGED_PROFILE_REMOVED";
/**
+ * Broadcast sent to the primary user when the credential-encrypted private storage for
+ * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
+ * specifies the UserHandle of the profile that was unlocked. Only applications (for example
+ * Launchers) that need to display merged content across both primary and managed profiles
+ * need to worry about this broadcast. This is only sent to registered receivers,
+ * not manifest receivers.
+ */
+ public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
+ "android.intent.action.MANAGED_PROFILE_UNLOCKED";
+
+ /**
* Broadcast sent to the primary user when an associated managed profile's availability has
* changed. This includes when the user toggles the profile's quiet mode. Carries an extra
* {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 43a0cc77d492..f58b16a7204f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -178,6 +178,11 @@ public class ActivityInfo extends ComponentInfo
*/
public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
/**
+ * Activity is does not support resizing, but we are forcing it to be resizeable.
+ * @hide
+ */
+ public static final int RESIZE_MODE_FORCE_RESIZEABLE = 4;
+ /**
* Value indicating if the resizing mode the activity supports.
* See {@link android.R.attr#resizeableActivity}.
* @hide
@@ -786,7 +791,9 @@ public class ActivityInfo extends ComponentInfo
/** @hide */
public static boolean isResizeableMode(int mode) {
- return mode == RESIZE_MODE_RESIZEABLE || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+ return mode == RESIZE_MODE_RESIZEABLE
+ || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
+ || mode == RESIZE_MODE_FORCE_RESIZEABLE;
}
/** @hide */
@@ -800,6 +807,8 @@ public class ActivityInfo extends ComponentInfo
return "RESIZE_MODE_RESIZEABLE";
case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
+ case RESIZE_MODE_FORCE_RESIZEABLE:
+ return "RESIZE_MODE_FORCE_RESIZEABLE";
default:
return "unknown=" + mode;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c79dae5dd913..90824820d0eb 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1038,10 +1038,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
deviceEncryptedDataDir = Environment
- .getDataUserDeviceEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getDataUserDePackageDirectory(volumeUuid, userId, packageName)
.getAbsolutePath();
credentialEncryptedDataDir = Environment
- .getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
+ .getDataUserCePackageDirectory(volumeUuid, userId, packageName)
.getAbsolutePath();
if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5dddebd3cae7..5ae8d4cf0ce4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -80,6 +80,7 @@ import libcore.io.IoUtils;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -3448,7 +3449,7 @@ public class PackageParser {
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
} else if (a.info.screenOrientation == SCREEN_ORIENTATION_UNSPECIFIED
&& (a.info.flags & FLAG_IMMERSIVE) == 0) {
- a.info.resizeMode = RESIZE_MODE_CROP_WINDOWS;
+ a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 766868da9193..8724a96ee30a 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -847,6 +847,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* to make forward progress from the partial results and avoid waiting for the completed
* result.</p>
*
+ * <p>For a particular request, {@link #onCaptureProgressed} may happen before or after
+ * {@link #onCaptureStarted}.</p>
+ *
* <p>Each request will generate at least {@code 1} partial results, and at most
* {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
*
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3f36d65e577c..b6fe68af51ac 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -105,7 +105,8 @@ public class NetworkScoreManager {
/**
* Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
* perform initialization once selected as the active scorer, or clean up unneeded resources
- * if another scorer has been selected. Note that it is unnecessary to clear existing scores as
+ * if another scorer has been selected. This is an explicit broadcast only sent to the
+ * previous scorer and new scorer. Note that it is unnecessary to clear existing scores as
* this is handled by the system.
*
* <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e841dfe2b2a3..59bf2938cfce 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -176,35 +176,37 @@ public class Environment {
return DIR_VENDOR_ROOT;
}
- /** {@hide} */
- @Deprecated
- public static File getSystemSecureDirectory() {
- return getDataSystemDirectory();
- }
-
- /** {@hide} */
- @Deprecated
- public static File getSecureDataDirectory() {
- return getDataDirectory();
- }
-
/**
- * Return the system directory for a user. This is for use by system services to store
- * files relating to the user. This directory will be automatically deleted when the user
- * is removed.
+ * Return the system directory for a user. This is for use by system
+ * services to store files relating to the user. This directory will be
+ * automatically deleted when the user is removed.
*
+ * @deprecated This directory is valid and still exists, but callers should
+ * <em>strongly</em> consider switching to
+ * {@link #getDataSystemCeDirectory(int)} which is protected
+ * with user credentials or
+ * {@link #getDataSystemDeDirectory(int)} which supports fast
+ * user wipe.
* @hide
*/
+ @Deprecated
public static File getUserSystemDirectory(int userId) {
return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
}
/**
- * Returns the config directory for a user. This is for use by system services to store files
- * relating to the user which should be readable by any app running as that user.
+ * Returns the config directory for a user. This is for use by system
+ * services to store files relating to the user which should be readable by
+ * any app running as that user.
*
+ * @deprecated This directory is valid and still exists, but callers should
+ * <em>strongly</em> consider switching to
+ * {@link #getDataMiscCeDirectory(int)} which is protected with
+ * user credentials or {@link #getDataMiscDeDirectory(int)}
+ * which supports fast user wipe.
* @hide
*/
+ @Deprecated
public static File getUserConfigDirectory(int userId) {
return new File(new File(new File(
getDataDirectory(), "misc"), "user"), Integer.toString(userId));
@@ -232,77 +234,72 @@ public class Environment {
}
/** {@hide} */
- public static File getDataSystemCredentialEncryptedDirectory() {
- return new File(getDataDirectory(), "system_ce");
+ public static File getDataSystemCeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
}
/** {@hide} */
- public static File getDataSystemCredentialEncryptedDirectory(int userId) {
- return new File(getDataSystemCredentialEncryptedDirectory(), String.valueOf(userId));
+ public static File getDataSystemDeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
}
/** {@hide} */
- public static File getDataAppDirectory(String volumeUuid) {
- return new File(getDataDirectory(volumeUuid), "app");
+ public static File getDataMiscDirectory() {
+ return new File(getDataDirectory(), "misc");
}
/** {@hide} */
- public static File getDataAppEphemeralDirectory(String volumeUuid) {
- return new File(getDataDirectory(volumeUuid), "app-ephemeral");
+ public static File getDataMiscCeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "misc_ce", String.valueOf(userId));
}
/** {@hide} */
- @Deprecated
- public static File getDataUserDirectory(String volumeUuid) {
- return getDataUserCredentialEncryptedDirectory(volumeUuid);
+ public static File getDataMiscDeDirectory(int userId) {
+ return buildPath(getDataDirectory(), "misc_de", String.valueOf(userId));
}
/** {@hide} */
- @Deprecated
- public static File getDataUserDirectory(String volumeUuid, int userId) {
- return getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
+ public static File getDataAppDirectory(String volumeUuid) {
+ return new File(getDataDirectory(volumeUuid), "app");
}
/** {@hide} */
- @Deprecated
- public static File getDataUserPackageDirectory(String volumeUuid, int userId,
- String packageName) {
- return getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName);
+ public static File getDataAppEphemeralDirectory(String volumeUuid) {
+ return new File(getDataDirectory(volumeUuid), "app-ephemeral");
}
/** {@hide} */
- public static File getDataUserCredentialEncryptedDirectory(String volumeUuid) {
+ public static File getDataUserCeDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user");
}
/** {@hide} */
- public static File getDataUserCredentialEncryptedDirectory(String volumeUuid, int userId) {
- return new File(getDataUserCredentialEncryptedDirectory(volumeUuid),
- String.valueOf(userId));
+ public static File getDataUserCeDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserCeDirectory(volumeUuid), String.valueOf(userId));
}
/** {@hide} */
- public static File getDataUserCredentialEncryptedPackageDirectory(String volumeUuid, int userId,
+ public static File getDataUserCePackageDirectory(String volumeUuid, int userId,
String packageName) {
// TODO: keep consistent with installd
- return new File(getDataUserCredentialEncryptedDirectory(volumeUuid, userId), packageName);
+ return new File(getDataUserCeDirectory(volumeUuid, userId), packageName);
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedDirectory(String volumeUuid) {
+ public static File getDataUserDeDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user_de");
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedDirectory(String volumeUuid, int userId) {
- return new File(getDataUserDeviceEncryptedDirectory(volumeUuid), String.valueOf(userId));
+ public static File getDataUserDeDirectory(String volumeUuid, int userId) {
+ return new File(getDataUserDeDirectory(volumeUuid), String.valueOf(userId));
}
/** {@hide} */
- public static File getDataUserDeviceEncryptedPackageDirectory(String volumeUuid, int userId,
+ public static File getDataUserDePackageDirectory(String volumeUuid, int userId,
String packageName) {
// TODO: keep consistent with installd
- return new File(getDataUserDeviceEncryptedDirectory(volumeUuid, userId), packageName);
+ return new File(getDataUserDeDirectory(volumeUuid, userId), packageName);
}
/**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b51d2dfb8694..9984755d316f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -823,6 +823,16 @@ public class Process {
}
/**
+ * Returns whether the given uid belongs to an application.
+ * @param uid A kernel uid.
+ * @return Whether the uid corresponds to an application sandbox running in
+ * a specific user.
+ */
+ public static boolean isApplicationUid(int uid) {
+ return UserHandle.isApp(uid);
+ }
+
+ /**
* Returns whether the current process is in an isolated sandbox.
* @hide
*/
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 24666fe71724..b3f44536214b 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -130,6 +130,15 @@ public final class UserHandle implements Parcelable {
}
/**
+ * Returns the user for a given uid.
+ * @param uid A uid for an application running in a particular user.
+ * @return A {@link UserHandle} for that user.
+ */
+ public static UserHandle getUserHandleForUid(int uid) {
+ return of(getUserId(uid));
+ }
+
+ /**
* Returns the user id for a given uid.
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dc0e249d72ec..69d564fc610b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1141,6 +1141,8 @@ public class UserManager {
UserInfo user = null;
try {
user = mService.createUser(name, flags);
+ // TODO: Keep this in sync with
+ // UserManagerService.LocalService.createUserEvenWhenDisallowed
if (user != null && !user.isAdmin()) {
mService.setUserRestriction(DISALLOW_SMS, true, user.id);
mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 58a026904373..d2ece8bba3ad 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -17,6 +17,7 @@ package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.UserInfo;
import android.graphics.Bitmap;
/**
@@ -106,4 +107,12 @@ public abstract class UserManagerInternal {
* non-ephemeral users left.
*/
public abstract void removeAllUsers();
+
+ /**
+ * Same as UserManager.createUser(), but bypasses the check for DISALLOW_ADD_USER.
+ *
+ * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
+ * createAndManageUser is called by the device owner.
+ */
+ public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags);
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8468040b1bad..2ca758935feb 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1201,7 +1201,7 @@ public final class DocumentsContract {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
sourceDocumentUri.getAuthority());
try {
- return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+ return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
targetParentDocumentUri);
} catch (Exception e) {
Log.w(TAG, "Failed to move document", e);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7830142d4e4b..3169bf4f0e64 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4660,6 +4660,14 @@ public final class Settings {
"lock_screen_allow_private_notifications";
/**
+ * When set by a user, allows notification remote input atop a securely locked screen
+ * without having to unlock
+ * @hide
+ */
+ public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT =
+ "lock_screen_allow_remote_input";
+
+ /**
* Set by the system to track if the user needs to see the call to action for
* the lockscreen notification policy.
* @hide
@@ -5139,14 +5147,6 @@ public final class Settings {
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
/**
- * Whether text-to-speech higher speech rate is enabled.
- * 0 = disabled.
- * 1 = enabled.
- * @hide
- */
- public static final String TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED =
- "tts_default_higher_speech_rate_enabled";
- /**
* Default text-to-speech language.
*
* @deprecated this setting is no longer in use, as of the Ice Cream
@@ -5907,13 +5907,6 @@ public final class Settings {
"camera_double_tap_power_gesture_disabled";
/**
- * Name of the package used as WebView provider (if unset the provider is instead determined
- * by the system).
- * @hide
- */
- public static final String WEBVIEW_PROVIDER = "webview_provider";
-
- /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5958,7 +5951,6 @@ public final class Settings {
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
- TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
TTS_DEFAULT_LANG,
@@ -6940,6 +6932,13 @@ public final class Settings {
public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
"webview_data_reduction_proxy_key";
+ /**
+ * Name of the package used as WebView provider (if unset the provider is instead determined
+ * by the system).
+ * @hide
+ */
+ public static final String WEBVIEW_PROVIDER = "webview_provider";
+
/**
* Whether Wifi display is enabled/disabled
* 0=disabled. 1=enabled.
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index 675803c7b7bd..707c0fcb75ee 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.Nullable;
import android.view.View;
import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -390,14 +391,17 @@ public final class BidiFormatter {
* @return Input string after applying the above processing. {@code null} if {@code str} is
* {@code null}.
*/
- public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+ public @Nullable String unicodeWrap(@Nullable String str, TextDirectionHeuristic heuristic,
+ boolean isolate) {
+ if (str == null) return null;
return unicodeWrap((CharSequence) str, heuristic, isolate).toString();
}
/**
* @hide
*/
- public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristic heuristic, boolean isolate) {
+ public @Nullable CharSequence unicodeWrap(@Nullable CharSequence str,
+ TextDirectionHeuristic heuristic, boolean isolate) {
if (str == null) return null;
final boolean isRtl = heuristic.isRtl(str, 0, str.length());
SpannableStringBuilder result = new SpannableStringBuilder();
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 82f69efaac45..409994d7cd84 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -54,6 +54,9 @@ import android.text.style.UnderlineSpan;
import java.io.IOException;
import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -356,24 +359,48 @@ public class Html {
}
}
- private static String getTextStyles(Spanned text, int start, int end) {
- final StringBuilder style = new StringBuilder(" style=\"margin-top:0; margin-bottom:0;");
+ private static String getTextStyles(Spanned text, int start, int end,
+ boolean forceNoVerticalMargin, boolean includeTextAlign) {
+ String margin = null;
+ String textAlign = null;
- final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
- final int len = alignmentSpans.length;
- if (len > 0) {
- final Layout.Alignment alignment = alignmentSpans[len - 1].getAlignment();
- if (alignment == Layout.Alignment.ALIGN_NORMAL) {
- style.append(" text-align:start;");
- } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
- style.append(" text-align:center;");
- } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
- style.append(" text-align:end;");
+ if (forceNoVerticalMargin) {
+ margin = "margin-top:0; margin-bottom:0;";
+ }
+ if (includeTextAlign) {
+ final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
+
+ // Only use the last AlignmentSpan with flag SPAN_PARAGRAPH
+ for (int i = alignmentSpans.length - 1; i >= 0; i--) {
+ AlignmentSpan s = alignmentSpans[i];
+ if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) {
+ final Layout.Alignment alignment = s.getAlignment();
+ if (alignment == Layout.Alignment.ALIGN_NORMAL) {
+ textAlign = "text-align:start;";
+ } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
+ textAlign = "text-align:center;";
+ } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
+ textAlign = "text-align:end;";
+ }
+ break;
+ }
}
}
- style.append("\"");
- return style.toString();
+ if (margin == null && textAlign == null) {
+ return "";
+ }
+
+ final StringBuilder style = new StringBuilder(" style=\"");
+ if (margin != null && textAlign != null) {
+ style.append(margin).append(" ").append(textAlign);
+ } else if (margin != null) {
+ style.append(margin);
+ } else if (textAlign != null) {
+ style.append(textAlign);
+ }
+
+ return style.append("\"").toString();
}
private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end,
@@ -395,46 +422,55 @@ public class Html {
next = end;
}
- boolean isListItem = false;
- ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
- for (ParagraphStyle paragraphStyle : paragraphStyles) {
- final int spanFlags = text.getSpanFlags(paragraphStyle);
- if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
- && paragraphStyle instanceof BulletSpan) {
- isListItem = true;
- break;
+ if (next == i) {
+ if (isInList) {
+ // Current paragraph is no longer a list item; close the previously opened list
+ isInList = false;
+ out.append("</ul>\n");
+ }
+ out.append("<br>\n");
+ } else {
+ boolean isListItem = false;
+ ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
+ for (ParagraphStyle paragraphStyle : paragraphStyles) {
+ final int spanFlags = text.getSpanFlags(paragraphStyle);
+ if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
+ && paragraphStyle instanceof BulletSpan) {
+ isListItem = true;
+ break;
+ }
}
- }
- if (isListItem && !isInList) {
- // Current paragraph is the first item in a list
- isInList = true;
- out.append("<ul>\n");
- }
+ if (isListItem && !isInList) {
+ // Current paragraph is the first item in a list
+ isInList = true;
+ out.append("<ul")
+ .append(getTextStyles(text, i, next, true, false))
+ .append(">\n");
+ }
- if (isInList && !isListItem) {
- // Current paragraph is no longer a list item; close the previously opened list
- isInList = false;
- out.append("</ul>\n");
- }
+ if (isInList && !isListItem) {
+ // Current paragraph is no longer a list item; close the previously opened list
+ isInList = false;
+ out.append("</ul>\n");
+ }
- String tagType = isListItem ? "li" : "p";
- out.append("<").append(tagType).append(getTextDirection(text, start, next))
- .append(getTextStyles(text, start, next)).append(">");
+ String tagType = isListItem ? "li" : "p";
+ out.append("<").append(tagType)
+ .append(getTextDirection(text, i, next))
+ .append(getTextStyles(text, i, next, !isListItem, true))
+ .append(">");
- if (next - i == 0) {
- out.append("<br>");
- } else {
withinParagraph(out, text, i, next);
- }
- out.append("</");
- out.append(tagType);
- out.append(">\n");
+ out.append("</");
+ out.append(tagType);
+ out.append(">\n");
- if (next == end && isInList) {
- isInList = false;
- out.append("</ul>\n");
+ if (next == end && isInList) {
+ isInList = false;
+ out.append("</ul>\n");
+ }
}
next++;
@@ -654,6 +690,25 @@ class HtmlToSpannedConverter implements ContentHandler {
private int mFlags;
private static Pattern sTextAlignPattern;
+ private static Pattern sForegroundColorPattern;
+ private static Pattern sBackgroundColorPattern;
+ private static Pattern sTextDecorationPattern;
+
+ /**
+ * Name-value mapping of HTML/CSS colors which have different values in {@link Color}.
+ */
+ private static final Map<String, Integer> sColorMap;
+
+ static {
+ sColorMap = new HashMap<>();
+ sColorMap.put("darkgray", 0xFFA9A9A9);
+ sColorMap.put("gray", 0xFF808080);
+ sColorMap.put("lightgray", 0xFFD3D3D3);
+ sColorMap.put("darkgrey", 0xFFA9A9A9);
+ sColorMap.put("grey", 0xFF808080);
+ sColorMap.put("lightgrey", 0xFFD3D3D3);
+ sColorMap.put("green", 0xFF008000);
+ }
private static Pattern getTextAlignPattern() {
if (sTextAlignPattern == null) {
@@ -662,6 +717,30 @@ class HtmlToSpannedConverter implements ContentHandler {
return sTextAlignPattern;
}
+ private static Pattern getForegroundColorPattern() {
+ if (sForegroundColorPattern == null) {
+ sForegroundColorPattern = Pattern.compile(
+ "(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b");
+ }
+ return sForegroundColorPattern;
+ }
+
+ private static Pattern getBackgroundColorPattern() {
+ if (sBackgroundColorPattern == null) {
+ sBackgroundColorPattern = Pattern.compile(
+ "(?:\\s+|\\A)background(?:-color)?\\s*:\\s*(\\S*)\\b");
+ }
+ return sBackgroundColorPattern;
+ }
+
+ private static Pattern getTextDecorationPattern() {
+ if (sTextDecorationPattern == null) {
+ sTextDecorationPattern = Pattern.compile(
+ "(?:\\s+|\\A)text-decoration\\s*:\\s*(\\S*)\\b");
+ }
+ return sTextDecorationPattern;
+ }
+
public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter,
Html.TagHandler tagHandler, Parser parser, int flags) {
mSource = source;
@@ -715,8 +794,15 @@ class HtmlToSpannedConverter implements ContentHandler {
// so we can safely emit the linebreaks when we handle the close tag.
} else if (tag.equalsIgnoreCase("p")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph());
+ startCssStyle(mSpannableStringBuilder, attributes);
+ } else if (tag.equalsIgnoreCase("ul")) {
+ startBlockElement(mSpannableStringBuilder, attributes, getMarginList());
+ } else if (tag.equalsIgnoreCase("li")) {
+ startLi(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("div")) {
startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv());
+ } else if (tag.equalsIgnoreCase("span")) {
+ startCssStyle(mSpannableStringBuilder, attributes);
} else if (tag.equalsIgnoreCase("strong")) {
start(mSpannableStringBuilder, new Bold());
} else if (tag.equalsIgnoreCase("b")) {
@@ -768,9 +854,16 @@ class HtmlToSpannedConverter implements ContentHandler {
if (tag.equalsIgnoreCase("br")) {
handleBr(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("p")) {
+ endCssStyle(mSpannableStringBuilder);
endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("ul")) {
+ endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("li")) {
+ endLi(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("div")) {
endBlockElement(mSpannableStringBuilder);
+ } else if (tag.equalsIgnoreCase("span")) {
+ endCssStyle(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("strong")) {
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
} else if (tag.equalsIgnoreCase("b")) {
@@ -824,6 +917,14 @@ class HtmlToSpannedConverter implements ContentHandler {
return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING);
}
+ private int getMarginListItem() {
+ return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM);
+ }
+
+ private int getMarginList() {
+ return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST);
+ }
+
private int getMarginDiv() {
return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV);
}
@@ -866,7 +967,7 @@ class HtmlToSpannedConverter implements ContentHandler {
final int len = text.length();
if (margin > 0) {
appendNewlines(text, margin);
- text.setSpan(new Newline(margin), len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Newline(margin));
}
String style = attributes.getValue("", "style");
@@ -875,14 +976,11 @@ class HtmlToSpannedConverter implements ContentHandler {
if (m.find()) {
String alignment = m.group(1);
if (alignment.equalsIgnoreCase("start")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_NORMAL),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL));
} else if (alignment.equalsIgnoreCase("center")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_CENTER),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_CENTER));
} else if (alignment.equalsIgnoreCase("end")) {
- text.setSpan(new Alignment(Layout.Alignment.ALIGN_OPPOSITE),
- len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE));
}
}
}
@@ -905,6 +1003,18 @@ class HtmlToSpannedConverter implements ContentHandler {
text.append('\n');
}
+ private void startLi(Editable text, Attributes attributes) {
+ startBlockElement(text, attributes, getMarginListItem());
+ start(text, new Bullet());
+ startCssStyle(text, attributes);
+ }
+
+ private static void endLi(Editable text) {
+ endCssStyle(text);
+ endBlockElement(text);
+ end(text, Bullet.class, new BulletSpan());
+ }
+
private void startBlockquote(Editable text, Attributes attributes) {
startBlockElement(text, attributes, getMarginBlockquote());
start(text, new Blockquote());
@@ -959,7 +1069,7 @@ class HtmlToSpannedConverter implements ContentHandler {
private static void start(Editable text, Object mark) {
int len = text.length();
- text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
+ text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
private static void end(Editable text, Class kind, Object repl) {
@@ -970,6 +1080,52 @@ class HtmlToSpannedConverter implements ContentHandler {
}
}
+ private void startCssStyle(Editable text, Attributes attributes) {
+ String style = attributes.getValue("", "style");
+ if (style != null) {
+ Matcher m = getForegroundColorPattern().matcher(style);
+ if (m.find()) {
+ int c = getHtmlColor(m.group(1));
+ if (c != -1) {
+ start(text, new Foreground(c | 0xFF000000));
+ }
+ }
+
+ m = getBackgroundColorPattern().matcher(style);
+ if (m.find()) {
+ int c = getHtmlColor(m.group(1));
+ if (c != -1) {
+ start(text, new Background(c | 0xFF000000));
+ }
+ }
+
+ m = getTextDecorationPattern().matcher(style);
+ if (m.find()) {
+ String textDecoration = m.group(1);
+ if (textDecoration.equalsIgnoreCase("line-through")) {
+ start(text, new Strikethrough());
+ }
+ }
+ }
+ }
+
+ private static void endCssStyle(Editable text) {
+ Strikethrough s = getLast(text, Strikethrough.class);
+ if (s != null) {
+ setSpanFromMark(text, s, new StrikethroughSpan());
+ }
+
+ Background b = getLast(text, Background.class);
+ if (b != null) {
+ setSpanFromMark(text, b, new BackgroundColorSpan(b.mBackgroundColor));
+ }
+
+ Foreground f = getLast(text, Foreground.class);
+ if (f != null) {
+ setSpanFromMark(text, f, new ForegroundColorSpan(f.mForegroundColor));
+ }
+ }
+
private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) {
String src = attributes.getValue("", "src");
Drawable d = null;
@@ -991,58 +1147,41 @@ class HtmlToSpannedConverter implements ContentHandler {
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
- private static void startFont(Editable text, Attributes attributes) {
+ private void startFont(Editable text, Attributes attributes) {
String color = attributes.getValue("", "color");
String face = attributes.getValue("", "face");
- int len = text.length();
- text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK);
+ if (!TextUtils.isEmpty(color)) {
+ int c = getHtmlColor(color);
+ if (c != -1) {
+ start(text, new Foreground(c | 0xFF000000));
+ }
+ }
+
+ if (!TextUtils.isEmpty(face)) {
+ start(text, new Font(face));
+ }
}
private static void endFont(Editable text) {
- int len = text.length();
- Font f = getLast(text, Font.class);
- int where = text.getSpanStart(f);
- text.removeSpan(f);
-
- if (where != len) {
- if (!TextUtils.isEmpty(f.mColor)) {
- if (f.mColor.startsWith("@")) {
- Resources res = Resources.getSystem();
- String name = f.mColor.substring(1);
- int colorRes = res.getIdentifier(name, "color", "android");
- if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes, null);
- text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- int c = Color.getHtmlColor(f.mColor);
- if (c != -1) {
- text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
+ Font font = getLast(text, Font.class);
+ if (font != null) {
+ setSpanFromMark(text, font, new TypefaceSpan(font.mFace));
+ }
- if (f.mFace != null) {
- text.setSpan(new TypefaceSpan(f.mFace), where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ Foreground foreground = getLast(text, Foreground.class);
+ if (foreground != null) {
+ setSpanFromMark(text, foreground,
+ new ForegroundColorSpan(foreground.mForegroundColor));
}
}
private static void startA(Editable text, Attributes attributes) {
String href = attributes.getValue("", "href");
-
- int len = text.length();
- text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
+ start(text, new Href(href));
}
private static void endA(Editable text) {
- int len = text.length();
Href h = getLast(text, Href.class);
if (h != null) {
if (h.mHref != null) {
@@ -1051,6 +1190,17 @@ class HtmlToSpannedConverter implements ContentHandler {
}
}
+ private int getHtmlColor(String color) {
+ if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS)
+ == Html.FROM_HTML_OPTION_USE_CSS_COLORS) {
+ Integer i = sColorMap.get(color.toLowerCase(Locale.US));
+ if (i != null) {
+ return i;
+ }
+ }
+ return Color.getHtmlColor(color);
+ }
+
public void setDocumentLocator(Locator locator) {
}
@@ -1132,13 +1282,12 @@ class HtmlToSpannedConverter implements ContentHandler {
private static class Blockquote { }
private static class Super { }
private static class Sub { }
+ private static class Bullet { }
private static class Font {
- public String mColor;
public String mFace;
- public Font(String color, String face) {
- mColor = color;
+ public Font(String face) {
mFace = face;
}
}
@@ -1151,6 +1300,22 @@ class HtmlToSpannedConverter implements ContentHandler {
}
}
+ private static class Foreground {
+ private int mForegroundColor;
+
+ public Foreground(int foregroundColor) {
+ mForegroundColor = foregroundColor;
+ }
+ }
+
+ private static class Background {
+ private int mBackgroundColor;
+
+ public Background(int backgroundColor) {
+ mBackgroundColor = backgroundColor;
+ }
+ }
+
private static class Heading {
private int mLevel;
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1703ed1810e2..6a2cc802e1f1 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -260,4 +260,6 @@ interface IWindowSession {
* Returns true if the move started successfully; false otherwise.
*/
boolean startMovingTask(IWindow window, float startX, float startY);
+
+ void updatePointerIcon(IWindow window);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a2960515da08..152dd66ec7be 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -224,7 +224,7 @@ public class SurfaceView extends View {
mParent.requestTransparentRegion(this);
mSession = getWindowSession();
mLayout.token = getWindowToken();
- mLayout.setTitle("SurfaceView");
+ mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2612ab2f8eaf..5ae426c24733 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6874,6 +6874,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param info The info whose drawing order should be populated
*/
private void populateAccessibilityNodeInfoDrawingOrderInParent(AccessibilityNodeInfo info) {
+ /*
+ * If the view's bounds haven't been set yet, layout has not completed. In that situation,
+ * drawing order may not be well-defined, and some Views with custom drawing order may
+ * not be initialized sufficiently to respond properly getChildDrawingOrder.
+ */
+ if ((mPrivateFlags & PFLAG_HAS_BOUNDS) == 0) {
+ info.setDrawingOrder(0);
+ return;
+ }
int drawingOrderInParent = 1;
// Iterate up the hierarchy if parents are not important for a11y
View viewAtDrawingLevel = this;
@@ -21572,6 +21581,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public void setPointerIcon(PointerIcon pointerIcon) {
mPointerIcon = pointerIcon;
+ if (mAttachInfo == null) {
+ return;
+ }
+ try {
+ mAttachInfo.mSession.updatePointerIcon(mAttachInfo.mWindow);
+ } catch (RemoteException e) {
+ }
}
/**
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 947906bf4403..609c471dfa18 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -18,6 +18,7 @@ package android.view;
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -388,6 +389,12 @@ public interface WindowManagerPolicy {
* Check whether the window is currently dimming.
*/
public boolean isDimming();
+
+ /**
+ * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+ * not attached to any stack.
+ */
+ int getStackId();
}
/**
@@ -465,6 +472,11 @@ public interface WindowManagerPolicy {
* @return The content insets of the docked divider window.
*/
int getDockedDividerInsetsLw();
+
+ /**
+ * Retrieves the {@param outBounds} from the stack with id {@param stackId}.
+ */
+ void getStackBounds(int stackId, Rect outBounds);
}
public interface PointerEventListener {
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 3f50fe2e376c..94e8b70ce38c 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -97,22 +97,12 @@ public class WebViewProviderInfo implements Parcelable {
*/
public boolean isEnabled() {
try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- int enabled_state = pm.getApplicationEnabledSetting(packageName);
- switch (enabled_state) {
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return true;
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
- return applicationInfo.enabled;
- default:
- return false;
- }
+ // Explicitly fetch up-to-date package info here since the enabled-state of the package
+ // might have changed since we last fetched its package info.
+ updatePackageInfo();
+ return getPackageInfo().applicationInfo.enabled;
} catch (WebViewPackageNotFoundException e) {
return false;
- } catch (IllegalArgumentException e) {
- // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
- return false;
}
}
@@ -124,14 +114,18 @@ public class WebViewProviderInfo implements Parcelable {
return availableByDefault;
}
+ private void updatePackageInfo() {
+ try {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new WebViewPackageNotFoundException(e);
+ }
+ }
+
public PackageInfo getPackageInfo() {
if (packageInfo == null) {
- try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
- } catch (PackageManager.NameNotFoundException e) {
- throw new WebViewPackageNotFoundException(e);
- }
+ updatePackageInfo();
}
return packageInfo;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 45fc6c3d4c7a..3796df78ff7f 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -36,6 +36,8 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.InputType;
@@ -1332,6 +1334,48 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
setIconified(false);
}
+ static class SavedState extends BaseSavedState {
+ boolean isIconified;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ isIconified = (Boolean) source.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeValue(isIconified);
+ }
+
+ @Override
+ public String toString() {
+ return "SearchView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " isIconified=" + isIconified + "}";
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.isIconified = isIconified();
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ updateViewsVisibility(ss.isIconified);
+ requestLayout();
+ }
+
@Override
public CharSequence getAccessibilityClassName() {
return SearchView.class.getName();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index b0fb93b89052..8c3c2b5c8022 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -45,6 +45,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
@@ -128,6 +129,7 @@ public class ChooserActivity extends ResolverActivity {
if (mServiceConnections.isEmpty()) {
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
sendVoiceChoicesIfNeeded();
+ mChooserListAdapter.setShowServiceTargets(true);
}
break;
@@ -137,6 +139,7 @@ public class ChooserActivity extends ResolverActivity {
}
unbindRemainingServices();
sendVoiceChoicesIfNeeded();
+ mChooserListAdapter.setShowServiceTargets(true);
break;
default:
@@ -232,7 +235,7 @@ public class ChooserActivity extends ResolverActivity {
// the case where we don't have access to credential encrypted storage we just won't
// have our pinned target info.
final File prefsFile = new File(new File(
- Environment.getDataUserCredentialEncryptedPackageDirectory(null,
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
context.getUserId(), context.getPackageName()),
"shared_prefs"),
PINNED_SHARED_PREFS_NAME + ".xml");
@@ -765,6 +768,7 @@ public class ChooserActivity extends ResolverActivity {
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
private final List<TargetInfo> mCallerTargets = new ArrayList<>();
+ private boolean mShowServiceTargets;
private float mLateFee = 1.f;
@@ -865,6 +869,9 @@ public class ChooserActivity extends ResolverActivity {
}
public int getServiceTargetCount() {
+ if (!mShowServiceTargets) {
+ return 0;
+ }
return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
}
@@ -954,6 +961,14 @@ public class ChooserActivity extends ResolverActivity {
notifyDataSetChanged();
}
+ /**
+ * Set to true to reveal all service targets at once.
+ */
+ public void setShowServiceTargets(boolean show) {
+ mShowServiceTargets = show;
+ notifyDataSetChanged();
+ }
+
private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) {
final float newScore = chooserTargetInfo.getModifiedScore();
for (int i = 0, N = mServiceTargets.size(); i < N; i++) {
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 210adce4d0af..465c4d833aa1 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -104,6 +104,9 @@ public class LocaleStore {
}
private boolean isSuggestionOfType(int suggestionMask) {
+ if (!mIsTranslated) { // Never suggest an untranslated locale
+ return false;
+ }
return (mSuggestionFlags & suggestionMask) == suggestionMask;
}
@@ -207,6 +210,27 @@ public class LocaleStore {
}
}
+ /*
+ * Show all the languages supported for a country in the suggested list.
+ * This is also handy for devices without SIM (tablets).
+ */
+ private static void addSuggestedLocalesForRegion(Locale locale) {
+ if (locale == null) {
+ return;
+ }
+ final String country = locale.getCountry();
+ if (country.isEmpty()) {
+ return;
+ }
+
+ for (LocaleInfo li : sLocaleCache.values()) {
+ if (country.equals(li.getLocale().getCountry())) {
+ // We don't need to differentiate between manual and SIM suggestions
+ li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
+ }
+ }
+ }
+
public static void fillCache(Context context) {
if (sFullyInitialized) {
return;
@@ -256,6 +280,8 @@ public class LocaleStore {
li.setTranslated(localizedLocales.contains(li.getLangScriptKey()));
}
+ addSuggestedLocalesForRegion(Locale.getDefault());
+
sFullyInitialized = true;
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 88af920df2d4..af3f7ec73bc3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -86,6 +86,7 @@ import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
@@ -1002,13 +1003,25 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ // If we didn't request fullscreen layout, but we still got it because of the
+ // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
+ boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
+ && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
+ && mForceWindowDrawsStatusBarBackground
+ && mLastTopInset != 0;
+
+ int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
if (mContentRoot != null
&& mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
- if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
+ if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
+ || lp.bottomMargin != consumedBottom) {
+ lp.topMargin = consumedTop;
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
mContentRoot.setLayoutParams(lp);
@@ -1022,7 +1035,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
if (insets != null) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetTop() - consumedTop,
insets.getSystemWindowInsetRight() - consumedRight,
insets.getSystemWindowInsetBottom() - consumedBottom);
}
@@ -1718,8 +1731,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private void loadBackgroundDrawablesIfNeeded() {
if (mResizingBackgroundDrawable == null) {
- mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+ mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+ if (mResizingBackgroundDrawable == null) {
+ // We shouldn't really get here as the background fallback should be always
+ // available since it is defaulted by the system.
+ Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
+ }
}
if (mCaptionBackgroundDrawable == null) {
mCaptionBackgroundDrawable = getContext().getDrawable(
@@ -1817,9 +1835,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
* Returns the color used to fill areas the app has not rendered content to yet when the
* user is resizing the window of an activity in multi-window mode.
*/
- private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
- final Context context = getContext();
-
+ public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
+ int backgroundFallbackRes) {
if (backgroundRes != 0) {
final Drawable drawable = context.getDrawable(backgroundRes);
if (drawable != null) {
@@ -1833,10 +1850,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return fallbackDrawable;
}
}
-
- // We shouldn't really get here as the background fallback should be always available since
- // it is defaulted by the system.
- Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
return null;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6e374e28103f..08d4fba8b1b8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -31,7 +32,23 @@ oneway interface IStatusBar
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
- void setSystemUiVisibility(int vis, int mask);
+
+ /**
+ * Notifies the status bar of a System UI visibility flag change.
+ *
+ * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
+ * separately in fullscreenStackVis and dockedStackVis
+ * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
+ * which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param dockedStackVis the flags that only apply in the region of the docked stack, which is
+ * currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ * @param mask which flags to change
+ * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
+ * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+ */
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ in Rect fullscreenBounds, in Rect dockedBounds);
+
void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bec18ec48ec2..8acf5d3070d3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -37,7 +38,6 @@ interface IStatusBarService
void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
- void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
@@ -47,7 +47,8 @@ interface IStatusBarService
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
out List<StatusBarIcon> iconList,
- out int[] switches, out List<IBinder> binders);
+ out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
+ out Rect dockedStackBounds);
void onPanelRevealed(boolean clearNotificationEffects, int numItems);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
diff --git a/core/java/com/android/internal/util/LineBreakBufferedWriter.java b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
index f831e7a84d2f..552a93f6666a 100644
--- a/core/java/com/android/internal/util/LineBreakBufferedWriter.java
+++ b/core/java/com/android/internal/util/LineBreakBufferedWriter.java
@@ -96,7 +96,7 @@ public class LineBreakBufferedWriter extends PrintWriter {
@Override
public void write(int c) {
- if (bufferIndex < bufferSize) {
+ if (bufferIndex < buffer.length) {
buffer[bufferIndex] = (char)c;
bufferIndex++;
if ((char)c == '\n') {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e239852673e8..cbc735fc065c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -283,6 +283,15 @@ public class LockPatternUtils {
getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
}
+ public int getCurrentFailedPasswordAttempts(int userId) {
+ return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
+ }
+
+ public int getMaximumFailedPasswordsForWipe(int userId) {
+ return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
+ null /* componentName */, userId);
+ }
+
/**
* Check to see if a pattern matches the saved pattern.
* If pattern matches, return an opaque attestation that the challenge
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index 0449340ea21a..1b4049212255 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -16,10 +16,6 @@
package com.android.server.backup;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.backup.BackupDataInputStream;
@@ -28,14 +24,21 @@ import android.app.backup.BackupHelper;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
+import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
@@ -73,6 +76,8 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper {
private static final String KEY_AUTHORITY_NAME = "name";
private static final String KEY_AUTHORITY_SYNC_STATE = "syncState";
private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled";
+ private static final String STASH_FILE = Environment.getDataDirectory()
+ + "/backup/unadded_account_syncsettings.json";
private Context mContext;
private AccountManager mAccountManager;
@@ -256,41 +261,99 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper {
}
try {
- HashSet<Account> currentAccounts = getAccountsHashSet();
- for (int i = 0; i < accountJSONArray.length(); i++) {
- JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
- String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
- String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
-
- Account account = new Account(accountName, accountType);
-
- // Check if the account already exists. Accounts that don't exist on the device
- // yet won't be restored.
- if (currentAccounts.contains(account)) {
- restoreExistingAccountSyncSettingsFromJSON(accountJSON);
- } else {
- // TODO:
- // Stash the data to a file that the SyncManager can read from to restore
- // settings at a later date.
- }
- }
+ restoreFromJsonArray(accountJSONArray);
} finally {
// Set the master sync preference to the value from the backup set.
ContentResolver.setMasterSyncAutomatically(masterSyncEnabled);
}
-
Log.i(TAG, "Restore successful.");
} catch (IOException | JSONException e) {
Log.e(TAG, "Couldn't restore account sync settings\n" + e);
}
}
+ private void restoreFromJsonArray(JSONArray accountJSONArray)
+ throws JSONException {
+ HashSet<Account> currentAccounts = getAccounts();
+ JSONArray unaddedAccountsJSONArray = new JSONArray();
+ for (int i = 0; i < accountJSONArray.length(); i++) {
+ JSONObject accountJSON = (JSONObject) accountJSONArray.get(i);
+ String accountName = accountJSON.getString(KEY_ACCOUNT_NAME);
+ String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE);
+
+ Account account = null;
+ try {
+ account = new Account(accountName, accountType);
+ } catch (IllegalArgumentException iae) {
+ continue;
+ }
+
+ // Check if the account already exists. Accounts that don't exist on the device
+ // yet won't be restored.
+ if (currentAccounts.contains(account)) {
+ if (DEBUG) Log.i(TAG, "Restoring Sync Settings for" + accountName);
+ restoreExistingAccountSyncSettingsFromJSON(accountJSON);
+ } else {
+ unaddedAccountsJSONArray.put(accountJSON);
+ }
+ }
+
+ if (unaddedAccountsJSONArray.length() > 0) {
+ try (FileOutputStream fOutput = new FileOutputStream(STASH_FILE)) {
+ String jsonString = unaddedAccountsJSONArray.toString();
+ DataOutputStream out = new DataOutputStream(fOutput);
+ out.writeUTF(jsonString);
+ } catch (IOException ioe) {
+ // Error in writing to stash file
+ Log.e(TAG, "unable to write the sync settings to the stash file", ioe);
+ }
+ } else {
+ File stashFile = new File(STASH_FILE);
+ if (stashFile.exists()) stashFile.delete();
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ private void accountAddedInternal() {
+ String jsonString;
+
+ try (FileInputStream fIn = new FileInputStream(new File(STASH_FILE))) {
+ DataInputStream in = new DataInputStream(fIn);
+ jsonString = in.readUTF();
+ } catch (FileNotFoundException fnfe) {
+ // This is expected to happen when there is no accounts info stashed
+ if (DEBUG) Log.d(TAG, "unable to find the stash file", fnfe);
+ return;
+ } catch (IOException ioe) {
+ if (DEBUG) Log.d(TAG, "could not read sync settings from stash file", ioe);
+ return;
+ }
+
+ try {
+ JSONArray unaddedAccountsJSONArray = new JSONArray(jsonString);
+ restoreFromJsonArray(unaddedAccountsJSONArray);
+ } catch (JSONException jse) {
+ // Malformed jsonString
+ Log.e(TAG, "there was an error with the stashed sync settings", jse);
+ }
+ }
+
+ /**
+ * Restore SyncSettings for all existing accounts from a stashed backup-set
+ */
+ public static void accountAdded(Context context) {
+ AccountSyncSettingsBackupHelper helper = new AccountSyncSettingsBackupHelper(context);
+ helper.accountAddedInternal();
+ }
+
/**
* Helper method - fetch accounts and return them as a HashSet.
*
* @return Accounts in a HashSet.
*/
- private HashSet<Account> getAccountsHashSet() {
+ private HashSet<Account> getAccounts() {
Account[] accounts = mAccountManager.getAccounts();
HashSet<Account> accountHashSet = new HashSet<Account>();
for (Account account : accounts) {
@@ -359,4 +422,4 @@ public class AccountSyncSettingsBackupHelper implements BackupHelper {
public void writeNewStateDescription(ParcelFileDescriptor newState) {
}
-}
+} \ No newline at end of file
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 92f781268732..8b248b07a07d 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -505,12 +505,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding
bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
}
-// Need to buffer enough input to be able to rewind as much as might be read by a decoder
-// trying to determine the stream's format. Currently the most is 64, read by
-// SkWebpCodec.
-// FIXME: Get this number from SkCodec
-#define BYTES_TO_BUFFER 64
-
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
jobject padding, jobject options) {
@@ -519,7 +513,7 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA
if (stream.get()) {
std::unique_ptr<SkStreamRewindable> bufferedStream(
- SkFrontBufferedStream::Create(stream.release(), BYTES_TO_BUFFER));
+ SkFrontBufferedStream::Create(stream.release(), SkCodec::MinBufferedBytesNeeded()));
SkASSERT(bufferedStream.get() != NULL);
bitmap = doDecode(env, bufferedStream.release(), padding, options);
}
@@ -565,7 +559,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi
// ensures that SkImageDecoder::Factory never rewinds beyond the
// current position of the file descriptor.
std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream.release(),
- BYTES_TO_BUFFER));
+ SkCodec::MinBufferedBytesNeeded()));
return doDecode(env, stream.release(), padding, bitmapFactoryOptions);
}
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 7c8dbe8ecf23..2e974a3ecaa3 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -20,11 +20,13 @@
#include <core_jni_helpers.h>
#include "SkData.h"
+#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
#include "GraphicsJNI.h"
#include <ScopedPrimitiveArray.h>
#include <ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/AssetManager.h>
#include "Utils.h"
@@ -33,6 +35,8 @@
#include <minikin/FontFamily.h>
#include "MinikinSkia.h"
+#include <memory>
+
namespace android {
static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
@@ -69,13 +73,89 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr,
return addSkTypeface(fontFamily, face);
}
+static struct {
+ jmethodID mGet;
+ jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+ jfieldID mTag;
+ jfieldID mStyleValue;
+} gAxisClassInfo;
+
+static void release_global_ref(const void* /*data*/, void* context) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ bool needToAttach = (env == NULL);
+ if (needToAttach) {
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_4;
+ args.name = "release_font_data";
+ args.group = NULL;
+ jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+ if (result != JNI_OK) {
+ ALOGE("failed to attach to thread to release global ref.");
+ return;
+ }
+ }
+
+ jobject obj = reinterpret_cast<jobject>(context);
+ env->DeleteGlobalRef(obj);
+
+ if (needToAttach) {
+ AndroidRuntime::getJavaVM()->DetachCurrentThread();
+ }
+}
+
static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
- jstring path, jint ttcIndex, jint weight, jboolean isItalic) {
- NPE_CHECK_RETURN_ZERO(env, path);
- ScopedUtfChars str(env, path);
- SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
+ jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
+ NPE_CHECK_RETURN_ZERO(env, font);
+
+ // Declare axis native type.
+ std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
+ int skiaAxesLength = 0;
+ if (listOfAxis) {
+ jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize);
+
+ skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
+ skiaAxesLength = listSize;
+ for (jint i = 0; i < listSize; ++i) {
+ jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i);
+ if (!axisObject) {
+ skiaAxes[i].fTag = 0;
+ skiaAxes[i].fStyleValue = 0;
+ continue;
+ }
+
+ jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag);
+ jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue);
+ skiaAxes[i].fTag = tag;
+ skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
+ }
+ }
+
+ void* fontPtr = env->GetDirectBufferAddress(font);
+ if (fontPtr == NULL) {
+ ALOGE("addFont failed to create font, buffer invalid");
+ return false;
+ }
+ jlong fontSize = env->GetDirectBufferCapacity(font);
+ if (fontSize < 0) {
+ ALOGE("addFont failed to create font, buffer size invalid");
+ return false;
+ }
+ jobject fontRef = MakeGlobalRefOrDie(env, font);
+ SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+ release_global_ref, reinterpret_cast<void*>(fontRef)));
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+ SkFontMgr::FontParameters params;
+ params.setCollectionIndex(ttcIndex);
+ params.setAxes(skiaAxes.get(), skiaAxesLength);
+
+ SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+ SkTypeface* face = fm->createFromStream(fontData.release(), params);
if (face == NULL) {
- ALOGE("addFont failed to create font %s", str.c_str());
+ ALOGE("addFont failed to create font, invalid request");
return false;
}
FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
@@ -129,15 +209,26 @@ static const JNINativeMethod gFontFamilyMethods[] = {
{ "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
- { "nAddFontWeightStyle", "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle },
+ { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
+ (void*)FontFamily_addFontWeightStyle },
{ "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
- (void*)FontFamily_addFontFromAsset },
+ (void*)FontFamily_addFontFromAsset },
};
int register_android_graphics_FontFamily(JNIEnv* env)
{
- return RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
- NELEM(gFontFamilyMethods));
+ int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
+ NELEM(gFontFamilyMethods));
+
+ jclass listClass = FindClassOrDie(env, "java/util/List");
+ gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+ gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
+
+ jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis");
+ gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I");
+ gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F");
+
+ return err;
}
}
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index bb13c35ced1f..651330446382 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -31,6 +31,7 @@
#define ENCODING_AAC_LC 10
#define ENCODING_AAC_HE_V1 11
#define ENCODING_AAC_HE_V2 12
+#define ENCODING_IEC61937 13
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -64,6 +65,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_AAC_HE_V1;
case ENCODING_AAC_HE_V2:
return AUDIO_FORMAT_AAC_HE_V2;
+ case ENCODING_IEC61937:
+ return AUDIO_FORMAT_IEC61937;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -103,6 +106,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
return ENCODING_AAC_HE_V1;
case AUDIO_FORMAT_AAC_HE_V2:
return ENCODING_AAC_HE_V2;
+ case AUDIO_FORMAT_IEC61937:
+ return ENCODING_IEC61937;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8e8f6c371204..80f8a64122e3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -128,11 +128,12 @@ static struct {
// other fields unused by JNI
} gAudioMixingRuleFields;
-static jclass gAttributeMatchCriterionClass;
+static jclass gAudioMixMatchCriterionClass;
static struct {
jfieldID mAttr;
+ jfieldID mIntProp;
jfieldID mRule;
-} gAttributeMatchCriterionFields;
+} gAudioMixMatchCriterionFields;
static jclass gAudioAttributesClass;
static struct {
@@ -1563,22 +1564,32 @@ static jint convertAudioMixToNative(JNIEnv *env,
}
for (jint i = 0; i < numCriteria; i++) {
- AttributeMatchCriterion nCriterion;
+ AudioMixMatchCriterion nCriterion;
jobject jCriterion = env->GetObjectArrayElement(jCriteria, i);
- nCriterion.mRule = env->GetIntField(jCriterion, gAttributeMatchCriterionFields.mRule);
+ nCriterion.mRule = env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mRule);
- jobject jAttributes = env->GetObjectField(jCriterion, gAttributeMatchCriterionFields.mAttr);
- if (nCriterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE ||
- nCriterion.mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) {
- nCriterion.mAttr.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mUsage);
- } else {
- nCriterion.mAttr.mSource = (audio_source_t)env->GetIntField(jAttributes,
- gAudioAttributesFields.mSource);
+ const uint32_t match_rule = nCriterion.mRule & ~RULE_EXCLUSION_MASK;
+ switch (match_rule) {
+ case RULE_MATCH_UID:
+ nCriterion.mValue.mUid = env->GetIntField(jCriterion,
+ gAudioMixMatchCriterionFields.mIntProp);
+ break;
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
+ jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
+ if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) {
+ nCriterion.mValue.mUsage = (audio_usage_t)env->GetIntField(jAttributes,
+ gAudioAttributesFields.mUsage);
+ } else {
+ nCriterion.mValue.mSource = (audio_source_t)env->GetIntField(jAttributes,
+ gAudioAttributesFields.mSource);
+ }
+ env->DeleteLocalRef(jAttributes);
+ }
+ break;
}
- env->DeleteLocalRef(jAttributes);
nAudioMix->mCriteria.add(nCriterion);
env->DeleteLocalRef(jCriterion);
@@ -1833,12 +1844,14 @@ int register_android_media_AudioSystem(JNIEnv *env)
gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria",
"Ljava/util/ArrayList;");
- jclass attributeMatchCriterionClass =
- FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AttributeMatchCriterion");
- gAttributeMatchCriterionClass = MakeGlobalRefOrDie(env, attributeMatchCriterionClass);
- gAttributeMatchCriterionFields.mAttr = GetFieldIDOrDie(env, attributeMatchCriterionClass, "mAttr",
+ jclass audioMixMatchCriterionClass =
+ FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
+ gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
+ gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr",
"Landroid/media/AudioAttributes;");
- gAttributeMatchCriterionFields.mRule = GetFieldIDOrDie(env, attributeMatchCriterionClass, "mRule",
+ gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp",
+ "I");
+ gAudioMixMatchCriterionFields.mRule = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mRule",
"I");
jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 61f185e10f4c..1ab95042a186 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -246,7 +246,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
// compute the frame count
size_t frameCount;
- if (audio_is_linear_pcm(format)) {
+ if (audio_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
} else {
@@ -1008,7 +1008,7 @@ static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thi
return -1;
}
const audio_format_t format = audioFormatToNative(audioFormat);
- if (audio_is_linear_pcm(format)) {
+ if (audio_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
return frameCount * channelCount * bytesPerSample;
} else {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index dd0e45636ee7..ac7700786ea0 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -322,7 +322,11 @@ private:
void NotifyHandler::handleMessage(const Message& message) {
JNIEnv* env = getenv(mVm);
- jobject target = env->NewLocalRef(mObserver->getObserverReference());
+ ObserverProxy* observer = mObserver.get();
+ LOG_ALWAYS_FATAL_IF(observer == nullptr, "received message with no observer configured");
+ LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "received message with no data to report");
+
+ jobject target = env->NewLocalRef(observer->getObserverReference());
if (target != nullptr) {
jlongArray javaBuffer = get_metrics_buffer(env, target);
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index dd161e38486e..ca0373773577 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -19,8 +19,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="0dp"
- android:layout_margin="20dp"
- android:elevation="2dp"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
android:focusable="true"
android:focusableInTouchMode="true"
android:background="?attr/floatingToolbarPopupBackgroundDrawable"/>
diff --git a/core/res/res/layout/text_edit_suggestion_container.xml b/core/res/res/layout/text_edit_suggestion_container.xml
index 17e93d0a9eae..b2589da8ea5b 100644
--- a/core/res/res/layout/text_edit_suggestion_container.xml
+++ b/core/res/res/layout/text_edit_suggestion_container.xml
@@ -22,8 +22,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:elevation="2dp"
- android:layout_margin="20dp"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
android:background="@drawable/text_edit_suggestions_window"
android:dropDownSelector="@drawable/list_selector_background"
android:divider="@null">
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 78268036c827..20a80489239d 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -24,8 +24,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
- android:elevation="2dp"
- android:layout_margin="20dp"
+ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
+ android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
android:orientation="vertical"
android:divider="?android:attr/listDivider"
android:showDividers="middle">
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 63df5beff76d..61753b1f0211 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -25,6 +25,7 @@
<style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
<style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 96a81d138457..2fe4f6652a87 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -82,6 +82,9 @@
<dimen name="text_size_medium_material">18sp</dimen>
<dimen name="text_size_small_material">14sp</dimen>
+ <dimen name="text_edit_floating_toolbar_elevation">2dp</dimen>
+ <dimen name="text_edit_floating_toolbar_margin">20dp</dimen>
+
<dimen name="floating_window_z">16dp</dimen>
<dimen name="floating_window_margin_left">16dp</dimen>
<dimen name="floating_window_margin_top">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4e10d39e0005..daa8202fa3e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4204,11 +4204,16 @@
<string name="new_sms_notification_content">Open SMS app to view</string>
<!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_title">Some functions might not be available</string>
+ <string name="user_encrypted_title">Some functionality may be limited</string>
<!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_message">Touch to continue</string>
+ <string name="user_encrypted_message">Tap to unlock</string>
<!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
- <string name="user_encrypted_detail">User profile locked</string>
+ <string name="user_encrypted_detail">User data locked</string>
+
+ <!-- Notification detail shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_detail">Work profile locked</string>
+ <!-- Notification message shown when work profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+ <string name="profile_encrypted_message">Tap to unlock work profile</string>
<!-- Title of notification shown after a MTP device is connected to Android. -->
<string name="usb_mtp_launch_notification_title">Connected to <xliff:g id="product_name">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f75f023b8c31..8df6c2e923ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2522,6 +2522,8 @@
<java-symbol type="string" name="user_encrypted_title" />
<java-symbol type="string" name="user_encrypted_message" />
<java-symbol type="string" name="user_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_detail" />
+ <java-symbol type="string" name="profile_encrypted_message" />
<java-symbol type="drawable" name="ic_user_secure" />
<java-symbol type="string" name="usb_mtp_launch_notification_title" />
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index 49ae10401983..4845c4ef28f8 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -180,6 +180,22 @@ public class LineBreakBufferedWriterTest extends TestCase {
assertOutput("aaaaaaaaaabbbbbc\nd", "ddddddddd");
}
+ public void testMoreThenInitialCapacitySimpleWrites() {
+ // This check is different from testMoreThanBufferSizeChar. The initial capacity is lower
+ // than the maximum buffer size here.
+ final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 1024, 3);
+
+ for(int i = 0; i < 10; i++) {
+ lw.print('$');
+ }
+ for(int i = 0; i < 10; i++) {
+ lw.print('%');
+ }
+ lw.flush();
+
+ assertOutput("$$$$$$$$$$%%%%%%%%%%");
+ }
+
private void assertOutput(String... golden) {
List<String> goldList = createTestGolden(golden);
assertEquals(goldList, mWriter.getStrings());
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 6309ed36eb51..f741e3cb5f8d 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -18,6 +18,9 @@ package android.graphics;
import android.content.res.AssetManager;
+import java.nio.ByteBuffer;
+import java.util.List;
+
/**
* A family of typefaces with different styles.
*
@@ -62,8 +65,9 @@ public class FontFamily {
return nAddFont(mNativePtr, path, ttcIndex);
}
- public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) {
- return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style);
+ public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
+ int weight, boolean style) {
+ return nAddFontWeightStyle(mNativePtr, font, ttcIndex, axes, weight, style);
}
public boolean addFontFromAsset(AssetManager mgr, String path) {
@@ -73,8 +77,9 @@ public class FontFamily {
private static native long nCreateFamily(String lang, int variant);
private static native void nUnrefFamily(long nativePtr);
private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
- private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
- int ttcIndex, int weight, boolean isItalic);
+ private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
+ int ttcIndex, List<FontListParser.Axis> listOfAxis,
+ int weight, boolean isItalic);
private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
String path);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 774f6b8922e0..8f7c6a62a4a3 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Pattern;
/**
* Parser for font config files.
@@ -42,15 +43,26 @@ public class FontListParser {
public List<Alias> aliases;
}
+ public static class Axis {
+ Axis(int tag, float styleValue) {
+ this.tag = tag;
+ this.styleValue = styleValue;
+ }
+ public final int tag;
+ public final float styleValue;
+ }
+
public static class Font {
- Font(String fontName, int ttcIndex, int weight, boolean isItalic) {
+ Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
this.fontName = fontName;
this.ttcIndex = ttcIndex;
+ this.axes = axes;
this.weight = weight;
this.isItalic = isItalic;
}
public String fontName;
public int ttcIndex;
+ public final List<Axis> axes;
public int weight;
public boolean isItalic;
}
@@ -93,9 +105,10 @@ public class FontListParser {
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
- if (parser.getName().equals("family")) {
+ String tag = parser.getName();
+ if (tag.equals("family")) {
config.families.add(readFamily(parser));
- } else if (parser.getName().equals("alias")) {
+ } else if (tag.equals("alias")) {
config.aliases.add(readAlias(parser));
} else {
skip(parser);
@@ -114,14 +127,7 @@ public class FontListParser {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("font")) {
- String ttcIndexStr = parser.getAttributeValue(null, "index");
- int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr);
- String weightStr = parser.getAttributeValue(null, "weight");
- int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
- boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
- String filename = parser.nextText();
- String fullFilename = "/system/fonts/" + filename;
- fonts.add(new Font(fullFilename, ttcIndex, weight, isItalic));
+ fonts.add(readFont(parser));
} else {
skip(parser);
}
@@ -129,6 +135,70 @@ public class FontListParser {
return new Family(name, fonts, lang, variant);
}
+ /** Matches leading and trailing XML whitespace. */
+ private static final Pattern FILENAME_WHITESPACE_PATTERN =
+ Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
+
+ private static Font readFont(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ String indexStr = parser.getAttributeValue(null, "index");
+ int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
+ List<Axis> axes = new ArrayList<Axis>();
+ String weightStr = parser.getAttributeValue(null, "weight");
+ int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
+ boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
+ StringBuilder filename = new StringBuilder();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ filename.append(parser.getText());
+ }
+ if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+ String tag = parser.getName();
+ if (tag.equals("axis")) {
+ axes.add(readAxis(parser));
+ } else {
+ skip(parser);
+ }
+ }
+ String fullFilename = "/system/fonts/" +
+ FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+ return new Font(fullFilename, index, axes, weight, isItalic);
+ }
+
+ /** The 'tag' attribute value is read as four character values between 0 and 255 inclusive. */
+ private static final Pattern TAG_PATTERN = Pattern.compile("[\\x00-\\xFF]{4}");
+
+ /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
+ * '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
+ */
+ private static final Pattern STYLE_VALUE_PATTERN =
+ Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
+
+ private static Axis readAxis(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int tag = 0;
+ String tagStr = parser.getAttributeValue(null, "tag");
+ if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
+ tag = tagStr.charAt(0) << 24 +
+ tagStr.charAt(1) << 16 +
+ tagStr.charAt(2) << 8 +
+ tagStr.charAt(3);
+ } else {
+ throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
+ }
+
+ float styleValue = 0;
+ String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+ if (styleValueStr != null && STYLE_VALUE_PATTERN.matcher(styleValueStr).matches()) {
+ styleValue = Float.parseFloat(styleValueStr);
+ } else {
+ throw new XmlPullParserException("Invalid styleValue attribute value.", parser, null);
+ }
+
+ skip(parser); // axis tag is empty, ignore any contents and consume end tag
+ return new Axis(tag, styleValue);
+ }
+
private static Alias readAlias(XmlPullParser parser)
throws XmlPullParserException, IOException {
Alias alias = new Alias();
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 1294323ce69b..f15aff7de39c 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -17,7 +17,6 @@
package android.graphics;
import android.content.res.AssetManager;
-import android.graphics.FontListParser.Family;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
@@ -28,6 +27,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -259,10 +260,26 @@ public class Typeface {
mStyle = nativeGetStyle(ni);
}
- private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
+ private static FontFamily makeFamilyFromParsed(FontListParser.Family family,
+ Map<String, ByteBuffer> bufferForPath) {
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
for (FontListParser.Font font : family.fonts) {
- fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic);
+ ByteBuffer fontBuffer = bufferForPath.get(font.fontName);
+ if (fontBuffer == null) {
+ try (FileInputStream file = new FileInputStream(font.fontName)) {
+ FileChannel fileChannel = file.getChannel();
+ long fontSize = fileChannel.size();
+ fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+ bufferForPath.put(font.fontName, fontBuffer);
+ } catch (IOException e) {
+ Log.e(TAG, "Error mapping font file " + font.fontName);
+ continue;
+ }
+ }
+ if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes,
+ font.weight, font.isItalic)) {
+ Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex);
+ }
}
return fontFamily;
}
@@ -280,13 +297,15 @@ public class Typeface {
FileInputStream fontsIn = new FileInputStream(configFilename);
FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
+ Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
+
List<FontFamily> familyList = new ArrayList<FontFamily>();
// Note that the default typeface is always present in the fallback list;
// this is an enhancement from pre-Minikin behavior.
for (int i = 0; i < fontConfig.families.size(); i++) {
- Family f = fontConfig.families.get(i);
+ FontListParser.Family f = fontConfig.families.get(i);
if (i == 0 || f.name == null) {
- familyList.add(makeFamilyFromParsed(f));
+ familyList.add(makeFamilyFromParsed(f, bufferForPath));
}
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
@@ -295,14 +314,14 @@ public class Typeface {
Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
for (int i = 0; i < fontConfig.families.size(); i++) {
Typeface typeface;
- Family f = fontConfig.families.get(i);
+ FontListParser.Family f = fontConfig.families.get(i);
if (f.name != null) {
if (i == 0) {
// The first entry is the default typeface; no sense in
// duplicating the corresponding FontFamily.
typeface = sDefaultTypeface;
} else {
- FontFamily fontFamily = makeFamilyFromParsed(f);
+ FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
FontFamily[] families = { fontFamily };
typeface = Typeface.createFromFamiliesWithDefault(families);
}
@@ -324,11 +343,11 @@ public class Typeface {
Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
- Log.e(TAG, "Error opening " + configFilename);
+ Log.e(TAG, "Error opening " + configFilename, e);
} catch (IOException e) {
- Log.e(TAG, "Error reading " + configFilename);
+ Log.e(TAG, "Error reading " + configFilename, e);
} catch (XmlPullParserException e) {
- Log.e(TAG, "XML parse exception for " + configFilename);
+ Log.e(TAG, "XML parse exception for " + configFilename, e);
}
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index bdbf3c04b000..9e0f1b460839 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -1482,8 +1482,9 @@ public class VectorDrawable extends Drawable {
if (mThemeAttrs != null) {
return true;
}
- boolean fillCanApplyTheme = canGradientApplyTheme(mFillColors);
- boolean strokeCanApplyTheme = canGradientApplyTheme(mStrokeColors);
+
+ boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
+ boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
if (fillCanApplyTheme || strokeCanApplyTheme) {
return true;
}
@@ -1493,30 +1494,42 @@ public class VectorDrawable extends Drawable {
@Override
public void applyTheme(Theme t) {
+ // Resolve the theme attributes directly referred by the VectorDrawable.
if (mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
updateStateFromTypedArray(a);
a.recycle();
}
- boolean fillCanApplyTheme = canGradientApplyTheme(mFillColors);
- boolean strokeCanApplyTheme = canGradientApplyTheme(mStrokeColors);
+ // Resolve the theme attributes in-directly referred by the VectorDrawable, for example,
+ // fillColor can refer to a color state list which itself needs to apply theme.
+ // And this is the reason we still want to keep partial update for the path's properties.
+ boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
+ boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
+
if (fillCanApplyTheme) {
mFillColors = mFillColors.obtainForTheme(t);
- nUpdateFullPathFillGradient(mNativePtr,
- ((GradientColor)mFillColors).getShader().getNativeInstance());
+ if (mFillColors instanceof GradientColor) {
+ nUpdateFullPathFillGradient(mNativePtr,
+ ((GradientColor) mFillColors).getShader().getNativeInstance());
+ } else if (mFillColors instanceof ColorStateList) {
+ nSetFillColor(mNativePtr, mFillColors.getDefaultColor());
+ }
}
if (strokeCanApplyTheme) {
mStrokeColors = mStrokeColors.obtainForTheme(t);
- nUpdateFullPathStrokeGradient(mNativePtr,
- ((GradientColor)mStrokeColors).getShader().getNativeInstance());
+ if (mStrokeColors instanceof GradientColor) {
+ nUpdateFullPathStrokeGradient(mNativePtr,
+ ((GradientColor) mStrokeColors).getShader().getNativeInstance());
+ } else if (mStrokeColors instanceof ColorStateList) {
+ nSetStrokeColor(mNativePtr, mStrokeColors.getDefaultColor());
+ }
}
}
- private boolean canGradientApplyTheme(ComplexColor complexColor) {
- return complexColor != null && complexColor.canApplyTheme()
- && complexColor instanceof GradientColor;
+ private boolean canComplexColorApplyTheme(ComplexColor complexColor) {
+ return complexColor != null && complexColor.canApplyTheme();
}
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1d9fe35bac35..3277c36c8a33 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2236,7 +2236,7 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
// See if any of the regions is better than the other
const int region_comparison = localeDataCompareRegions(
country, o.country,
- language, localeScript, requested->country);
+ language, requested->localeScript, requested->country);
if (region_comparison != 0) {
return (region_comparison > 0);
}
@@ -2526,17 +2526,34 @@ bool ResTable_config::match(const ResTable_config& settings) const {
// For backward compatibility and supporting private-use locales, we
// fall back to old behavior if we couldn't determine the script for
- // either of the desired locale or the provided locale.
- if (localeScript[0] == '\0' || localeScript[1] == '\0') {
+ // either of the desired locale or the provided locale. But if we could determine
+ // the scripts, they should be the same for the locales to match.
+ bool countriesMustMatch = false;
+ char computed_script[4];
+ const char* script;
+ if (settings.localeScript[0] == '\0') { // could not determine the request's script
+ countriesMustMatch = true;
+ } else {
+ if (localeScript[0] == '\0') { // script was not provided, so we try to compute it
+ localeDataComputeScript(computed_script, language, country);
+ if (computed_script[0] == '\0') { // we could not compute the script
+ countriesMustMatch = true;
+ } else {
+ script = computed_script;
+ }
+ } else { // script was provided, so just use it
+ script = localeScript;
+ }
+ }
+
+ if (countriesMustMatch) {
if (country[0] != '\0'
&& (country[0] != settings.country[0]
|| country[1] != settings.country[1])) {
return false;
}
} else {
- // But if we could determine the scripts, they should be the same
- // for the locales to match.
- if (memcmp(localeScript, settings.localeScript, sizeof(localeScript)) != 0) {
+ if (memcmp(script, settings.localeScript, sizeof(settings.localeScript)) != 0) {
return false;
}
}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 7b386404fe8c..4b8d65cf86b4 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -371,6 +371,19 @@ TEST(ConfigLocaleTest, match) {
EXPECT_TRUE(supported.match(requested));
}
+TEST(ConfigLocaleTest, match_emptyScript) {
+ ResTable_config supported, requested;
+
+ fillIn("fr", "FR", NULL, NULL, &supported);
+ fillIn("fr", "CA", NULL, NULL, &requested);
+
+ // emulate packages built with older AAPT
+ memset(supported.localeScript, '\0', 4);
+ supported.localeScriptWasProvided = false;
+
+ EXPECT_TRUE(supported.match(requested));
+}
+
TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
ResTable_config config1, config2, request;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1f242a36e80e..6a565033093c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -144,20 +144,8 @@ hwui_c_includes += \
external/skia/include/private \
external/skia/src/core
-hwui_shared_libraries := \
- liblog \
- libcutils \
- libutils \
- libEGL \
- libGLESv2 \
- libskia \
- libui \
- libgui \
- libprotobuf-cpp-lite \
-
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
- hwui_shared_libraries += libRS libRScpp
hwui_c_includes += \
$(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
frameworks/rs/cpp \
@@ -180,12 +168,15 @@ include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES := $(hwui_src_files)
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -196,7 +187,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static_null_gpu
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -205,8 +195,12 @@ LOCAL_SRC_FILES := \
debug/nullegl.cpp \
debug/nullgles.cpp
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(hwui_c_includes) \
+ $(call hwui_proto_include)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_STATIC_LIBRARY)
# ------------------------
@@ -218,8 +212,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE := libhwui
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_SHARED_LIBRARY)
# ------------------------
@@ -230,7 +225,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
LOCAL_CFLAGS := \
$(hwui_cflags) \
@@ -263,6 +257,7 @@ ifeq (true, $(HWUI_NEW_OPS))
tests/unit/RecordingCanvasTests.cpp
endif
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_NATIVE_TEST)
# ------------------------
@@ -278,7 +273,6 @@ LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuitest
LOCAL_MODULE_STEM_64 := hwuitest64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := $(hwui_cflags)
# set to libhwui_static_null_gpu to skip actual GL commands
@@ -289,6 +283,7 @@ LOCAL_SRC_FILES += \
tests/macrobench/TestSceneRunner.cpp \
tests/macrobench/main.cpp
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
# ------------------------
@@ -303,7 +298,6 @@ LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := hwuimicro
LOCAL_MODULE_STEM_64 := hwuimicro64
-LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
@@ -325,6 +319,5 @@ ifeq (true, $(HWUI_NEW_OPS))
tests/microbench/FrameBuilderBench.cpp
endif
-LOCAL_CLANG := true # workaround gcc bug
-
+include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 3db28c982469..5a5845af81b9 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -100,7 +100,7 @@ public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const RecordedOp& recordedOp) {
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- BakedOpState* bakedState = new (allocator) BakedOpState(
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
allocator, snapshot, recordedOp, false);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
@@ -124,7 +124,7 @@ public:
? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
: true;
- BakedOpState* bakedState = new (allocator) BakedOpState(
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
allocator, snapshot, recordedOp, expandForStroke);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
@@ -140,16 +140,12 @@ public:
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
// clip isn't empty, so construct the op
- return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
}
static BakedOpState* directConstruct(LinearAllocator& allocator,
const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
- return new (allocator) BakedOpState(clip, dstRect, recordedOp);
- }
-
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
}
// computed state:
@@ -162,6 +158,8 @@ public:
const RecordedOp* op;
private:
+ friend class LinearAllocator;
+
BakedOpState(LinearAllocator& allocator, Snapshot& snapshot,
const RecordedOp& recordedOp, bool expandForStroke)
: computedState(allocator, snapshot, recordedOp, expandForStroke)
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index c2e14a29f29e..6d5833b3be86 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -45,7 +45,7 @@ struct DirtyStack {
};
DamageAccumulator::DamageAccumulator() {
- mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ mHead = mAllocator.create_trivial<DirtyStack>();
memset(mHead, 0, sizeof(DirtyStack));
// Create a root that we will not pop off
mHead->prev = mHead;
@@ -78,7 +78,7 @@ void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
void DamageAccumulator::pushCommon() {
if (!mHead->next) {
- DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ DirtyStack* nextFrame = mAllocator.create_trivial<DirtyStack>();
nextFrame->next = nullptr;
nextFrame->prev = mHead;
mHead->next = nextFrame;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 2d5979f2f1a7..98ccf11b1c2a 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -49,11 +49,6 @@ typedef const void* mergeid_t;
class DeferredDisplayState {
public:
- static void* operator new(size_t size) = delete;
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
// global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
Rect mBounds;
@@ -124,7 +119,7 @@ private:
DeferredDisplayList(const DeferredDisplayList& other); // disallow copy
DeferredDisplayState* createState() {
- return new (mAllocator) DeferredDisplayState();
+ return mAllocator.create_trivial<DeferredDisplayState>();
}
void tryRecycleState(DeferredDisplayState* state) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index e5711e35a88b..a703e227fc8d 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -251,7 +251,7 @@ private:
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -320,8 +320,7 @@ private:
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
- alloc().autoDestroy(localBitmap);
+ SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 20501ba3c1d9..98315d0a416a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -64,7 +64,9 @@ public:
static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); }
static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ // FIXME: Quick hack to keep old pipeline working, delete this when
+ // we no longer need to support HWUI_NEWOPS := false
+ return allocator.alloc<char>(size);
}
enum OpLogFlag {
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 185accec1ef6..4f51036b336e 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -209,7 +209,7 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
// not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
if (node.getLayer()) {
// HW layer
- LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
+ LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
// Node's layer already deferred, schedule it to render into parent layer
@@ -220,13 +220,13 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
// (temp layers are clipped to viewport, since they don't persist offscreen content)
SkPaint saveLayerPaint;
saveLayerPaint.setAlpha(properties.getAlpha());
- deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
+ deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>(
saveLayerBounds,
Matrix4::identity(),
nullptr, // no record-time clip - need only respect defer-time one
&saveLayerPaint));
deferNodeOps(node);
- deferEndLayerOp(*new (mAllocator) EndLayerOp());
+ deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>());
} else {
deferNodeOps(node);
}
@@ -549,7 +549,7 @@ void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
SkPaint* paint = op.vectorDrawable->getPaint();
- const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+ const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
op.localMatrix,
op.localClip,
paint,
@@ -565,7 +565,7 @@ void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
float y = *(op.y);
float radius = *(op.radius);
Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
- const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+ const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(
unmappedBounds,
op.localMatrix,
op.localClip,
@@ -626,7 +626,7 @@ void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
// allocate a temporary round rect op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
- const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+ const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>(
Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
op.localMatrix,
op.localClip,
@@ -754,7 +754,7 @@ void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
// record the draw operation into the previous layer's list of draw commands
// uses state from the associated beginLayerOp, since it has all the state needed for drawing
- LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+ LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
beginLayerOp.unmappedBounds,
beginLayerOp.localMatrix,
beginLayerOp.localClip,
@@ -788,7 +788,7 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
/**
* First, defer an operation to copy out the content from the rendertarget into a layer.
*/
- auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
+ auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
&(currentLayer().viewportClip), dstRect, *copyToOp);
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
@@ -803,7 +803,7 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
* And stash an operation to copy that layer back under the rendertarget until
* a balanced EndUnclippedLayerOp is seen
*/
- auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
+ auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
bakedState = BakedOpState::directConstruct(mAllocator,
&(currentLayer().viewportClip), dstRect, *copyFromOp);
currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 11293d61211b..c8f5e9435594 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -165,6 +165,10 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
generateTexture(colors, positions, info.width, 2, texture);
mSize += size;
+ LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
+ "size != texture->objectSize(), size %" PRIu32 ", objectSize %d"
+ " width = %" PRIu32 " bytesPerPixel() = %zu",
+ size, texture->objectSize(), info.width, bytesPerPixel());
mCache.put(gradient, texture);
return texture;
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index 7170d4fbeea7..1ba3bf26c0d4 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -64,10 +64,6 @@ protected:
class OpBatch : public BatchBase {
public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
OpBatch(batchid_t batchId, BakedOpState* op)
: BatchBase(batchId, op, false) {
}
@@ -80,10 +76,6 @@ public:
class MergingOpBatch : public BatchBase {
public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
MergingOpBatch(batchid_t batchId, BakedOpState* op)
: BatchBase(batchId, op, true)
, mClipSideFlags(op->computedState.clipSideFlags) {
@@ -247,7 +239,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
// put the verts in the frame allocator, since
// 1) SimpleRectsOps needs verts, not rects
// 2) even if mClearRects stored verts, std::vectors will move their contents
- Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+ Vertex* const verts = (Vertex*) allocator.alloc<Vertex>(vertCount * sizeof(Vertex));
Vertex* currentVert = verts;
Rect bounds = mClearRects[0];
@@ -264,7 +256,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
// Flush all of these clears with a single draw
SkPaint* paint = allocator.create<SkPaint>();
paint->setXfermodeMode(SkXfermode::kClear_Mode);
- SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+ SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds,
Matrix4::identity(), nullptr, paint,
verts, vertCount);
BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
@@ -292,7 +284,7 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
targetBatch->batchOp(op);
} else {
// new non-merging batch
- targetBatch = new (allocator) OpBatch(batchId, op);
+ targetBatch = allocator.create<OpBatch>(batchId, op);
mBatchLookup[batchId] = targetBatch;
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
}
@@ -323,7 +315,7 @@ void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
targetBatch->mergeOp(op);
} else {
// new merging batch
- targetBatch = new (allocator) MergingOpBatch(batchId, op);
+ targetBatch = allocator.create<MergingOpBatch>(batchId, op);
mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 16929b8ac8ee..269e590892d3 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -83,9 +83,9 @@ void RecordingCanvas::onViewportInitialized() {
void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
if (removed.flags & Snapshot::kFlagIsFboLayer) {
- addOp(new (alloc()) EndLayerOp());
+ addOp(alloc().create_trivial<EndLayerOp>());
} else if (removed.flags & Snapshot::kFlagIsLayer) {
- addOp(new (alloc()) EndUnclippedLayerOp());
+ addOp(alloc().create_trivial<EndUnclippedLayerOp>());
}
}
@@ -167,7 +167,7 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
snapshot.roundRectClipState = nullptr;
- addOp(new (alloc()) BeginLayerOp(
+ addOp(alloc().create_trivial<BeginLayerOp>(
unmappedBounds,
*previous.transform, // transform to *draw* with
previousClip, // clip to *draw* with
@@ -175,7 +175,7 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
} else {
snapshot.flags |= Snapshot::kFlagIsLayer;
- addOp(new (alloc()) BeginUnclippedLayerOp(
+ addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
unmappedBounds,
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -241,7 +241,7 @@ void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
}
void RecordingCanvas::drawPaint(const SkPaint& paint) {
- addOp(new (alloc()) RectOp(
+ addOp(alloc().create_trivial<RectOp>(
mState.getRenderTargetClipBounds(), // OK, since we've not passed transform
Matrix4::identity(),
getRecordedClip(),
@@ -261,7 +261,7 @@ void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPa
if (floatCount < 2) return;
floatCount &= ~0x1; // round down to nearest two
- addOp(new (alloc()) PointsOp(
+ addOp(alloc().create_trivial<PointsOp>(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -272,7 +272,7 @@ void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPai
if (floatCount < 4) return;
floatCount &= ~0x3; // round down to nearest four
- addOp(new (alloc()) LinesOp(
+ addOp(alloc().create_trivial<LinesOp>(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
getRecordedClip(),
@@ -280,7 +280,7 @@ void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPai
}
void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
- addOp(new (alloc()) RectOp(
+ addOp(alloc().create_trivial<RectOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -290,7 +290,7 @@ void RecordingCanvas::drawRect(float left, float top, float right, float bottom,
void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
if (rects == nullptr) return;
- Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex));
+ Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc<Vertex>(vertexCount * sizeof(Vertex));
Vertex* vertex = rectData;
float left = FLT_MAX;
@@ -313,7 +313,7 @@ void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const
right = std::max(right, r);
bottom = std::max(bottom, b);
}
- addOp(new (alloc()) SimpleRectsOp(
+ addOp(alloc().create_trivial<SimpleRectsOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -347,7 +347,7 @@ void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
}
void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
- addOp(new (alloc()) RoundRectOp(
+ addOp(alloc().create_trivial<RoundRectOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -367,7 +367,7 @@ void RecordingCanvas::drawRoundRect(
mDisplayList->ref(ry);
mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
- addOp(new (alloc()) RoundRectPropsOp(
+ addOp(alloc().create_trivial<RoundRectPropsOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),
&paint->value,
@@ -389,7 +389,7 @@ void RecordingCanvas::drawCircle(
mDisplayList->ref(radius);
mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
- addOp(new (alloc()) CirclePropsOp(
+ addOp(alloc().create_trivial<CirclePropsOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),
&paint->value,
@@ -397,7 +397,7 @@ void RecordingCanvas::drawCircle(
}
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
- addOp(new (alloc()) OvalOp(
+ addOp(alloc().create_trivial<OvalOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -406,7 +406,7 @@ void RecordingCanvas::drawOval(float left, float top, float right, float bottom,
void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
- addOp(new (alloc()) ArcOp(
+ addOp(alloc().create_trivial<ArcOp>(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -415,7 +415,7 @@ void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
}
void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- addOp(new (alloc()) PathOp(
+ addOp(alloc().create_trivial<PathOp>(
Rect(path.getBounds()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -424,7 +424,7 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mDisplayList->ref(tree);
- addOp(new (alloc()) VectorDrawableOp(
+ addOp(alloc().create_trivial<VectorDrawableOp>(
tree,
Rect(tree->getBounds()),
*(mState.currentSnapshot()->transform),
@@ -475,7 +475,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr
drawBitmap(&bitmap, paint);
restore();
} else {
- addOp(new (alloc()) BitmapRectOp(
+ addOp(alloc().create_trivial<BitmapRectOp>(
Rect(dstLeft, dstTop, dstRight, dstBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -487,7 +487,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr
void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
int vertexCount = (meshWidth + 1) * (meshHeight + 1);
- addOp(new (alloc()) BitmapMeshOp(
+ addOp(alloc().create_trivial<BitmapMeshOp>(
calcBoundsOfPoints(vertices, vertexCount * 2),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -499,7 +499,7 @@ void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int
void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
- addOp(new (alloc()) PatchOp(
+ addOp(alloc().create_trivial<PatchOp>(
Rect(dstLeft, dstTop, dstRight, dstBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -515,7 +515,7 @@ void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, i
positions = refBuffer<float>(positions, glyphCount * 2);
// TODO: either must account for text shadow in bounds, or record separate ops for text shadows
- addOp(new (alloc()) TextOp(
+ addOp(alloc().create_trivial<TextOp>(
Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -527,7 +527,7 @@ void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, con
float hOffset, float vOffset, const SkPaint& paint) {
if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
- addOp(new (alloc()) TextOnPathOp(
+ addOp(alloc().create_trivial<TextOnPathOp>(
mState.getLocalClipBounds(), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -535,7 +535,7 @@ void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, con
}
void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
- addOp(new (alloc()) BitmapOp(
+ addOp(alloc().create_trivial<BitmapOp>(
Rect(bitmap->width(), bitmap->height()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -544,7 +544,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
auto&& stagingProps = renderNode->stagingProperties();
- RenderNodeOp* op = new (alloc()) RenderNodeOp(
+ RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
Rect(stagingProps.getWidth(), stagingProps.getHeight()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
@@ -570,7 +570,7 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
totalTransform.multiply(layer->getTransform());
- addOp(new (alloc()) TextureLayerOp(
+ addOp(alloc().create_trivial<TextureLayerOp>(
Rect(layer->getWidth(), layer->getHeight()),
totalTransform,
getRecordedClip(),
@@ -579,7 +579,7 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
void RecordingCanvas::callDrawGLFunction(Functor* functor) {
mDisplayList->functors.push_back(functor);
- addOp(new (alloc()) FunctorOp(
+ addOp(alloc().create_trivial<FunctorOp>(
mState.getLocalClipBounds(), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
getRecordedClip(),
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index cc14e6111cec..719872d35169 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -219,7 +219,7 @@ private:
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -290,8 +290,7 @@ private:
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
- alloc().autoDestroy(localBitmap);
+ SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index dbaa905b0728..0ac2f1411140 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -46,7 +46,7 @@ class RoundRectClipState {
public:
/** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.alloc<RoundRectClipState>(size);
}
bool areaRequiresRoundRectClip(const Rect& rect) const {
@@ -67,7 +67,7 @@ class ProjectionPathMask {
public:
/** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
+ return allocator.alloc<ProjectionPathMask>(size);
}
const SkPath* projectionMask;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index c09b6dd89e4e..4f49a3518be0 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -28,13 +28,19 @@ namespace uirenderer {
static int bytesPerPixel(GLint glFormat) {
switch (glFormat) {
+ // The wrapped-texture case, usually means a SurfaceTexture
+ case 0:
+ return 0;
case GL_ALPHA:
return 1;
case GL_RGB:
return 3;
case GL_RGBA:
- default:
return 4;
+ case GL_RGBA16F:
+ return 16;
+ default:
+ LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
}
}
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
new file mode 100644
index 000000000000..7d4ef0f1f31e
--- /dev/null
+++ b/libs/hwui/hwui_static_deps.mk
@@ -0,0 +1,28 @@
+###############################################################################
+#
+#
+# This file contains the shared and static dependencies needed by any target
+# that attempts to statically link HWUI (i.e. libhwui_static build target). This
+# file should be included by any target that lists libhwui_static as a
+# dependency.
+#
+# This is a workaround for the fact that the build system does not add these
+# transitive dependencies when it attempts to link libhwui_static into another
+# library.
+#
+###############################################################################
+
+LOCAL_SHARED_LIBRARIES += \
+ liblog \
+ libcutils \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libskia \
+ libui \
+ libgui \
+ libprotobuf-cpp-lite
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+endif \ No newline at end of file
diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
index 5c442901045e..402a09c55e8f 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -30,7 +30,7 @@ struct SimplePair {
TEST(LinearAllocator, create) {
LinearAllocator la;
EXPECT_EQ(0u, la.usedSize());
- la.alloc(64);
+ la.alloc<char>(64);
// There's some internal tracking as well as padding
// so the usedSize isn't strictly defined
EXPECT_LE(64u, la.usedSize());
@@ -50,13 +50,12 @@ TEST(LinearAllocator, dtor) {
la.create<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
la.create<SimplePair>();
}
- la.alloc(100);
+ la.alloc<char>(100);
for (int i = 0; i < 5; i++) {
- auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
- la.autoDestroy(sd);
- new (la) SimplePair();
+ la.create<TestUtils::SignalingDtor>(destroyed + 5 + i);
+ la.create_trivial<SimplePair>();
}
- la.alloc(100);
+ la.alloc<char>(100);
for (int i = 0; i < 10; i++) {
EXPECT_EQ(0, destroyed[i]);
}
@@ -70,7 +69,7 @@ TEST(LinearAllocator, rewind) {
int destroyed = 0;
{
LinearAllocator la;
- auto addr = la.alloc(100);
+ auto addr = la.alloc<char>(100);
EXPECT_LE(100u, la.usedSize());
la.rewindIfLastAlloc(addr, 100);
EXPECT_GT(16u, la.usedSize());
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index e6a4c03156b4..5bba420a258f 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -81,10 +81,6 @@ static void _addAllocation(int count) {
#define min(x,y) (((x) < (y)) ? (x) : (y))
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
- return la.alloc(size);
-}
-
namespace android {
namespace uirenderer {
@@ -171,7 +167,7 @@ void LinearAllocator::ensureNext(size_t size) {
mNext = start(mCurrentPage);
}
-void* LinearAllocator::alloc(size_t size) {
+void* LinearAllocator::allocImpl(size_t size) {
size = ALIGN(size);
if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
@@ -196,7 +192,7 @@ void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
"DestructorNode must have standard layout");
static_assert(std::is_trivially_destructible<DestructorNode>::value,
"DestructorNode must be trivially destructable");
- auto node = new (*this) DestructorNode();
+ auto node = new (allocImpl(sizeof(DestructorNode))) DestructorNode();
node->dtor = dtor;
node->addr = addr;
node->next = mDtorList;
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index dcbc0dda951a..0a0e1858cd91 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -52,30 +52,36 @@ public:
* The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
* delete() on an object stored in a buffer is needed, it should be overridden to use
* rewindIfLastAlloc()
+ *
+ * Note that unlike create, for alloc the type is purely for compile-time error
+ * checking and does not affect size.
*/
- void* alloc(size_t size);
+ template<class T>
+ void* alloc(size_t size) {
+ static_assert(std::is_trivially_destructible<T>::value,
+ "Error, type is non-trivial! did you mean to use create()?");
+ return allocImpl(size);
+ }
/**
* Allocates an instance of the template type with the given construction parameters
* and adds it to the automatic destruction list.
*/
template<class T, typename... Params>
- T* create(Params... params) {
- T* ret = new (*this) T(params...);
- autoDestroy(ret);
+ T* create(Params&&... params) {
+ T* ret = new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
+ if (!std::is_trivially_destructible<T>::value) {
+ auto dtor = [](void* ret) { ((T*)ret)->~T(); };
+ addToDestructionList(dtor, ret);
+ }
return ret;
}
- /**
- * Adds the pointer to the tracking list to have its destructor called
- * when the LinearAllocator is destroyed.
- */
- template<class T>
- void autoDestroy(T* addr) {
- if (!std::is_trivially_destructible<T>::value) {
- auto dtor = [](void* addr) { ((T*)addr)->~T(); };
- addToDestructionList(dtor, addr);
- }
+ template<class T, typename... Params>
+ T* create_trivial(Params&&... params) {
+ static_assert(std::is_trivially_destructible<T>::value,
+ "Error, called create_trivial on a non-trivial type");
+ return new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...);
}
/**
@@ -114,6 +120,8 @@ private:
DestructorNode* next = nullptr;
};
+ void* allocImpl(size_t size);
+
void addToDestructionList(Destructor, void* addr);
void runDestructorFor(void* addr);
Page* newPage(size_t pageSize);
@@ -159,7 +167,7 @@ public:
: linearAllocator(other.linearAllocator) {}
T* allocate(size_t num, const void* = 0) {
- return (T*)(linearAllocator.alloc(num * sizeof(T)));
+ return (T*)(linearAllocator.alloc<void*>(num * sizeof(T)));
}
void deallocate(pointer p, size_t num) {
@@ -187,6 +195,4 @@ public:
}; // namespace uirenderer
}; // namespace android
-void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
-
#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d8f507c9b70e..a490685a3a8b 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -28,7 +28,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class GnssMeasurement implements Parcelable {
private int mFlags;
- private byte mPrn;
+ private short mSvid;
private double mTimeOffsetInNs;
private short mState;
private long mReceivedGpsTowInNs;
@@ -198,7 +198,7 @@ public final class GnssMeasurement implements Parcelable {
*/
public void set(GnssMeasurement measurement) {
mFlags = measurement.mFlags;
- mPrn = measurement.mPrn;
+ mSvid = measurement.mSvid;
mTimeOffsetInNs = measurement.mTimeOffsetInNs;
mState = measurement.mState;
mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
@@ -248,15 +248,15 @@ public final class GnssMeasurement implements Parcelable {
* Gets the Pseudo-random number (PRN).
* Range: [1, 32]
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number (PRN).
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -1210,7 +1210,7 @@ public final class GnssMeasurement implements Parcelable {
GnssMeasurement gnssMeasurement = new GnssMeasurement();
gnssMeasurement.mFlags = parcel.readInt();
- gnssMeasurement.mPrn = parcel.readByte();
+ gnssMeasurement.mSvid = (short) parcel.readInt();
gnssMeasurement.mTimeOffsetInNs = parcel.readDouble();
gnssMeasurement.mState = (short) parcel.readInt();
gnssMeasurement.mReceivedGpsTowInNs = parcel.readLong();
@@ -1253,9 +1253,10 @@ public final class GnssMeasurement implements Parcelable {
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mFlags);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeDouble(mTimeOffsetInNs);
parcel.writeInt(mState);
parcel.writeLong(mReceivedGpsTowInNs);
@@ -1301,7 +1302,7 @@ public final class GnssMeasurement implements Parcelable {
final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n";
StringBuilder builder = new StringBuilder("GnssMeasurement:\n");
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
@@ -1422,7 +1423,7 @@ public final class GnssMeasurement implements Parcelable {
private void initialize() {
mFlags = HAS_NO_FLAGS;
- setPrn(Byte.MIN_VALUE);
+ setSvid((short) 0);
setTimeOffsetInNs(Long.MIN_VALUE);
setState(STATE_UNKNOWN);
setReceivedGpsTowInNs(Long.MIN_VALUE);
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 0e011d5c98a8..86328eb5dcd7 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy;
import java.security.InvalidParameterException;
/**
- * A class containing a GPS satellite Navigation Message.
+ * A class containing a GNSS satellite Navigation Message.
*/
public final class GnssNavigationMessage implements Parcelable {
@@ -84,7 +84,7 @@ public final class GnssNavigationMessage implements Parcelable {
// End enumerations in sync with gps.h
private byte mType;
- private byte mPrn;
+ private short mSvid;
private short mMessageId;
private short mSubmessageId;
private byte[] mData;
@@ -99,7 +99,7 @@ public final class GnssNavigationMessage implements Parcelable {
*/
public void set(GnssNavigationMessage navigationMessage) {
mType = navigationMessage.mType;
- mPrn = navigationMessage.mPrn;
+ mSvid = navigationMessage.mSvid;
mMessageId = navigationMessage.mMessageId;
mSubmessageId = navigationMessage.mSubmessageId;
mData = navigationMessage.mData;
@@ -153,15 +153,15 @@ public final class GnssNavigationMessage implements Parcelable {
* Gets the Pseudo-random number.
* Range: [1, 32].
*/
- public byte getPrn() {
- return mPrn;
+ public short getSvid() {
+ return mSvid;
}
/**
* Sets the Pseud-random number.
*/
- public void setPrn(byte value) {
- mPrn = value;
+ public void setSvid(short value) {
+ mSvid = value;
}
/**
@@ -256,7 +256,7 @@ public final class GnssNavigationMessage implements Parcelable {
GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
navigationMessage.setType(parcel.readByte());
- navigationMessage.setPrn(parcel.readByte());
+ navigationMessage.setSvid((short) parcel.readInt());
navigationMessage.setMessageId((short) parcel.readInt());
navigationMessage.setSubmessageId((short) parcel.readInt());
@@ -281,9 +281,10 @@ public final class GnssNavigationMessage implements Parcelable {
}
};
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeByte(mType);
- parcel.writeByte(mPrn);
+ parcel.writeInt(mSvid);
parcel.writeInt(mMessageId);
parcel.writeInt(mSubmessageId);
parcel.writeInt(mData.length);
@@ -302,7 +303,7 @@ public final class GnssNavigationMessage implements Parcelable {
StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
builder.append(String.format(format, "Type", getTypeString()));
- builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Svid", mSvid));
builder.append(String.format(format, "Status", getStatusString()));
builder.append(String.format(format, "MessageId", mMessageId));
builder.append(String.format(format, "SubmessageId", mSubmessageId));
@@ -321,7 +322,7 @@ public final class GnssNavigationMessage implements Parcelable {
private void initialize() {
mType = MESSAGE_TYPE_UNKNOWN;
- mPrn = 0;
+ mSvid = 0;
mMessageId = -1;
mSubmessageId = -1;
mData = EMPTY_ARRAY;
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 77e8a5bbaca0..906e944f06ab 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -47,24 +47,26 @@ public final class GnssStatus {
public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
/** @hide */
- public static final int PRN_SHIFT_WIDTH = 3;
+ public static final int SVID_SHIFT_WIDTH = 7;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 3;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_MASK = 0xf;
/* These package private values are modified by the LocationManager class */
- /* package */ int[] mPrnWithFlags;
+ /* package */ int[] mSvidWithFlags;
/* package */ float[] mSnrs;
/* package */ float[] mElevations;
/* package */ float[] mAzimuths;
- /* package */ int[] mConstellationTypes;
/* package */ int mSvCount;
- GnssStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations, float[] azimuths,
- int[] constellationTypes) {
+ GnssStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
mSvCount = svCount;
- mPrnWithFlags = prnWithFlags;
+ mSvidWithFlags = svidWithFlags;
mSnrs = snrs;
mElevations = elevations;
mAzimuths = azimuths;
- mConstellationTypes = constellationTypes;
}
/**
@@ -79,15 +81,16 @@ public final class GnssStatus {
* @param satIndex the index of the satellite in the list.
*/
public int getConstellationType(int satIndex) {
- return mConstellationTypes[satIndex];
+ return (mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & CONSTELLATION_TYPE_MASK;
}
/**
* Retrieves the pseudo-random number of the satellite at the specified position.
* @param satIndex the index of the satellite in the list.
*/
- public int getPrn(int satIndex) {
- return mPrnWithFlags[satIndex] >> PRN_SHIFT_WIDTH;
+ public int getSvid(int satIndex) {
+ return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
}
/**
@@ -119,7 +122,7 @@ public final class GnssStatus {
* @param satIndex the index of the satellite in the list.
*/
public boolean hasEphemeris(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
}
/**
@@ -127,7 +130,7 @@ public final class GnssStatus {
* @param satIndex the index of the satellite in the list.
*/
public boolean hasAlmanac(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
}
/**
@@ -135,6 +138,6 @@ public final class GnssStatus {
* @param satIndex the index of the satellite in the list.
*/
public boolean usedInFix(int satIndex) {
- return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 8d2f781e497f..e41e20c172f7 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -138,15 +138,19 @@ public final class GpsStatus {
// For API-compat a public ctor() is not available
GpsStatus() {}
- private void setStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes) {
+ private void setStatus(int svCount, int[] svidWithFlags, float[] snrs, float[] elevations,
+ float[] azimuths) {
clearSatellites();
for (int i = 0; i < svCount; i++) {
+ final int constellationType =
+ (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & GnssStatus.CONSTELLATION_TYPE_MASK;
// Skip all non-GPS satellites.
- if (constellationTypes[i] != GnssStatus.CONSTELLATION_GPS) {
+ if (constellationType != GnssStatus.CONSTELLATION_GPS) {
+ // TODO: translate the defacto pre-N use of prn's >32 to new struct
continue;
}
- int prn = prnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH;
+ int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
if (prn > 0 && prn <= NUM_SATELLITES) {
GpsSatellite satellite = mSatellites.get(prn);
if (satellite == null) {
@@ -159,11 +163,11 @@ public final class GpsStatus {
satellite.mElevation = elevations[i];
satellite.mAzimuth = azimuths[i];
satellite.mHasEphemeris =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
satellite.mHasAlmanac =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
satellite.mUsedInFix =
- (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
}
}
@@ -176,8 +180,8 @@ public final class GpsStatus {
*/
void setStatus(GnssStatus status, int timeToFirstFix) {
mTimeToFirstFix = timeToFirstFix;
- setStatus(status.mSvCount, status.mPrnWithFlags, status.mSnrs, status.mElevations,
- status.mAzimuths, status.mConstellationTypes);
+ setStatus(status.mSvCount, status.mSvidWithFlags, status.mSnrs, status.mElevations,
+ status.mAzimuths);
}
void setTimeToFirstFix(int ttff) {
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d1c6a85a9fdd..8c7d06eee5e6 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -26,7 +26,7 @@ oneway interface IGnssStatusListener
void onGnssStarted();
void onGnssStopped();
void onFirstFix(int ttff);
- void onSvStatusChanged(int svCount, in int[] prnWithFlags, in float[] snrs,
- in float[] elevations, in float[] azimuths, in int[] constellationTypes);
+ void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] snrs,
+ in float[] elevations, in float[] azimuths);
void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 30cf101397db..23f0710a0a9d 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1562,10 +1562,9 @@ public class LocationManager {
@Override
public void onSvStatusChanged(int svCount, int[] prnWithFlags,
- float[] snrs, float[] elevations, float[] azimuths, int[] constellationTypes) {
+ float[] snrs, float[] elevations, float[] azimuths) {
if (mGnssCallback != null) {
- mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths,
- constellationTypes);
+ mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index bdf6d9f19558..6fc2f87ee640 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -885,10 +885,10 @@ public class AudioTrack implements AudioRouting
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) {
- // NB: this section is only valid with PCM data.
+ // NB: this section is only valid with PCM or IEC61937 data.
// To update when supporting compressed formats
int frameSizeInBytes;
- if (AudioFormat.isEncodingLinearPcm(mAudioFormat)) {
+ if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) {
frameSizeInBytes = mChannelCount * AudioFormat.getBytesPerSample(mAudioFormat);
} else {
frameSizeInBytes = 1;
@@ -1052,32 +1052,27 @@ public class AudioTrack implements AudioRouting
}
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
-// TODO Change setBufferSizeInFrames(int) reference below to
-// {@link #setBufferSizeInFrames(int)} after @hide is removed.
+
/**
- * Returns the effective size of the <code>AudioTrack</code> buffer
+ * Returns the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
- * <p> This will be less than or equal to the result of
- * getBufferCapacityInFrames().
- * It will be equal if setBufferSizeInFrames(int) has never been called.
- * <p> If the track is subsequently routed to a different output sink, the buffer
- * size and capacity may enlarge to accommodate.
- * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
- * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
- * the size of the native <code>AudioTrack</code> buffer in bytes.
- * <p> See also {@link AudioManager#getProperty(String)} for key
- * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
- * @return current size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * <p> This will be less than or equal to the result of
+ * {@link #getBufferCapacityInFrames()}.
+ * It will be equal if {@link #setBufferSizeInFrames(int)} has never been called.
+ * <p> If the track is subsequently routed to a different output sink, the buffer
+ * size and capacity may enlarge to accommodate.
+ * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
+ * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
+ * the size of the native <code>AudioTrack</code> buffer in bytes.
+ * <p> See also {@link AudioManager#getProperty(String)} for key
+ * {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
+ * @return current size in frames of the <code>AudioTrack</code> buffer.
+ * @throws IllegalStateException
*/
public int getBufferSizeInFrames() {
return native_get_buffer_size_frames();
}
-// TODO Change getBufferCapacityInFrames() reference below to
-// {@link #getBufferCapacityInFrames()} after @hide is removed.
/**
* Limits the effective size of the <code>AudioTrack</code> buffer
* that the application writes to.
@@ -1087,9 +1082,9 @@ public class AudioTrack implements AudioRouting
* <p>Changing this limit modifies the latency associated with
* the buffer for this track. A smaller size will give lower latency
* but there may be more glitches due to buffer underruns.
- * <p>The actual size used may not be equal to this requested size.
+ * <p>The actual size used may not be equal to this requested size.
* It will be limited to a valid range with a maximum of
- * getBufferCapacityInFrames().
+ * {@link #getBufferCapacityInFrames()}.
* It may also be adjusted slightly for internal reasons.
* If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
* will be returned.
@@ -1097,9 +1092,9 @@ public class AudioTrack implements AudioRouting
* It is not supported for compressed audio tracks.
*
* @param bufferSizeInFrames requested buffer size
- * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
- * {@link #ERROR_INVALID_OPERATION}
- * @throws IllegalStateException
+ * @return the actual buffer size in frames or an error code,
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalStateException
*/
public int setBufferSizeInFrames(int bufferSizeInFrames) {
if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 4b6b4fad4de7..8618ec98c6c3 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -32,8 +32,8 @@ import java.nio.ByteBuffer;
import java.util.Map;
/**
- * MediaMuxer facilitates muxing elementary streams. Currently only supports an
- * mp4 file as the output and at most one audio and/or one video elementary
+ * MediaMuxer facilitates muxing elementary streams. Currently supports mp4 or
+ * webm file as the output and at most one audio and/or one video elementary
* stream.
* <p>
* It is generally used like this:
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 54543ec43f89..e197141e0214 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -42,7 +42,7 @@ import java.util.Objects;
@SystemApi
public class AudioMixingRule {
- private AudioMixingRule(int mixType, ArrayList<AttributeMatchCriterion> criteria) {
+ private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria) {
mCriteria = criteria;
mTargetMixType = mixType;
}
@@ -91,21 +91,21 @@ public class AudioMixingRule {
public static final int RULE_EXCLUDE_UID =
RULE_EXCLUSION_MASK | RULE_MATCH_UID;
- static final class AttributeMatchCriterion {
+ static final class AudioMixMatchCriterion {
final AudioAttributes mAttr;
- final Integer mIntProp;
+ final int mIntProp;
final int mRule;
/** input parameters must be valid */
- AttributeMatchCriterion(AudioAttributes attributes, int rule) {
+ AudioMixMatchCriterion(AudioAttributes attributes, int rule) {
mAttr = attributes;
- mIntProp = null;
+ mIntProp = Integer.MIN_VALUE;
mRule = rule;
}
/** input parameters must be valid */
- AttributeMatchCriterion(Integer intProp, int rule) {
+ AudioMixMatchCriterion(Integer intProp, int rule) {
mAttr = null;
- mIntProp = intProp;
+ mIntProp = intProp.intValue();
mRule = rule;
}
@@ -125,10 +125,10 @@ public class AudioMixingRule {
dest.writeInt(mAttr.getCapturePreset());
break;
case RULE_MATCH_UID:
- dest.writeInt(mIntProp.intValue());
+ dest.writeInt(mIntProp);
break;
default:
- Log.e("AttributeMatchCriterion", "Unknown match rule" + match_rule
+ Log.e("AudioMixMatchCriterion", "Unknown match rule" + match_rule
+ " when writing to Parcel");
dest.writeInt(-1);
}
@@ -137,8 +137,8 @@ public class AudioMixingRule {
private final int mTargetMixType;
int getTargetMixType() { return mTargetMixType; }
- private final ArrayList<AttributeMatchCriterion> mCriteria;
- ArrayList<AttributeMatchCriterion> getCriteria() { return mCriteria; }
+ private final ArrayList<AudioMixMatchCriterion> mCriteria;
+ ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
@Override
public int hashCode() {
@@ -205,7 +205,7 @@ public class AudioMixingRule {
*/
@SystemApi
public static class Builder {
- private ArrayList<AttributeMatchCriterion> mCriteria;
+ private ArrayList<AudioMixMatchCriterion> mCriteria;
private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
/**
@@ -213,7 +213,7 @@ public class AudioMixingRule {
*/
@SystemApi
public Builder() {
- mCriteria = new ArrayList<AttributeMatchCriterion>();
+ mCriteria = new ArrayList<AudioMixMatchCriterion>();
}
/**
@@ -378,10 +378,10 @@ public class AudioMixingRule {
throw new IllegalArgumentException("Incompatible rule for mix");
}
synchronized (mCriteria) {
- Iterator<AttributeMatchCriterion> crIterator = mCriteria.iterator();
+ Iterator<AudioMixMatchCriterion> crIterator = mCriteria.iterator();
final int match_rule = rule & ~RULE_EXCLUSION_MASK;
while (crIterator.hasNext()) {
- final AttributeMatchCriterion criterion = crIterator.next();
+ final AudioMixMatchCriterion criterion = crIterator.next();
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
// "usage"-based rule
@@ -413,7 +413,7 @@ public class AudioMixingRule {
break;
case RULE_MATCH_UID:
// "usage"-based rule
- if (criterion.mIntProp.intValue() == intProp.intValue()) {
+ if (criterion.mIntProp == intProp.intValue()) {
if (criterion.mRule == rule) {
// rule already exists, we're done
return this;
@@ -431,10 +431,10 @@ public class AudioMixingRule {
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule));
+ mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule));
break;
case RULE_MATCH_UID:
- mCriteria.add(new AttributeMatchCriterion(intProp, rule));
+ mCriteria.add(new AudioMixMatchCriterion(intProp, rule));
break;
default:
throw new IllegalStateException("Unreachable code in addRuleInternal()");
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 5ad61265f439..5d2bac02a5e0 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -17,7 +17,7 @@
package android.media.audiopolicy;
import android.media.AudioFormat;
-import android.media.audiopolicy.AudioMixingRule.AttributeMatchCriterion;
+import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -86,9 +86,9 @@ public class AudioPolicyConfig implements Parcelable {
dest.writeInt(mix.getFormat().getEncoding());
dest.writeInt(mix.getFormat().getChannelMask());
// write mix rules
- final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
+ final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
dest.writeInt(criteria.size());
- for (AttributeMatchCriterion criterion : criteria) {
+ for (AudioMixMatchCriterion criterion : criteria) {
criterion.writeToParcel(dest);
}
}
@@ -150,8 +150,8 @@ public class AudioPolicyConfig implements Parcelable {
textDump += " channels=0x";
textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() +"\n";
// write mix rules
- final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
- for (AttributeMatchCriterion criterion : criteria) {
+ final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
+ for (AudioMixMatchCriterion criterion : criteria) {
switch(criterion.mRule) {
case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE:
textDump += " exclude usage ";
@@ -171,11 +171,11 @@ public class AudioPolicyConfig implements Parcelable {
break;
case AudioMixingRule.RULE_MATCH_UID:
textDump += " match UID ";
- textDump += criterion.mIntProp.toString();
+ textDump += criterion.mIntProp;
break;
case AudioMixingRule.RULE_EXCLUDE_UID:
textDump += " exclude UID ";
- textDump += criterion.mIntProp.toString();
+ textDump += criterion.mIntProp;
break;
default:
textDump += "invalid rule!";
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1320e382a7ea..00083e650314 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -157,35 +157,22 @@ public final class TvInputManager {
public static final int RECORDING_ERROR_UNKNOWN = 0;
/**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * failed to establish a connection to a recording session.
- */
- public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
-
- /**
- * Error for {@link TvRecordingClient.RecordingCallback#onError(int)}: The recording client has
- * been disconnected from the current recording session.
- */
- public static final int RECORDING_ERROR_DISCONNECTED = 2;
-
- /**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
* insufficient storage space.
*/
- public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 3;
+ public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
* a required recording resource was not able to be allocated.
*/
- public static final int RECORDING_ERROR_RESOURCE_BUSY = 4;
+ public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
- RECORDING_ERROR_DISCONNECTED, RECORDING_ERROR_INSUFFICIENT_SPACE,
+ @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 334c84b1f35e..4ebd0fccc5c8 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1584,6 +1584,10 @@ public abstract class TvInputService extends Service {
* new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
* recorded program.
*
+ * <p>The recording session must call this method in response to {@link #onStopRecording()}.
+ * The session may call it even before receiving a call to {@link #onStopRecording()} if a
+ * partially recorded program is available when there is an error.
+ *
* @param recordedProgramUri The URI of the newly recorded program.
*/
public void notifyRecordingStopped(final Uri recordedProgramUri) {
@@ -1604,8 +1608,14 @@ public abstract class TvInputService extends Service {
}
/**
- * Informs the application that there is an error. It may be called at any time after this
- * recording session is created until {@link #onRelease()} is called.
+ * Informs the application that there is an error and this recording session is no longer
+ * able to start or continue recording. It may be called at any time after the recording
+ * session is created until {@link #onRelease()} is called.
+ *
+ * <p>The application may release the current session upon receiving the error code through
+ * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call
+ * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program
+ * is available, before calling this method.
*
* @param error The error code. Should be one of the followings.
* <ul>
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 72606f59f53b..1c920f57de2a 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -68,7 +68,7 @@ public class TvRecordingClient {
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -88,7 +88,7 @@ public class TvRecordingClient {
/**
* Tunes to a given channel for TV program recording. The first tune request will create a new
- * recording session for the corresponding TV input and establish the connection between the
+ * recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
* session, this method throws an exception.
*
@@ -226,6 +226,23 @@ public class TvRecordingClient {
*/
public abstract static class RecordingCallback {
/**
+ * This is called when an error occurred while establishing a connection to the recording
+ * session for the corresponding TV input.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onConnectionFailed(String inputId) {
+ }
+
+ /**
+ * This is called when the connection to the current recording session is lost.
+ *
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ */
+ public void onDisconnected(String inputId) {
+ }
+
+ /**
* This is called when the recording session has been tuned to the given channel and is
* ready to start recording.
*/
@@ -249,8 +266,6 @@ public class TvRecordingClient {
* @param error The error code. Should be one of the followings.
* <ul>
* <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
- * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
- * <li>{@link TvInputManager#RECORDING_ERROR_DISCONNECTED}
* <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
* <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
* </ul>
@@ -305,7 +320,9 @@ public class TvRecordingClient {
mSession.tune(mChannelUri, mConnectionParams);
} else {
mSessionCallback = null;
- mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+ if (mCallback != null) {
+ mCallback.onConnectionFailed(mInputId);
+ }
}
}
@@ -331,11 +348,13 @@ public class TvRecordingClient {
Log.w(TAG, "onSessionReleased - session not created");
return;
}
- mSessionCallback = null;
- mSession = null;
mIsTuned = false;
mIsRecordingStarted = false;
- mCallback.onError(TvInputManager.RECORDING_ERROR_DISCONNECTED);
+ mSessionCallback = null;
+ mSession = null;
+ if (mCallback != null) {
+ mCallback.onDisconnected(mInputId);
+ }
}
@Override
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ed432c463b8a..6a44b1e6e4e9 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -52,11 +52,14 @@ public final class TvTrackInfo implements Parcelable {
private final int mVideoHeight;
private final float mVideoFrameRate;
private final float mVideoPixelAspectRatio;
+ private final byte mVideoActiveFormatDescription;
+
private final Bundle mExtra;
private TvTrackInfo(int type, String id, String language, CharSequence description,
int audioChannelCount, int audioSampleRate, int videoWidth, int videoHeight,
- float videoFrameRate, float videoPixelAspectRatio, Bundle extra) {
+ float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
+ Bundle extra) {
mType = type;
mId = id;
mLanguage = language;
@@ -67,6 +70,7 @@ public final class TvTrackInfo implements Parcelable {
mVideoHeight = videoHeight;
mVideoFrameRate = videoFrameRate;
mVideoPixelAspectRatio = videoPixelAspectRatio;
+ mVideoActiveFormatDescription = videoActiveFormatDescription;
mExtra = extra;
}
@@ -81,6 +85,7 @@ public final class TvTrackInfo implements Parcelable {
mVideoHeight = in.readInt();
mVideoFrameRate = in.readFloat();
mVideoPixelAspectRatio = in.readFloat();
+ mVideoActiveFormatDescription = in.readByte();
mExtra = in.readBundle();
}
@@ -179,6 +184,20 @@ public final class TvTrackInfo implements Parcelable {
}
/**
+ * Returns the Active Format Description (AFD) code of the video.
+ * Valid only for {@link #TYPE_VIDEO} tracks.
+ *
+ * <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+ * 4 and SMPTE 2016-1-2007.
+ */
+ public final byte getVideoActiveFormatDescription() {
+ if (mType != TYPE_VIDEO) {
+ throw new IllegalStateException("Not a video track");
+ }
+ return mVideoActiveFormatDescription;
+ }
+
+ /**
* Returns the extra information about the current track.
*/
public final Bundle getExtra() {
@@ -208,6 +227,7 @@ public final class TvTrackInfo implements Parcelable {
dest.writeInt(mVideoHeight);
dest.writeFloat(mVideoFrameRate);
dest.writeFloat(mVideoPixelAspectRatio);
+ dest.writeByte(mVideoActiveFormatDescription);
dest.writeBundle(mExtra);
}
@@ -238,6 +258,7 @@ public final class TvTrackInfo implements Parcelable {
private int mVideoHeight;
private float mVideoFrameRate;
private float mVideoPixelAspectRatio = 1.0f;
+ private byte mVideoActiveFormatDescription;
private Bundle mExtra;
/**
@@ -368,6 +389,25 @@ public final class TvTrackInfo implements Parcelable {
}
/**
+ * Sets the Active Format Description (AFD) code of the video.
+ * Valid only for {@link #TYPE_VIDEO} tracks.
+ *
+ * <p>This is needed for applications to be able to scale the video properly based on the
+ * information about where in the coded picture the active video is.
+ * The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
+ * 4 and SMPTE 2016-1-2007.
+ *
+ * @param videoActiveFormatDescription The AFD code of the video.
+ */
+ public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
+ if (mType != TYPE_VIDEO) {
+ throw new IllegalStateException("Not a video track");
+ }
+ mVideoActiveFormatDescription = videoActiveFormatDescription;
+ return this;
+ }
+
+ /**
* Sets the extra information about the current track.
*
* @param extra The extra information.
@@ -385,7 +425,7 @@ public final class TvTrackInfo implements Parcelable {
public TvTrackInfo build() {
return new TvTrackInfo(mType, mId, mLanguage, mDescription, mAudioChannelCount,
mAudioSampleRate, mVideoWidth, mVideoHeight, mVideoFrameRate,
- mVideoPixelAspectRatio, mExtra);
+ mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
}
}
}
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index e414f4838583..35da84ce82e5 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -25,7 +25,9 @@
#include "JNIHelp.h"
#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
#include <media/ICrypto.h>
+#include <media/IMediaDrmService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -61,19 +63,30 @@ JCrypto::~JCrypto() {
// static
sp<ICrypto> JCrypto::MakeCrypto() {
sp<IServiceManager> sm = defaultServiceManager();
-
- sp<IBinder> binder =
- sm->getService(String16("media.player"));
-
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- if (service == NULL) {
- return NULL;
+ sp<ICrypto> crypto;
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.mediadrmservice.enable", value, NULL)
+ && (!strcmp("1", value) || !strcasecmp("true", value))) {
+ sp<IBinder> binder =
+ sm->getService(String16("media.drm"));
+ sp<IMediaDrmService> service =
+ interface_cast<IMediaDrmService>(binder);
+ if (service == NULL) {
+ return NULL;
+ }
+ crypto = service->makeCrypto();
+ } else {
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+ if (service == NULL) {
+ return NULL;
+ }
+ crypto = service->makeCrypto();
}
- sp<ICrypto> crypto = service->makeCrypto();
-
if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
return NULL;
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index b8849c6e1879..73ddedf2bec1 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -28,7 +28,9 @@
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
+#include <cutils/properties.h>
#include <media/IDrm.h>
+#include <media/IMediaDrmService.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaErrors.h>
@@ -352,19 +354,30 @@ JDrm::~JDrm() {
// static
sp<IDrm> JDrm::MakeDrm() {
sp<IServiceManager> sm = defaultServiceManager();
-
- sp<IBinder> binder =
- sm->getService(String16("media.player"));
-
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- if (service == NULL) {
- return NULL;
+ sp<IDrm> drm;
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.mediadrmservice.enable", value, NULL)
+ && (!strcmp("1", value) || !strcasecmp("true", value))) {
+ sp<IBinder> binder =
+ sm->getService(String16("media.drm"));
+ sp<IMediaDrmService> service =
+ interface_cast<IMediaDrmService>(binder);
+ if (service == NULL) {
+ return NULL;
+ }
+ drm = service->makeDrm();
+ } else {
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+ if (service == NULL) {
+ return NULL;
+ }
+ drm = service->makeDrm();
}
- sp<IDrm> drm = service->makeDrm();
-
if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
return NULL;
}
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 8414febfb7ca..84a928dc3d9b 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -16,11 +16,14 @@
<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key
+ callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/coordinator_layout">
+ android:id="@+id/coordinator_layout"
+ android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index d0364ff44294..0fb74e5eca34 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -83,7 +83,7 @@
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
- android:id="@+id/list"
+ android:id="@+id/dir_list"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index f3de3b43be50..b33b8d09b992 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/list"
+<com.android.documentsui.RootsList xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/roots_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="8dp"
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index f53d69808929..235d22d0737b 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -16,11 +16,14 @@
<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
floating action buttons) to operate correctly. -->
+<!-- focusableInTouchMode is set in order to force key events to go to the activity's global key
+ callback, which is necessary for proper event routing. See BaseActivity.onKeyDown. -->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/coordinator_layout">
+ android:id="@+id/coordinator_layout"
+ android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 475387bb224b..4a55906e56c1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -42,6 +42,7 @@ import android.support.annotation.CallSuper;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Spinner;
@@ -83,6 +84,8 @@ public abstract class BaseActivity extends Activity
// We use the time gap to figure out whether to close app or reopen the drawer.
private long mDrawerLastFiddled;
+ private boolean mNavDrawerHasFocus;
+
public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -580,6 +583,54 @@ public abstract class BaseActivity extends Activity
}
}
+ /**
+ * Declare a global key handler to route key events when there isn't a specific focus view. This
+ * covers the scenario where a user opens DocumentsUI and just starts typing.
+ *
+ * @param keyCode
+ * @param event
+ * @return
+ */
+ @CallSuper
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (Events.isNavigationKeyCode(keyCode)) {
+ // Forward all unclaimed navigation keystrokes to the DirectoryFragment. This causes any
+ // stray navigation keystrokes focus the content pane, which is probably what the user
+ // is trying to do.
+ DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+ if (df != null) {
+ df.requestFocus();
+ return true;
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_TAB) {
+ toggleNavDrawerFocus();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Toggles focus between the navigation drawer and the directory listing. If the drawer isn't
+ * locked, open/close it as appropriate.
+ */
+ void toggleNavDrawerFocus() {
+ if (mNavDrawerHasFocus) {
+ mDrawer.setOpen(false);
+ DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
+ if (df != null) {
+ df.requestFocus();
+ }
+ } else {
+ mDrawer.setOpen(true);
+ RootsFragment rf = RootsFragment.get(getFragmentManager());
+ if (rf != null) {
+ rf.requestFocus();
+ }
+ }
+ mNavDrawerHasFocus = !mNavDrawerHasFocus;
+ }
+
DocumentInfo getRootDocumentBlocking(RootInfo root) {
try {
final Uri uri = DocumentsContract.buildDocumentUri(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index ff1940aea94d..4cba1354d853 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -132,7 +132,7 @@ class NavigationView {
showBreadcrumb(true);
mToolbar.setTitle(null);
mIgnoreNextNavigation = true;
- mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1);
+ mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1, false);
}
if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle());
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 7dac0c10e077..0e2762291b09 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -83,7 +83,7 @@ public class RecentsCreateFragment extends Fragment {
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
- mRecView = (RecyclerView) view.findViewById(R.id.list);
+ mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecView.addOnItemTouchListener(mItemListener);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 26bda31261ef..53f82972738c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -89,7 +89,7 @@ public class RootsFragment extends Fragment {
final Context context = inflater.getContext();
final View view = inflater.inflate(R.layout.fragment_roots, container, false);
- mList = (ListView) view.findViewById(android.R.id.list);
+ mList = (ListView) view.findViewById(R.id.roots_list);
mList.setOnItemClickListener(mItemListener);
mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
return view;
@@ -167,6 +167,13 @@ public class RootsFragment extends Fragment {
}
}
+ /**
+ * Attempts to shift focus back to the navigation drawer.
+ */
+ public void requestFocus() {
+ mList.requestFocus();
+ }
+
private void showAppDetails(ResolveInfo ri) {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsList.java b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
new file mode 100644
index 000000000000..bf03ffd1e6d6
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsList.java
@@ -0,0 +1,63 @@
+/*
+ * 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.documentsui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.ListView;
+
+/**
+ * The list in the navigation drawer. This class exists for the purpose of overriding the key
+ * handler on ListView. Ignoring keystrokes (e.g. the tab key) cannot be properly done using
+ * View.OnKeyListener.
+ */
+public class RootsList extends ListView {
+
+ // Multiple constructors are needed to handle all the different ways this View could be
+ // constructed by the framework. Don't remove them!
+ public RootsList(Context context) {
+ super(context);
+ }
+
+ public RootsList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public RootsList(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public RootsList(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ // Ignore tab key events - this causes them to bubble up to the global key handler where
+ // they are appropriately handled. See BaseActivity.onKeyDown.
+ case KeyEvent.KEYCODE_TAB:
+ return false;
+ // Prevent left/right arrow keystrokes from shifting focus away from the roots list.
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return true;
+ default:
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 174984cec8ed..f8735b2f99a1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -101,7 +101,6 @@ import com.android.documentsui.model.RootInfo;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
-
import com.google.common.collect.Lists;
import java.lang.annotation.Retention;
@@ -183,7 +182,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
mEmptyView = view.findViewById(android.R.id.empty);
- mRecView = (RecyclerView) view.findViewById(R.id.list);
+ mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
mRecView.setRecyclerListener(
new RecyclerListener() {
@Override
@@ -194,6 +193,11 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
+ // Make the RecyclerView unfocusable. This is needed in order for the focus search code in
+ // FocusManager to work correctly. Setting android:focusable=false in the layout xml doesn't
+ // work, for some reason.
+ mRecView.setFocusable(false);
+
// TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDirectoryView(mRecView);
@@ -263,7 +267,8 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
mSelectionManager.addCallback(selectionListener);
- mFocusManager = new FocusManager(mRecView, mSelectionManager);
+ // Make sure this is done after the RecyclerView is set up.
+ mFocusManager = new FocusManager(mRecView);
mModel = new Model();
mModel.addUpdateListener(mAdapter);
@@ -834,6 +839,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
@Override
public void initDocumentHolder(DocumentHolder holder) {
holder.addEventListener(mItemEventListener);
+ holder.itemView.setOnFocusChangeListener(mFocusManager);
}
@Override
@@ -1054,6 +1060,13 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
}
}
+ /**
+ * Attempts to restore focus on the directory listing.
+ */
+ public void requestFocus() {
+ mFocusManager.restoreLastFocus();
+ }
+
private void setupDragAndDropOnDirectoryView(View view) {
// Listen for drops on non-directory items and empty space.
view.setOnDragListener(mOnDragListener);
@@ -1253,16 +1266,37 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
}
if (mFocusManager.handleKey(doc, keyCode, event)) {
+ // Handle range selection adjustments. Extending the selection will adjust the
+ // bounds of the in-progress range selection. Each time an unshifted navigation
+ // event is received, the range selection is restarted.
+ if (shouldExtendSelection(event)) {
+ if (!mSelectionManager.isRangeSelectionActive()) {
+ // Start a range selection if one isn't active
+ mSelectionManager.startRangeSelection(doc.getAdapterPosition());
+ }
+ mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
+ } else {
+ mSelectionManager.endRangeSelection();
+ }
return true;
}
// Handle enter key events
if (keyCode == KeyEvent.KEYCODE_ENTER) {
- return onActivate(doc);
+ if (event.isShiftPressed()) {
+ return onSelect(doc);
+ } else {
+ return onActivate(doc);
+ }
}
return false;
}
+
+ private boolean shouldExtendSelection(KeyEvent event) {
+ return Events.isNavigationKeyCode(event.getKeyCode()) &&
+ event.isShiftPressed();
+ }
}
private final class ModelUpdateListener implements Model.UpdateListener {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index 86b9146ba48c..93ec8426e74f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -16,7 +16,7 @@
package com.android.documentsui.dirlist;
-import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
@@ -27,19 +27,19 @@ import com.android.documentsui.Events;
/**
* A class that handles navigation and focus within the DirectoryFragment.
*/
-class FocusManager {
+class FocusManager implements View.OnFocusChangeListener {
private static final String TAG = "FocusManager";
private RecyclerView mView;
private RecyclerView.Adapter<?> mAdapter;
- private LinearLayoutManager mLayout;
- private MultiSelectManager mSelectionManager;
+ private GridLayoutManager mLayout;
- public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
+ private int mLastFocusPosition = RecyclerView.NO_POSITION;
+
+ public FocusManager(RecyclerView view) {
mView = view;
mAdapter = view.getAdapter();
- mLayout = (LinearLayoutManager) view.getLayoutManager();
- mSelectionManager = selectionManager;
+ mLayout = (GridLayoutManager) view.getLayoutManager();
}
/**
@@ -52,24 +52,58 @@ class FocusManager {
* @return Whether the event was handled.
*/
public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
- boolean handled = false;
+ boolean extendSelection = false;
+ // Translate space/shift-space into PgDn/PgUp
+ if (keyCode == KeyEvent.KEYCODE_SPACE) {
+ if (event.isShiftPressed()) {
+ keyCode = KeyEvent.KEYCODE_PAGE_UP;
+ } else {
+ keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
+ }
+ } else {
+ extendSelection = event.isShiftPressed();
+ }
+
if (Events.isNavigationKeyCode(keyCode)) {
// Find the target item and focus it.
int endPos = findTargetPosition(doc.itemView, keyCode, event);
if (endPos != RecyclerView.NO_POSITION) {
focusItem(endPos);
-
- // Handle any necessary adjustments to selection.
- boolean extendSelection = event.isShiftPressed();
- if (extendSelection) {
- int startPos = doc.getAdapterPosition();
- mSelectionManager.selectRange(startPos, endPos);
- }
- handled = true;
}
+ // Swallow all navigation keystrokes. Otherwise they go to the app's global
+ // key-handler, which will route them back to the DF and cause focus to be reset.
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Remember focus events on items.
+ if (hasFocus && v.getParent() == mView) {
+ mLastFocusPosition = mView.getChildAdapterPosition(v);
+ }
+ }
+
+ /**
+ * Requests focus on the item that last had focus. Scrolls to that item if necessary.
+ */
+ public void restoreLastFocus() {
+ if (mLastFocusPosition != RecyclerView.NO_POSITION) {
+ // The system takes care of situations when a view is no longer on screen, etc,
+ focusItem(mLastFocusPosition);
+ } else {
+ // Focus the first visible item
+ focusItem(mLayout.findFirstVisibleItemPosition());
}
- return handled;
+ }
+
+ /**
+ * @return The adapter position of the last focused item.
+ */
+ public int getFocusPosition() {
+ return mLastFocusPosition;
}
/**
@@ -100,12 +134,27 @@ class FocusManager {
case KeyEvent.KEYCODE_DPAD_DOWN:
searchDir = View.FOCUS_DOWN;
break;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- searchDir = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- searchDir = View.FOCUS_RIGHT;
- break;
+ }
+
+ if (inGridMode()) {
+ int currentPosition = mView.getChildAdapterPosition(view);
+ // Left and right arrow keys only work in grid mode.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (currentPosition > 0) {
+ // Stop backward focus search at the first item, otherwise focus will wrap
+ // around to the last visible item.
+ searchDir = View.FOCUS_BACKWARD;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (currentPosition < mAdapter.getItemCount() - 1) {
+ // Stop forward focus search at the last item, otherwise focus will wrap
+ // around to the first visible item.
+ searchDir = View.FOCUS_FORWARD;
+ }
+ break;
+ }
}
if (searchDir != -1) {
@@ -204,4 +253,11 @@ class FocusManager {
});
}
}
+
+ /**
+ * @return Whether the layout manager is currently in a grid-configuration.
+ */
+ private boolean inGridMode() {
+ return mLayout.getSpanCount() > 1;
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index d60825baa7cf..c8b6f8528272 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -370,39 +370,41 @@ public final class MultiSelectManager {
}
/**
- * Handle a range selection event.
- * <li> If the MSM is currently in single-select mode, only the last item in the range will
- * actually be selected.
- * <li>If a range selection is not already active, one will be started, and the given range of
- * items will be selected. The given startPos becomes the anchor for the range selection.
- * <li>If a range selection is already active, the anchor is not changed. The range is extended
- * from its current anchor to endPos.
+ * Starts a range selection. If a range selection is already active, this will start a new range
+ * selection (which will reset the range anchor).
*
- * @param startPos
- * @param endPos
+ * @param pos The anchor position for the selection range.
*/
- public void selectRange(int startPos, int endPos) {
- // In single-select mode, just select the last item in the range.
- if (mSingleSelect) {
- attemptSelect(mAdapter.getModelId(endPos));
- return;
- }
+ void startRangeSelection(int pos) {
+ attemptSelect(mAdapter.getModelId(pos));
+ setSelectionRangeBegin(pos);
+ }
- // In regular (i.e. multi-select) mode
- if (!isRangeSelectionActive()) {
- // If a range selection isn't active, start one up
- attemptSelect(mAdapter.getModelId(startPos));
- setSelectionRangeBegin(startPos);
- }
- // Extend the range selection
- mRanger.snapSelection(endPos);
+ /**
+ * Sets the end point for the current range selection, started by a call to
+ * {@link #startRangeSelection(int)}. This function should only be called when a range selection
+ * is active (see {@link #isRangeSelectionActive()}. Items in the range [anchor, end] will be
+ * selected.
+ *
+ * @param pos The new end position for the selection range.
+ */
+ void snapRangeSelection(int pos) {
+ checkNotNull(mRanger);
+ mRanger.snapSelection(pos);
notifySelectionChanged();
}
/**
+ * Stops an in-progress range selection.
+ */
+ void endRangeSelection() {
+ mRanger = null;
+ }
+
+ /**
* @return Whether or not there is a current range selection active.
*/
- private boolean isRangeSelectionActive() {
+ boolean isRangeSelectionActive() {
return mRanger != null;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 77f16d9ca9e6..609dc0c67e50 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -21,6 +21,7 @@ import static com.android.documentsui.StubProvider.ROOT_1_ID;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
@LargeTest
public class FilesActivityUiTest extends ActivityTest<FilesActivity> {
@@ -115,4 +116,37 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> {
bot.waitForDeleteSnackbarGone();
assertFalse(bot.hasDocuments("poodles.text"));
}
+
+ // Tests that pressing tab switches focus between the roots and directory listings.
+ public void testKeyboard_tab() throws Exception {
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+
+ // Tests that arrow keys do not switch focus away from the dir list.
+ public void testKeyboard_arrowsDirList() throws Exception {
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ bot.assertHasFocus("com.android.documentsui:id/dir_list");
+ }
+ }
+
+ // Tests that arrow keys do not switch focus away from the roots list.
+ public void testKeyboard_arrowsRootsList() throws Exception {
+ bot.pressKey(KeyEvent.KEYCODE_TAB);
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ }
+ for (int i = 0; i < 10; i++) {
+ bot.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ bot.assertHasFocus("com.android.documentsui:id/roots_list");
+ }
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
index 1069a660862d..770bc2c2fd4b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -81,7 +81,6 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> {
bot.openOverflowMenu();
bot.openDialog(R.string.menu_rename);
bot.setDialogText(newName);
- bot.dismissKeyboardIfPresent();
device.waitForIdle(TIMEOUT);
bot.findRenameDialogOkButton().click();
@@ -110,7 +109,6 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> {
bot.openOverflowMenu();
bot.openDialog(R.string.menu_rename);
bot.setDialogText(newName);
- bot.dismissKeyboardIfPresent();
device.waitForIdle(TIMEOUT);
bot.findRenameDialogCancelButton().click();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index 4534c40c6e0e..d2f84034593c 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -71,7 +71,7 @@ class UiBot {
UiObject findRoot(String label) throws UiObjectNotFoundException {
final UiSelector rootsList = new UiSelector().resourceId(
"com.android.documentsui:id/container_roots").childSelector(
- new UiSelector().resourceId("android:id/list"));
+ new UiSelector().resourceId("com.android.documentsui:id/roots_list"));
// We might need to expand drawer if not visible
if (!new UiObject(rootsList).waitForExists(mTimeout)) {
@@ -195,6 +195,15 @@ class UiBot {
assertNotNull(getSnackbar(mContext.getString(id)));
}
+ /**
+ * Asserts that the specified view or one of its descendents has focus.
+ */
+ void assertHasFocus(String resourceName) {
+ UiObject2 candidate = mDevice.findObject(By.res(resourceName));
+ assertNotNull("Expected " + resourceName + " to have focus, but it didn't.",
+ candidate.findObject(By.focused(true)));
+ }
+
void openDocument(String label) throws UiObjectNotFoundException {
int toolType = Configurator.getInstance().getToolType();
Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
@@ -309,7 +318,7 @@ class UiBot {
UiObject findDocument(String label) throws UiObjectNotFoundException {
final UiSelector docList = new UiSelector().resourceId(
"com.android.documentsui:id/container_directory").childSelector(
- new UiSelector().resourceId("com.android.documentsui:id/list"));
+ new UiSelector().resourceId("com.android.documentsui:id/dir_list"));
// Wait for the first list item to appear
new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
@@ -330,7 +339,7 @@ class UiBot {
UiObject findDocumentsList() {
return findObject(
"com.android.documentsui:id/container_directory",
- "com.android.documentsui:id/list");
+ "com.android.documentsui:id/dir_list");
}
UiObject findSearchView() {
@@ -416,4 +425,8 @@ class UiBot {
mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), mTimeout);
mDevice.waitForIdle();
}
+
+ void pressKey(int keyCode) {
+ mDevice.pressKeyCode(keyCode);
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index d95fb490d81e..9447d9c18a83 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -189,6 +189,54 @@ public class MultiSelectManagerTest extends AndroidTestCase {
assertSelection(items.get(20));
}
+ public void testRangeSelection() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapExpand() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(19);
+ mManager.snapRangeSelection(27);
+ assertRangeSelection(15, 27);
+ }
+
+ public void testRangeSelection_snapContract() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(19);
+ assertRangeSelection(15, 19);
+ }
+
+ public void testRangeSelection_snapInvert() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.snapRangeSelection(3);
+ assertRangeSelection(3, 15);
+ }
+
+ public void testRangeSelection_multiple() {
+ mManager.startRangeSelection(15);
+ mManager.snapRangeSelection(27);
+ mManager.endRangeSelection();
+ mManager.startRangeSelection(42);
+ mManager.snapRangeSelection(57);
+ assertSelectionSize(29);
+ assertRangeSelected(15, 27);
+ assertRangeSelected(42, 57);
+
+ }
+
+ public void testRangeSelection_singleSelect() {
+ mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null);
+ mManager.addCallback(mCallback);
+ mManager.startRangeSelection(11);
+ mManager.snapRangeSelection(19);
+ assertSelectionSize(1);
+ assertSelection(items.get(19));
+ }
+
public void testProvisionalSelection() {
Selection s = mManager.getSelection();
assertSelection();
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index 43a420cf6d97..f3a3fcf0563c 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -25,4 +25,8 @@
<string name="accessing_notification_title">Accessing files from <xliff:g id="device_model" example="Nexus 9">%1$s</xliff:g></string>
<!-- Description of notification showing Files app is accessing files in a MTP device. [CHAR LIMIT=60]-->
<string name="accessing_notification_description">Don\'t disconnect the device</string>
+ <!-- Error message shown in Files app when the connected MTP device is busy. [CHAR LIMIT=150]-->
+ <string name="error_busy_device">The other device is busy. You can\'t transfer files until it\'s available.</string>
+ <!-- Error message shown in Files app when the connected MTP device may be locked. [CHAR LIMIT=150]-->
+ <string name="error_locked_device">No files found. The other device may be locked. If so, unlock it and try again.</string>
</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index c456be93d5a3..5a1132721e77 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -37,6 +37,7 @@ import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.FileNotFoundException;
import java.util.Objects;
@@ -244,15 +245,16 @@ class MtpDatabase {
}
/**
- * Returns identifier of single storage if given document points device and it has only one
- * storage. Otherwise null.
+ * Returns document IDs of storages under the given device document.
*
- * @param documentId Document ID that may point a device.
- * @return Identifier for single storage or null.
+ * @param documentId Document ID that points a device.
+ * @return Storage document IDs.
* @throws FileNotFoundException The given document ID is not registered in database.
*/
- @Nullable Identifier getSingleStorageIdentifier(String documentId)
+ String[] getStorageDocumentIds(String documentId)
throws FileNotFoundException {
+ Preconditions.checkArgument(createIdentifier(documentId).mDocumentType ==
+ DOCUMENT_TYPE_DEVICE);
// Check if the parent document is device that has single storage.
try (final Cursor cursor = mDatabase.query(
TABLE_DOCUMENTS,
@@ -267,12 +269,11 @@ class MtpDatabase {
null,
null,
null)) {
- if (cursor.getCount() == 1) {
- cursor.moveToNext();
- return createIdentifier(cursor.getString(0));
- } else {
- return null;
+ final String[] ids = new String[cursor.getCount()];
+ for (int i = 0; cursor.moveToNext(); i++) {
+ ids[i] = cursor.getString(0);
}
+ return ids;
}
}
@@ -342,6 +343,26 @@ class MtpDatabase {
}
}
+ String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ try (final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ COLUMN_DEVICE_ID + " = ? AND " + COLUMN_DOCUMENT_TYPE + " = ? AND " +
+ COLUMN_ROW_STATE + " != ?",
+ strings(deviceId, DOCUMENT_TYPE_DEVICE, ROW_STATE_DISCONNECTED),
+ null,
+ null,
+ null,
+ "1")) {
+ if (cursor.getCount() > 0) {
+ cursor.moveToNext();
+ return cursor.getString(0);
+ } else {
+ throw new FileNotFoundException("The device ID not found: " + deviceId);
+ }
+ }
+ }
+
/**
* Adds new document under the parent.
* The method does not affect invalidated and pending documents because we know the document is
@@ -557,13 +578,9 @@ class MtpDatabase {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 1) {
- db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
- db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
- onCreate(db);
- return;
- }
- throw new UnsupportedOperationException();
+ db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
+ db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
+ onCreate(db);
}
}
@@ -680,7 +697,12 @@ class MtpDatabase {
if (formatCodeMimeType != null) {
return formatCodeMimeType;
}
- return MediaFile.getMimeTypeForFile(info.getName());
+ final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
+ if (mediaFileMimeType != null) {
+ return mediaFileMimeType;
+ }
+ // We don't know the file type.
+ return "application/octet-stream";
}
static String[] strings(Object... args) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index cb076af118b4..ab356ceba1c2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -30,7 +30,7 @@ import java.util.Map;
* Class containing MtpDatabase constants.
*/
class MtpDatabaseConstants {
- static final int DATABASE_VERSION = 2;
+ static final int DATABASE_VERSION = 3;
static final String DATABASE_NAME = "database";
static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -125,7 +125,7 @@ class MtpDatabaseConstants {
COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
COLUMN_ROW_STATE + " INTEGER NOT NULL," +
COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
- Document.COLUMN_MIME_TYPE + " TEXT," +
+ Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
Document.COLUMN_SUMMARY + " TEXT," +
Document.COLUMN_LAST_MODIFIED + " INTEGER," +
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 033845401b4b..a51250943404 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -20,10 +20,12 @@ import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Point;
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
@@ -35,6 +37,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -163,17 +166,25 @@ public class MtpDocumentsProvider extends DocumentsProvider {
try {
openDevice(parentIdentifier.mDeviceId);
if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
- final Identifier singleStorageIdentifier =
- mDatabase.getSingleStorageIdentifier(parentDocumentId);
- if (singleStorageIdentifier == null) {
+ final String[] storageDocIds = mDatabase.getStorageDocumentIds(parentDocumentId);
+ if (storageDocIds.length == 0) {
+ // Remote device does not provide storages. Maybe it is locked.
+ return createErrorCursor(projection, R.string.error_locked_device);
+ } else if (storageDocIds.length > 1) {
// Returns storage list from database.
return mDatabase.queryChildDocuments(projection, parentDocumentId);
}
- parentIdentifier = singleStorageIdentifier;
+
+ // Exact one storage is found. Skip storage and returns object in the single
+ // storage.
+ parentIdentifier = mDatabase.createIdentifier(storageDocIds[0]);
}
+
// Returns object list from document loader.
return getDocumentLoader(parentIdentifier).queryChildDocuments(
projection, parentIdentifier);
+ } catch (BusyDeviceException exception) {
+ return createErrorCursor(projection, R.string.error_busy_device);
} catch (IOException exception) {
Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception);
throw new FileNotFoundException(exception.getMessage());
@@ -350,6 +361,16 @@ public class MtpDocumentsProvider extends DocumentsProvider {
}
/**
+ * Obtains document ID for the given device ID.
+ * @param deviceId
+ * @return document ID
+ * @throws FileNotFoundException device ID has not been build.
+ */
+ public String getDeviceDocumentId(int deviceId) throws FileNotFoundException {
+ return mDatabase.getDeviceDocumentId(deviceId);
+ }
+
+ /**
* Resumes root scanner to handle the update of device list.
*/
void resumeRootScanner() {
@@ -442,6 +463,21 @@ public class MtpDocumentsProvider extends DocumentsProvider {
}
}
+ /**
+ * Creates empty cursor with specific error message.
+ *
+ * @param projection Column names.
+ * @param stringResId String resource ID of error message.
+ * @return Empty cursor with error message.
+ */
+ private Cursor createErrorCursor(String[] projection, int stringResId) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(DocumentsContract.EXTRA_ERROR, mResources.getString(stringResId));
+ final Cursor cursor = new MatrixCursor(projection);
+ cursor.setExtras(bundle);
+ return cursor;
+ }
+
private static class DeviceToolkit {
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 5519efd5a323..0527790470fb 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.mtp.exceptions.BusyDeviceException;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -101,7 +102,8 @@ class MtpManager {
}
if (!device.open(connection)) {
- throw new IOException("Failed to open a MTP device.");
+ // We cannot open connection when another application use the device.
+ throw new BusyDeviceException();
}
// Handle devices that fail to obtain storages just after opening a MTP session.
@@ -134,7 +136,7 @@ class MtpManager {
try {
roots = getRoots(device.getDeviceId());
} catch (IOException exp) {
- Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
// If we failed to fetch roots for the device, we still returns device model
// with an empty set of roots so that the device is shown DocumentsUI as long as
// the device is physically connected.
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
index c7206a7929e0..84745b29e428 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
@@ -17,10 +17,15 @@
package com.android.mtp;
import android.app.Activity;
-import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import java.io.IOException;
/**
* Invisible activity to receive intents.
@@ -33,14 +38,21 @@ public class ReceiverActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
- // TODO: To obtain data URI for the attached device, we need to wait until RootScanner
- // found the device and add it to database. Set correct root URI, and use ACTION_BROWSE
- // to launch Documents UI.
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(new ComponentName(
- "com.android.documentsui", "com.android.documentsui.LauncherActivity"));
- this.startActivity(intent);
+ final UsbDevice device = getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ try {
+ final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+ provider.openDevice(device.getDeviceId());
+ final String deviceRootId = provider.getDeviceDocumentId(device.getDeviceId());
+ final Uri uri = DocumentsContract.buildRootUri(
+ MtpDocumentsProvider.AUTHORITY, deviceRootId);
+
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+ intent.setData(uri);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ this.startActivity(intent);
+ } catch (IOException exception) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exception);
+ }
}
finish();
}
diff --git a/tools/aapt2/util/Comparators.h b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
index 0ee0bf35457d..55f55b0edbc3 100644
--- a/tools/aapt2/util/Comparators.h
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/exceptions/BusyDeviceException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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,24 +14,12 @@
* limitations under the License.
*/
-#ifndef AAPT_UTIL_COMPARATORS_H
-#define AAPT_UTIL_COMPARATORS_H
+package com.android.mtp.exceptions;
-#include "ConfigDescription.h"
-#include "ResourceTable.h"
+import java.io.IOException;
-namespace aapt {
-namespace cmp {
-
-inline bool lessThanConfig(const ResourceConfigValue& a, const ConfigDescription& b) {
- return a.config < b;
-}
-
-inline bool lessThanType(const std::unique_ptr<ResourceTableType>& a, ResourceType b) {
- return a->type < b;
+/**
+ * Exception thrown when the device is busy and the requested operation cannon be completed.
+ */
+public class BusyDeviceException extends IOException {
}
-
-} // namespace cmp
-} // namespace aapt
-
-#endif /* AAPT_UTIL_COMPARATORS_H */
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 5b0f55703a2b..01fcc55d58f0 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -29,6 +29,8 @@ import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.mtp.exceptions.BusyDeviceException;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -526,6 +528,52 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
}
+ public void testBusyDevice() throws Exception {
+ mMtpManager = new TestMtpManager(getContext()) {
+ @Override
+ void openDevice(int deviceId) throws IOException {
+ throw new BusyDeviceException();
+ }
+ };
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_busy_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
+ public void testLockedDevice() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
+
+ mProvider.resumeRootScanner();
+ mResolver.waitForNotification(ROOTS_URI, 1);
+
+ try (final Cursor cursor = mProvider.queryRoots(null)) {
+ assertEquals(1, cursor.getCount());
+ }
+
+ try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+ assertEquals(0, cursor.getCount());
+ assertEquals(
+ "error_locked_device",
+ cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
+ }
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
index b23038b6c3f6..8676b5a0d315 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -24,6 +24,10 @@ class TestResources extends MockResources {
switch (id) {
case R.string.root_name:
return "%1$s %2$s";
+ case R.string.error_busy_device:
+ return "error_busy_device";
+ case R.string.error_locked_device:
+ return "error_locked_device";
}
throw new NotFoundException();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index 73171c70d856..1d6197a9333f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -65,7 +65,7 @@ public class SettingsDrawerAdapter extends BaseAdapter {
}
public Tile getTile(int position) {
- return mItems.get(position).tile;
+ return mItems.get(position) != null ? mItems.get(position).tile : null;
}
@Override
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c555a655bef..bad7e202e1d1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -88,8 +88,8 @@ import android.widget.Toast;
* <p>
* The workflow is:
* <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the
- * estimated total effort.
+ * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
+ * its pid, and the estimated total effort.
* <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
* <li>Upon start, this service:
* <ol>
@@ -132,6 +132,7 @@ public class BugreportProgressService extends Service {
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+ static final String EXTRA_ID = "android.intent.extra.ID";
static final String EXTRA_PID = "android.intent.extra.PID";
static final String EXTRA_MAX = "android.intent.extra.MAX";
static final String EXTRA_NAME = "android.intent.extra.NAME";
@@ -177,7 +178,7 @@ public class BugreportProgressService extends Service {
*/
private static final String SCREENSHOT_DIR = "bugreports";
- /** Managed dumpstate processes (keyed by pid) */
+ /** Managed dumpstate processes (keyed by id) */
private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
private Context mContext;
@@ -222,7 +223,7 @@ public class BugreportProgressService extends Service {
}
// If service is killed it cannot be recreated because it would not know which
- // dumpstate PIDs it would have to watch.
+ // dumpstate IDs it would have to watch.
return START_NOT_STICKY;
}
@@ -299,38 +300,41 @@ public class BugreportProgressService extends Service {
}
final String action = intent.getAction();
final int pid = intent.getIntExtra(EXTRA_PID, 0);
+ // TODO: temporarily using pid as id until test cases and dumpstate are changed.
+ final int id = intent.getIntExtra(EXTRA_ID, pid);
final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
- if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
- + ", max: "+ max);
+ if (DEBUG)
+ Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
+ + pid + ", max: " + max);
switch (action) {
case INTENT_BUGREPORT_STARTED:
- if (!startProgress(name, pid, max)) {
+ if (!startProgress(name, id, pid, max)) {
stopSelfWhenDone();
return;
}
poll();
break;
case INTENT_BUGREPORT_FINISHED:
- if (pid == 0) {
+ if (id == 0) {
// Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
// out-of-sync dumpstate process.
- Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
+ Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
}
- onBugreportFinished(pid, intent);
+ onBugreportFinished(id, intent);
break;
case INTENT_BUGREPORT_INFO_LAUNCH:
- launchBugreportInfoDialog(pid);
+ launchBugreportInfoDialog(id);
break;
case INTENT_BUGREPORT_SCREENSHOT:
- takeScreenshot(pid, true);
+ takeScreenshot(id, true);
break;
case INTENT_BUGREPORT_SHARE:
- shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
+ shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
break;
case INTENT_BUGREPORT_CANCEL:
- cancel(pid);
+ cancel(id);
break;
default:
Log.w(TAG, "Unsupported intent: " + action);
@@ -367,10 +371,10 @@ public class BugreportProgressService extends Service {
}
}
- private BugreportInfo getInfo(int pid) {
- final BugreportInfo info = mProcesses.get(pid);
+ private BugreportInfo getInfo(int id) {
+ final BugreportInfo info = mProcesses.get(id);
if (info == null) {
- Log.w(TAG, "Not monitoring process with PID " + pid);
+ Log.w(TAG, "Not monitoring process with ID " + id);
}
return info;
}
@@ -381,10 +385,14 @@ public class BugreportProgressService extends Service {
*
* @return whether it succeeded or not.
*/
- private boolean startProgress(String name, int pid, int max) {
+ private boolean startProgress(String name, int id, int pid, int max) {
if (name == null) {
Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
}
+ if (id == -1) {
+ Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
+ return false;
+ }
if (pid == -1) {
Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
return false;
@@ -394,14 +402,14 @@ public class BugreportProgressService extends Service {
return false;
}
- final BugreportInfo info = new BugreportInfo(mContext, pid, name, max);
- if (mProcesses.indexOfKey(pid) >= 0) {
- Log.w(TAG, "PID " + pid + " already watched");
+ final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
+ if (mProcesses.indexOfKey(id) >= 0) {
+ Log.w(TAG, "ID " + id + " already watched");
} else {
- mProcesses.put(info.pid, info);
+ mProcesses.put(info.id, info);
}
// Take initial screenshot.
- takeScreenshot(pid, false);
+ takeScreenshot(id, false);
updateProgress(info);
return true;
}
@@ -423,22 +431,22 @@ public class BugreportProgressService extends Service {
com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
- infoIntent.putExtra(EXTRA_PID, info.pid);
+ infoIntent.putExtra(EXTRA_ID, info.id);
final Action infoAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_info_action),
- PendingIntent.getService(mContext, info.pid, infoIntent,
+ PendingIntent.getService(mContext, info.id, infoIntent,
PendingIntent.FLAG_UPDATE_CURRENT)).build();
final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
- screenshotIntent.putExtra(EXTRA_PID, info.pid);
+ screenshotIntent.putExtra(EXTRA_ID, info.id);
PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent
- .getService(mContext, info.pid, screenshotIntent,
+ .getService(mContext, info.id, screenshotIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
final Action screenshotAction = new Action.Builder(null,
mContext.getString(R.string.bugreport_screenshot_action),
screenshotPendingIntent).build();
- final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
+ final String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
final String name =
info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -464,8 +472,8 @@ public class BugreportProgressService extends Service {
+ info + ")");
return;
}
- Log.v(TAG, "Sending 'Progress' notification for pid " + info.pid + ": " + percentText);
- NotificationManager.from(mContext).notify(TAG, info.pid, notification);
+ Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+ NotificationManager.from(mContext).notify(TAG, info.id, notification);
}
/**
@@ -474,38 +482,38 @@ public class BugreportProgressService extends Service {
private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
intent.setClass(context, BugreportProgressService.class);
- intent.putExtra(EXTRA_PID, info.pid);
- return PendingIntent.getService(context, info.pid, intent,
+ intent.putExtra(EXTRA_ID, info.id);
+ return PendingIntent.getService(context, info.id, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
- private void stopProgress(int pid) {
- if (mProcesses.indexOfKey(pid) < 0) {
- Log.w(TAG, "PID not watched: " + pid);
+ private void stopProgress(int id) {
+ if (mProcesses.indexOfKey(id) < 0) {
+ Log.w(TAG, "ID not watched: " + id);
} else {
- Log.d(TAG, "Removing PID " + pid);
- mProcesses.remove(pid);
+ Log.d(TAG, "Removing ID " + id);
+ mProcesses.remove(id);
}
stopSelfWhenDone();
- Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
- NotificationManager.from(mContext).cancel(TAG, pid);
+ Log.v(TAG, "stopProgress(" + id + "): cancel notification");
+ NotificationManager.from(mContext).cancel(TAG, id);
}
/**
* Cancels a bugreport upon user's request.
*/
- private void cancel(int pid) {
- Log.v(TAG, "cancel: pid=" + pid);
- final BugreportInfo info = getInfo(pid);
+ private void cancel(int id) {
+ Log.v(TAG, "cancel: ID=" + id);
+ final BugreportInfo info = getInfo(id);
if (info != null && !info.finished) {
- Log.i(TAG, "Cancelling bugreport service (pid=" + pid + ") on user's request");
+ Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
deleteScreenshots(info);
}
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -522,14 +530,15 @@ public class BugreportProgressService extends Service {
for (int i = 0; i < total; i++) {
final BugreportInfo info = mProcesses.valueAt(i);
if (info == null) {
- Log.wtf(TAG, "pollProgress(): null info at index " + i + "(pid = "
+ Log.wtf(TAG, "pollProgress(): null info at index " + i + "(ID = "
+ mProcesses.keyAt(i) + ")");
continue;
}
final int pid = info.pid;
+ final int id = info.id;
if (info.finished) {
- if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+ if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
continue;
}
activeProcesses++;
@@ -544,13 +553,13 @@ public class BugreportProgressService extends Service {
if (progressChanged || maxChanged) {
if (progressChanged) {
- if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from "
- + info.progress + " to " + progress);
+ if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id
+ + ") from " + info.progress + " to " + progress);
info.progress = progress;
}
if (maxChanged) {
- Log.i(TAG, "Updating max progress for PID " + pid + " from " + info.max
- + " to " + max);
+ Log.i(TAG, "Updating max progress for PID " + pid + "(id: " + id
+ + ") from " + info.max + " to " + max);
info.max = max;
}
info.lastUpdate = System.currentTimeMillis();
@@ -558,9 +567,9 @@ public class BugreportProgressService extends Service {
} else {
long inactiveTime = System.currentTimeMillis() - info.lastUpdate;
if (inactiveTime >= INACTIVITY_TIMEOUT) {
- Log.w(TAG, "No progress update for process " + pid + " since "
+ Log.w(TAG, "No progress update for PID " + pid + " since "
+ info.getFormattedLastUpdate());
- stopProgress(info.pid);
+ stopProgress(info.id);
}
}
}
@@ -572,19 +581,16 @@ public class BugreportProgressService extends Service {
* Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can
* change its values.
*/
- private void launchBugreportInfoDialog(int pid) {
+ private void launchBugreportInfoDialog(int id) {
// Copy values so it doesn't lock mProcesses while UI is being updated
final String name, title, description;
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
- name = info.name;
- title = info.title;
- description = info.description;
collapseNotificationBar();
- mInfoDialog.initialize(mContext, pid, name, title, description);
+ mInfoDialog.initialize(mContext, info);
}
/**
@@ -597,7 +603,7 @@ public class BugreportProgressService extends Service {
* Typical usage is delaying when taken from the notification action, and taking it right away
* upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
*/
- private void takeScreenshot(int pid, boolean delayed) {
+ private void takeScreenshot(int id, boolean delayed) {
setTakingScreenshot(true);
if (delayed) {
collapseNotificationBar();
@@ -608,28 +614,28 @@ public class BugreportProgressService extends Service {
// Show a toast just once, otherwise it might be captured in the screenshot.
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
- takeScreenshot(pid, SCREENSHOT_DELAY_SECONDS);
+ takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
} else {
- takeScreenshot(pid, 0);
+ takeScreenshot(id, 0);
}
}
/**
* Takes a screenshot after {@code delay} seconds.
*/
- private void takeScreenshot(int pid, int delay) {
+ private void takeScreenshot(int id, int delay) {
if (delay > 0) {
- Log.d(TAG, "Taking screenshot for " + pid + " in " + delay + " seconds");
+ Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
final Message msg = mMainHandler.obtainMessage();
msg.what = MSG_DELAYED_SCREENSHOT;
- msg.arg1 = pid;
+ msg.arg1 = id;
msg.arg2 = delay - 1;
mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
return;
}
// It's time to take the screenshot: let the proper thread handle it
- final BugreportInfo info = getInfo(pid);
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -638,7 +644,7 @@ public class BugreportProgressService extends Service {
final Message requestMsg = new Message();
requestMsg.what = MSG_SCREENSHOT_REQUEST;
- requestMsg.arg1 = pid;
+ requestMsg.arg1 = id;
requestMsg.obj = screenshotPath;
mScreenshotHandler.sendMessage(requestMsg);
}
@@ -715,30 +721,30 @@ public class BugreportProgressService extends Service {
*/
private void stopSelfWhenDone() {
if (mProcesses.size() > 0) {
- if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses);
+ if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses);
return;
}
- Log.v(TAG, "No more pids to handle, shutting down");
+ Log.v(TAG, "No more processes to handle, shutting down");
stopSelf();
}
/**
* Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
*/
- private void onBugreportFinished(int pid, Intent intent) {
+ private void onBugreportFinished(int id, Intent intent) {
final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
if (bugreportFile == null) {
// Should never happen, dumpstate always set the file.
Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
return;
}
- mInfoDialog.onBugreportFinished(pid);
- BugreportInfo info = getInfo(pid);
+ mInfoDialog.onBugreportFinished(id);
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
- Log.v(TAG, "Creating info for untracked pid " + pid);
- info = new BugreportInfo(mContext, pid);
- mProcesses.put(pid, info);
+ Log.v(TAG, "Creating info for untracked ID " + id);
+ info = new BugreportInfo(mContext, id);
+ mProcesses.put(id, info);
}
info.renameScreenshots(mScreenshotsDir);
info.bugreportFile = bugreportFile;
@@ -765,7 +771,7 @@ public class BugreportProgressService extends Service {
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
- stopProgress(info.pid);
+ stopProgress(info.id);
return;
}
@@ -837,12 +843,12 @@ public class BugreportProgressService extends Service {
* Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
* intent, but issuing a warning dialog the first time.
*/
- private void shareBugreport(int pid, BugreportInfo sharedInfo) {
- BugreportInfo info = getInfo(pid);
+ private void shareBugreport(int id, BugreportInfo sharedInfo) {
+ BugreportInfo info = getInfo(id);
if (info == null) {
// Service was terminated but notification persisted
info = sharedInfo;
- Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+ Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+ mProcesses + "), using info from intent instead (" + info + ")");
}
@@ -863,7 +869,7 @@ public class BugreportProgressService extends Service {
mContext.startActivity(notifIntent);
// ... and stop watching this process.
- stopProgress(pid);
+ stopProgress(id);
}
/**
@@ -877,16 +883,16 @@ public class BugreportProgressService extends Service {
final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
shareIntent.setClass(context, BugreportProgressService.class);
shareIntent.setAction(INTENT_BUGREPORT_SHARE);
- shareIntent.putExtra(EXTRA_PID, info.pid);
+ shareIntent.putExtra(EXTRA_ID, info.id);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title = context.getString(R.string.bugreport_finished_title, info.pid);
+ final String title = context.getString(R.string.bugreport_finished_title, info.id);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
.setTicker(title)
.setContentText(context.getString(R.string.bugreport_finished_text))
- .setContentIntent(PendingIntent.getService(context, info.pid, shareIntent,
+ .setContentIntent(PendingIntent.getService(context, info.id, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(newCancelIntent(context, info))
.setLocalOnly(true)
@@ -897,8 +903,8 @@ public class BugreportProgressService extends Service {
builder.setContentInfo(info.name);
}
- Log.v(TAG, "Sending 'Share' notification for pid " + info.pid + ": " + title);
- NotificationManager.from(context).notify(TAG, info.pid, builder.build());
+ Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ NotificationManager.from(context).notify(TAG, info.id, builder.build());
}
/**
@@ -906,7 +912,7 @@ public class BugreportProgressService extends Service {
* finishes - at this point there is nothing to be done other than waiting, hence it has no
* pending action.
*/
- private static void sendBugreportBeingUpdatedNotification(Context context, int pid) {
+ private static void sendBugreportBeingUpdatedNotification(Context context, int id) {
final String title = context.getString(R.string.bugreport_updating_title);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -916,8 +922,8 @@ public class BugreportProgressService extends Service {
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- Log.v(TAG, "Sending 'Updating zip' notification for pid " + pid + ": " + title);
- NotificationManager.from(context).notify(TAG, pid, builder.build());
+ Log.v(TAG, "Sending 'Updating zip' notification for ID " + id + ": " + title);
+ NotificationManager.from(context).notify(TAG, id, builder.build());
}
/**
@@ -985,7 +991,7 @@ public class BugreportProgressService extends Service {
// It's not possible to add a new entry into an existing file, so we need to create a new
// zip, copy all entries, then rename it.
- sendBugreportBeingUpdatedNotification(context, info.pid); // ...and that takes time
+ sendBugreportBeingUpdatedNotification(context, info.id); // ...and that takes time
final File dir = info.bugreportFile.getParentFile();
final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description");
@@ -1113,8 +1119,8 @@ public class BugreportProgressService extends Service {
/**
* Updates the user-provided details of a bugreport.
*/
- private void updateBugreportInfo(int pid, String name, String title, String description) {
- final BugreportInfo info = getInfo(pid);
+ private void updateBugreportInfo(int id, String name, String title, String description) {
+ final BugreportInfo info = getInfo(id);
if (info == null) {
return;
}
@@ -1179,6 +1185,7 @@ public class BugreportProgressService extends Service {
private EditText mInfoDescription;
private AlertDialog mDialog;
private Button mOkButton;
+ private int mId;
private int mPid;
/**
@@ -1207,8 +1214,7 @@ public class BugreportProgressService extends Service {
/**
* Sets its internal state and displays the dialog.
*/
- private void initialize(Context context, int pid, String name, String title,
- String description) {
+ private void initialize(Context context, BugreportInfo info) {
// First initializes singleton.
if (mDialog == null) {
@SuppressLint("InflateParams")
@@ -1232,7 +1238,7 @@ public class BugreportProgressService extends Service {
mDialog = new AlertDialog.Builder(context)
.setView(view)
- .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
+ .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
.setCancelable(false)
.setPositiveButton(context.getString(com.android.internal.R.string.ok),
null)
@@ -1258,16 +1264,17 @@ public class BugreportProgressService extends Service {
}
// Then set fields.
- mSavedName = mTempName = name;
- mPid = pid;
- if (!TextUtils.isEmpty(name)) {
- mInfoName.setText(name);
+ mSavedName = mTempName = info.name;
+ mId = info.id;
+ mPid = info.pid;
+ if (!TextUtils.isEmpty(info.name)) {
+ mInfoName.setText(info.name);
}
- if (!TextUtils.isEmpty(title)) {
- mInfoTitle.setText(title);
+ if (!TextUtils.isEmpty(info.title)) {
+ mInfoTitle.setText(info.title);
}
- if (!TextUtils.isEmpty(description)) {
- mInfoDescription.setText(description);
+ if (!TextUtils.isEmpty(info.description)) {
+ mInfoDescription.setText(info.description);
}
// And finally display it.
@@ -1290,7 +1297,7 @@ public class BugreportProgressService extends Service {
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
- updateBugreportInfo(mPid, name, title, description);
+ updateBugreportInfo(mId, name, title, description);
mDialog.dismiss();
}
});
@@ -1328,7 +1335,7 @@ public class BugreportProgressService extends Service {
// Must update system property for the cases where dumpstate finishes
// while the user is still entering other fields (like title or
// description)
- setBugreportNameProperty(mPid, name);
+ setBugreportNameProperty(mId, name);
}
/**
@@ -1337,7 +1344,7 @@ public class BugreportProgressService extends Service {
* <p>Once the bugreport is finished dumpstate has already generated the final files, so
* changing the name would have no effect.
*/
- private void onBugreportFinished(int pid) {
+ private void onBugreportFinished(int id) {
if (mInfoName != null) {
mInfoName.setEnabled(false);
mInfoName.setText(mSavedName);
@@ -1353,6 +1360,11 @@ public class BugreportProgressService extends Service {
private final Context context;
/**
+ * Sequential, user-friendly id used to identify the bugreport.
+ */
+ final int id;
+
+ /**
* {@code pid} of the {@code dumpstate} process generating the bugreport.
*/
final int pid;
@@ -1426,8 +1438,9 @@ public class BugreportProgressService extends Service {
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid, String name, int max) {
+ BugreportInfo(Context context, int id, int pid, String name, int max) {
this.context = context;
+ this.id = id;
this.pid = pid;
this.name = name;
this.max = max;
@@ -1437,8 +1450,8 @@ public class BugreportProgressService extends Service {
* Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
* without a previous call to BUGREPORT_STARTED.
*/
- BugreportInfo(Context context, int pid) {
- this(context, pid, null, 0);
+ BugreportInfo(Context context, int id) {
+ this(context, id, id, null, 0);
this.finished = true;
}
@@ -1494,7 +1507,7 @@ public class BugreportProgressService extends Service {
@Override
public String toString() {
final float percent = ((float) progress * 100 / max);
- return "pid: " + pid + ", name: " + name + ", finished: " + finished
+ return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
+ "\n\ttitle: " + title + "\n\tdescription: " + description
+ "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles
+ "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
@@ -1506,6 +1519,7 @@ public class BugreportProgressService extends Service {
// Parcelable contract
protected BugreportInfo(Parcel in) {
context = null;
+ id = in.readInt();
pid = in.readInt();
name = in.readString();
title = in.readString();
@@ -1527,6 +1541,7 @@ public class BugreportProgressService extends Service {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
dest.writeInt(pid);
dest.writeString(name);
dest.writeString(title);
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 127bddd71c43..c23c745931c6 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -20,11 +20,31 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <com.android.systemui.qs.PageIndicator
- android:id="@+id/page_indicator"
- android:layout_width="match_parent"
+ <FrameLayout
+ android:id="@+id/page_decor"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="center" />
+ android:layout_gravity="bottom">
+
+ <com.android.systemui.qs.PageIndicator
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@android:id/edit"
+ style="@style/QSBorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:minWidth="88dp"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton"
+ android:textColor="#4DFFFFFF"
+ android:focusable="true"
+ android:text="@string/qs_edit" />
+
+ </FrameLayout>
</com.android.systemui.qs.PagedTileLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index bb37b83af336..45236a075e72 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,13 +18,16 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/qs_background_primary"
- android:paddingBottom="8dp"
- android:elevation="2dp">
+ android:background="@drawable/qs_background_primary">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
android:background="#0000"
+ android:layout_marginTop="@dimen/status_bar_header_height"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp" />
+
+ <include layout="@layout/quick_status_bar_expanded_header" />
+
</com.android.systemui.qs.QSContainer>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 89abe2dc2c4c..289b1d9db32c 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -39,32 +39,11 @@
android:clipToPadding="false"
android:clipChildren="false">
- <com.android.systemui.statusbar.phone.ObservableScrollView
- android:id="@+id/scroll_view"
+ <include
+ layout="@layout/qs_panel"
android:layout_width="@dimen/notification_panel_width"
- android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:scrollbars="none"
- android:overScrollMode="never"
- android:fillViewport="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <include
- layout="@layout/qs_panel"
- android:layout_marginTop="@dimen/status_bar_header_height_expanded"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <!-- A view to reserve space for the collapsed stack -->
- <!-- Layout height: notification_min_height + bottom_stack_peek_amount -->
- <View
- android:id="@+id/reserve_notification_space"
- android:layout_height="@dimen/min_stack_height"
- android:layout_width="match_parent" />
- </LinearLayout>
- </com.android.systemui.statusbar.phone.ObservableScrollView>
+ android:layout_height="wrap_content"
+ android:layout_gravity="@integer/notification_panel_layout_gravity" />
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
@@ -90,12 +69,6 @@
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
- <ViewStub
- android:id="@+id/status_bar_header"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_gravity="@integer/notification_panel_layout_gravity" />
-
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
android:layout_height="96dp"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c0652d8a8e8b..122413d2677a 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -95,4 +95,10 @@
<dimen name="navigation_key_padding">25dp</dimen>
<dimen name="qs_expand_margin">0dp</dimen>
+
+ <!-- The top padding for the task stack. -->
+ <dimen name="recents_stack_top_padding">40dp</dimen>
+
+ <!-- The side padding for the task stack. -->
+ <dimen name="recents_stack_left_right_padding">64dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a9b8df2934c7..4cd920a86733 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,8 +52,12 @@
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
+ <!-- The disabled recents task bar background color. -->
+ <color name="recents_task_bar_disabled_background_color">#ff676767</color>
<!-- The default recents task bar background color. -->
<color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
+ <!-- The default recents task view background color. -->
+ <color name="recents_task_view_default_background_color">#fff3f3f3</color>
<!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
<color name="recents_task_bar_light_text_color">#ffeeeeee</color>
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a6ba8b59303d..72421a3070ae 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -165,9 +165,6 @@
<!-- The animation duration for entering and exiting the history. -->
<integer name="recents_history_transition_duration">250</integer>
- <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
- <integer name="recents_max_task_stack_view_dim">96</integer>
-
<!-- The delay to enforce between each alt-tab key press. -->
<integer name="recents_alt_tab_key_delay">200</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6702cefeb3e8..f3b91995cbd2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -249,15 +249,15 @@
<!-- The height of the search bar space. -->
<dimen name="recents_search_bar_space_height">64dp</dimen>
- <!-- The side padding for the task stack as a percentage of the width. -->
- <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.03333</item>
-
<!-- The overscroll percentage allowed on the stack. -->
<item name="recents_stack_overscroll_percentage" format="float" type="dimen">0.0875</item>
- <!-- The top offset for the task stack. -->
+ <!-- The top padding for the task stack. -->
<dimen name="recents_stack_top_padding">16dp</dimen>
+ <!-- The side padding for the task stack. -->
+ <dimen name="recents_stack_left_right_padding">16dp</dimen>
+
<!-- The dimesnsions of the dismiss all recents button. -->
<dimen name="recents_dismiss_all_button_size">48dp</dimen>
@@ -274,7 +274,10 @@
<dimen name="recents_stack_overscroll">24dp</dimen>
<!-- The size of the peek area at the top of the stack. -->
- <dimen name="recents_layout_focused_peek_size">@dimen/recents_history_button_height</dimen>
+ <dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
+
+ <!-- The size of the peek area at the bottom of the stack. -->
+ <dimen name="recents_layout_focused_bottom_peek_size">@dimen/recents_history_button_height</dimen>
<!-- The height of the history button. -->
<dimen name="recents_history_button_height">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ff9be14b3ce..5e8c12310bb0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -714,6 +714,8 @@
<string name="recents_search_bar_label">search</string>
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+ <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
+ <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_history_button_label">History</string>
<!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
@@ -1174,15 +1176,10 @@
<!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
<string name="qs_paging" translatable="false">Use the new Quick Settings</string>
- <!-- Toggles fast-toggling recents via the recents button. DO NOT TRANSLATE -->
- <string name="overview_fast_toggle_via_button">Enable fast toggle</string>
+ <!-- Disables fast-toggling recents via the recents button. DO NOT TRANSLATE -->
+ <string name="overview_disable_fast_toggle_via_button">Disable fast toggle</string>
<!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
- <string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
-
- <!-- Toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
- <string name="overview_initial_state_paging">Initialize to paging</string>
- <!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
- <string name="overview_initial_state_paging_desc">Determines whether Overview will initially be in a stacked or paged state</string>
+ <string name="overview_disable_fast_toggle_via_button_desc">Disable launch timeout while paging</string>
<!-- Toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=60]-->
<string name="overview_nav_bar_gesture">Enable split-screen swipe-up accelerator</string>
@@ -1405,4 +1402,7 @@
<!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
<string name="drag_to_add_tiles">Drag to add tiles</string>
+ <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
+ <string name="qs_edit">Edit</string>
+
</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 4de4cede0faa..39281bcb0809 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -113,14 +113,9 @@
android:title="@string/overview" >
<com.android.systemui.tuner.TunerSwitch
- android:key="overview_initial_state_paging"
- android:title="@string/overview_initial_state_paging"
- android:summary="@string/overview_initial_state_paging_desc" />
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="overview_fast_toggle_via_button"
- android:title="@string/overview_fast_toggle_via_button"
- android:summary="@string/overview_fast_toggle_via_button_desc" />
+ android:key="overview_disable_fast_toggle_via_button"
+ android:title="@string/overview_disable_fast_toggle_via_button"
+ android:summary="@string/overview_disable_fast_toggle_via_button_desc" />
<com.android.systemui.tuner.TunerSwitch
android:key="overview_nav_bar_gesture"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 454d1ceb4b04..4845425c233e 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -62,6 +62,7 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
mPlusPaint;
private float mTextHeight, mWarningTextHeight;
private int mIconTint = Color.WHITE;
+ private float mOldDarkIntensity = 0f;
private int mHeight;
private int mWidth;
@@ -295,6 +296,9 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
}
public void setDarkIntensity(float darkIntensity) {
+ if (darkIntensity == mOldDarkIntensity) {
+ return;
+ }
int backgroundColor = getBackgroundColor(darkIntensity);
int fillColor = getFillColor(darkIntensity);
mIconTint = fillColor;
@@ -302,6 +306,7 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
mBoltPaint.setColor(fillColor);
mChargeColor = fillColor;
invalidateSelf();
+ mOldDarkIntensity = darkIntensity;
}
private int getBackgroundColor(float darkIntensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 958572fd4fd5..90d56f78697e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -475,6 +475,23 @@ public class KeyguardViewMediator extends SystemUI {
break;
}
}
+
+ @Override
+ public void onFingerprintAuthFailed() {
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+ currentUser);
+ }
+ }
+
+ @Override
+ public void onFingerprintAuthenticated(int userId) {
+ if (mLockPatternUtils.isSecure(userId)) {
+ mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+ userId);
+ }
+ }
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -1370,8 +1387,9 @@ public class KeyguardViewMediator extends SystemUI {
* @see #KEYGUARD_DONE
*/
private void handleKeyguardDone(boolean authenticated) {
- if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
- mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed();
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
@@ -1484,8 +1502,9 @@ public class KeyguardViewMediator extends SystemUI {
* @see #SHOW
*/
private void handleShow(Bundle options) {
- if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
- mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured();
+ final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ if (mLockPatternUtils.isSecure(currentUser)) {
+ mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index d72336739886..8e9857d27f74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,7 +6,6 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.internal.widget.PagerAdapter;
import com.android.internal.widget.ViewPager;
import com.android.systemui.R;
@@ -27,6 +26,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private PageIndicator mPageIndicator;
private int mNumPages;
+ private View mDecorGroup;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -55,7 +55,8 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
- ((LayoutParams) mPageIndicator.getLayoutParams()).isDecor = true;
+ mDecorGroup = findViewById(R.id.page_decor);
+ ((LayoutParams) mDecorGroup.getLayoutParams()).isDecor = true;
mPages.add((TilePage) LayoutInflater.from(mContext)
.inflate(R.layout.qs_paged_page, this, false));
@@ -137,7 +138,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
maxHeight = height;
}
}
- setMeasuredDimension(getMeasuredWidth(), maxHeight + mPageIndicator.getMeasuredHeight());
+ setMeasuredDimension(getMeasuredWidth(), maxHeight + mDecorGroup.getMeasuredHeight());
}
private final Runnable mDistribute = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index cfe8d07dd14b..32eeb0787113 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -16,19 +16,38 @@
package com.android.systemui.qs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
- * Wrapper view with background which contains {@link QSPanel}
+ * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
+ *
+ * Also manages animations for the QS Header and Panel.
*/
public class QSContainer extends FrameLayout {
+ private static final String TAG = "QSContainer";
+ private static final boolean DEBUG = false;
private int mHeightOverride = -1;
private QSPanel mQSPanel;
+ protected BaseStatusBarHeader mHeader;
+ private float mQsExpansion;
+ private boolean mQsExpanded;
+ private boolean mHeaderAnimating;
+ private boolean mKeyguardShowing;
+ private boolean mStackScrollerOverscrolling;
+
+ private long mDelay;
public QSContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -38,6 +57,7 @@ public class QSContainer extends FrameLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
+ mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
}
@Override
@@ -63,14 +83,132 @@ public class QSContainer extends FrameLayout {
*/
public int getDesiredHeight() {
if (mQSPanel.isClosingDetail()) {
- return mQSPanel.getGridHeight() + getPaddingTop() + getPaddingBottom();
+ return mQSPanel.getGridHeight() + mHeader.getCollapsedHeight() + getPaddingBottom();
} else {
return getMeasuredHeight();
}
}
private void updateBottom() {
- int height = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+ int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+ int height = (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
+ + mHeader.getCollapsedHeight();
setBottom(getTop() + height);
}
+
+ private void updateQsState() {
+ boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
+ mQSPanel.setExpanded(mQsExpanded);
+ mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+ ? View.VISIBLE
+ : View.INVISIBLE);
+ mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
+ || (mQsExpanded && !mStackScrollerOverscrolling));
+ mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ public BaseStatusBarHeader getHeader() {
+ return mHeader;
+ }
+
+ public QSPanel getQsPanel() {
+ return mQSPanel;
+ }
+
+ public void setHeaderClickable(boolean clickable) {
+ if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
+ mHeader.setClickable(clickable);
+ }
+
+ public void setExpanded(boolean expanded) {
+ if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
+ mQsExpanded = expanded;
+ updateQsState();
+ }
+
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
+ mKeyguardShowing = keyguardShowing;
+ updateQsState();
+ }
+
+ public void setOverscrolling(boolean stackScrollerOverscrolling) {
+ if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling);
+ mStackScrollerOverscrolling = stackScrollerOverscrolling;
+ updateQsState();
+ }
+
+ public void setListening(boolean listening) {
+ if (DEBUG) Log.d(TAG, "setListening " + listening);
+ mQSPanel.setListening(listening);
+ mHeader.setListening(listening);
+ }
+
+ public void setQsExpansion(float expansion, float headerTranslation) {
+ if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ mQsExpansion = expansion;
+ final float translationScaleY = expansion - 1;
+ if (!mHeaderAnimating) {
+ setTranslationY(mKeyguardShowing ? (translationScaleY * mHeader.getHeight())
+ : headerTranslation);
+ }
+ mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
+ mQSPanel.setTranslationY(translationScaleY * mQSPanel.getHeight());
+ updateBottom();
+ }
+
+ public void animateHeaderSlidingIn(long delay) {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
+ // If the QS is already expanded we don't need to slide in the header as it's already
+ // visible.
+ if (!mQsExpanded) {
+ mHeaderAnimating = true;
+ mDelay = delay;
+ getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ }
+ }
+
+ public void animateHeaderSlidingOut() {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
+ mHeaderAnimating = true;
+ animate().y(-mHeader.getHeight())
+ .setStartDelay(0)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animate().setListener(null);
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ })
+ .start();
+ }
+
+ private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
+ = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ animate()
+ .translationY(0f)
+ .setStartDelay(mDelay)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(mAnimateHeaderSlidingInListener)
+ .start();
+ setY(-mHeader.getHeight());
+ return true;
+ }
+ };
+
+ private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
+ = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 1961860d95f6..4ffa527c6a42 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -117,6 +117,17 @@ public class QSPanel extends FrameLayout implements Tunable {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
R.layout.qs_paged_tile_layout, mQsContainer, false);
mQsContainer.addView((View) mTileLayout);
+ findViewById(android.R.id.edit).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ mHost.startRunnableDismissingKeyguard(new Runnable() {
+ @Override
+ public void run() {
+ showEdit(v);
+ }
+ });
+ }
+ });
mFooter = new QSFooter(this, context);
mQsContainer.addView(mFooter.getView());
@@ -369,19 +380,7 @@ public class QSPanel extends FrameLayout implements Tunable {
final View.OnLongClickListener longClick = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- if (mCustomizePanel != null) {
- if (!mCustomizePanel.isCustomizing()) {
- int[] loc = new int[2];
- getLocationInWindow(loc);
- int x = r.tileView.getLeft() + r.tileView.getWidth() / 2 + loc[0];
- int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r)
- + r.tileView.getHeight() / 2 + loc[1];
- mCustomizePanel.show(x, y);
- }
- } else {
- r.tile.longClick();
- }
- return true;
+ return false;
}
};
r.tileView.init(click, longClick);
@@ -395,6 +394,25 @@ public class QSPanel extends FrameLayout implements Tunable {
}
}
+
+ private void showEdit(final View v) {
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mCustomizePanel != null) {
+ if (!mCustomizePanel.isCustomizing()) {
+ int[] loc = new int[2];
+ v.getLocationInWindow(loc);
+ int x = loc[0];
+ int y = loc[1];
+ mCustomizePanel.show(x, y);
+ }
+ }
+
+ }
+ });
+ }
+
protected void onTileClick(QSTile<?> tile) {
tile.click();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index fc14758af0f4..711d834cc07b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -52,11 +52,9 @@ public class RecentsDebugFlags implements TunerService.Tunable {
public static final int MockTaskGroupsTaskCount = 12;
}
- private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
- private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
+ private static final String KEY_DISABLE_FAST_TOGGLE = "overview_disable_fast_toggle_via_button";
- private boolean mFastToggleRecents;
- private boolean mInitialStatePaging;
+ private boolean mDisableFastToggleRecents;
/**
* We read the prefs once when we start the activity, then update them as the tuner changes
@@ -65,7 +63,7 @@ public class RecentsDebugFlags implements TunerService.Tunable {
public RecentsDebugFlags(Context context) {
// Register all our flags, this will also call onTuningChanged() for each key, which will
// initialize the current state of each flag
- TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_INITIAL_STATE_PAGING);
+ TunerService.get(context).addTunable(this, KEY_DISABLE_FAST_TOGGLE);
}
/**
@@ -74,32 +72,21 @@ public class RecentsDebugFlags implements TunerService.Tunable {
public boolean isFastToggleRecentsEnabled() {
// These checks EnableFastToggleTimeoutOverride
SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask() ||
- ssp.isTouchExplorationEnabled()) {
+ if (mDisableFastToggleRecents || ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask()
+ || ssp.isTouchExplorationEnabled()) {
return false;
}
if (Static.EnableFastToggleTimeoutOverride) {
return true;
}
- return mFastToggleRecents;
- }
-
- /**
- * @return whether the initial stack state is paging.
- */
- public boolean isInitialStatePaging() {
- return mInitialStatePaging;
+ return true;
}
@Override
public void onTuningChanged(String key, String newValue) {
switch (key) {
- case KEY_FAST_TOGGLE:
- mFastToggleRecents = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
- break;
- case KEY_INITIAL_STATE_PAGING:
- mInitialStatePaging = (newValue != null) &&
+ case KEY_DISABLE_FAST_TOGGLE:
+ mDisableFastToggleRecents = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9da5c2bd3f02..e0efaa5ae38d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -837,11 +837,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
* Draws the header of a task used for the window animation into a bitmap.
*/
private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (toTransform != null && toTask.key != null) {
Bitmap thumbnail;
synchronized (mHeaderBarLock) {
int toHeaderWidth = (int) toTransform.rect.width();
int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
(int) toTransform.rect.height());
thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
@@ -851,7 +853,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
} else {
Canvas c = new Canvas(thumbnail);
c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */);
+ mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+ disabledInSafeMode);
mHeaderBar.draw(c);
c.setBitmap(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index 843adc15c54a..3c4adb23e77a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -186,6 +186,7 @@ public class RecentsHistoryView extends LinearLayout
mRecyclerView = (RecyclerView) findViewById(R.id.list);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mRecyclerView.getItemAnimator().setRemoveDuration(100);
ItemTouchHelper touchHelper = new ItemTouchHelper(mItemTouchHandler);
touchHelper.attachToRecyclerView(mRecyclerView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 244c0df30da4..95aa10f4876e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.misc;
import android.os.Handler;
+import android.view.ViewDebug;
/**
* A dozer is a class that fires a trigger after it falls asleep.
@@ -26,8 +27,11 @@ public class DozeTrigger {
Handler mHandler;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsDozing;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mHasTriggered;
+ @ViewDebug.ExportedProperty(category="recents")
int mDozeDurationMilliseconds;
Runnable mOnSleepRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 22ab79430cd2..8b4474f1cea5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -112,6 +112,8 @@ public class SystemServicesProxy {
Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
+
+ boolean mIsSafeMode;
boolean mHasFreeformWorkspaceSupport;
Bitmap mDummyIcon;
@@ -137,6 +139,7 @@ public class SystemServicesProxy {
mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ mIsSafeMode = mPm.isSafeMode();
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -187,7 +190,8 @@ public class SystemServicesProxy {
rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
- Bitmap.createBitmap(mDummyIcon),
+ Bitmap.createBitmap(mDummyIcon), null,
+ 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0xFF000000 | (0xFFFFFF & new Random().nextInt()));
} else {
rti.taskDescription = new ActivityManager.TaskDescription();
@@ -260,6 +264,13 @@ public class SystemServicesProxy {
return mHasFreeformWorkspaceSupport;
}
+ /**
+ * Returns whether this device is in the safe mode.
+ */
+ public boolean isInSafeMode() {
+ return mIsSafeMode;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
MutableBoolean isHomeTopMost) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 52043f400bd7..e86b92dd39b5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -17,6 +17,8 @@
package com.android.systemui.recents.misc;
import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.annotation.FloatRange;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -30,6 +32,7 @@ import android.view.ViewParent;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskViewTransform;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -102,6 +105,46 @@ public class Utilities {
return setOut;
}
+ /**
+ * @return the clamped {@param value} between the provided {@param min} and {@param max}.
+ */
+ public static float clamp(float value, float min, float max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * @return the clamped {@param value} between the provided {@param min} and {@param max}.
+ */
+ public static int clamp(int value, int min, int max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * @return the clamped {@param value} between 0 and 1.
+ */
+ public static float clamp01(float value) {
+ return Math.max(0f, Math.min(1f, value));
+ }
+
+ /**
+ * Scales the {@param value} to be proportionally between the {@param min} and
+ * {@param max} values.
+ *
+ * @param value must be between 0 and 1
+ */
+ public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
+ /**
+ * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
+ *
+ * @param value must be between {@param min} and {@param max}
+ */
+ public static float unmapRange(float value, float min, float max) {
+ return (value - min) / (max - min);
+ }
+
/** Scales a rect about its centroid */
public static void scaleRectAboutCenter(RectF r, float scale) {
if (scale != 1.0f) {
@@ -154,12 +197,25 @@ public class Utilities {
*/
public static void cancelAnimationWithoutCallbacks(Animator animator) {
if (animator != null) {
- animator.removeAllListeners();
+ removeAnimationListenersRecursive(animator);
animator.cancel();
}
}
/**
+ * Recursively removes all the listeners of all children of this animator
+ */
+ public static void removeAnimationListenersRecursive(Animator animator) {
+ if (animator instanceof AnimatorSet) {
+ ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
+ for (int i = animators.size() - 1; i >= 0; i--) {
+ removeAnimationListenersRecursive(animators.get(i));
+ }
+ }
+ animator.removeAllListeners();
+ }
+
+ /**
* Updates {@param transforms} to be the same size as {@param tasks}.
*/
public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index c51aa7ce38c9..016e6d30843d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents.model;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -188,11 +189,15 @@ public class RecentsTaskLoadPlan {
: null;
Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+ int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+ boolean isSystemApp = (loader.getAndUpdateActivityInfo(taskKey).applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, dismissDescription, activityColor,
- !isStackTask, isLaunchTarget, t.bounds, t.taskDescription);
+ backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
+ t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 26130abc138b..5e1af1280400 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -264,13 +264,16 @@ public class RecentsTaskLoader {
private int mNumVisibleThumbnailsLoaded;
int mDefaultTaskBarBackgroundColor;
+ int mDefaultTaskViewBackgroundColor;
BitmapDrawable mDefaultIcon;
Bitmap mDefaultThumbnail;
public RecentsTaskLoader(Context context) {
Resources res = context.getResources();
mDefaultTaskBarBackgroundColor =
- res.getColor(R.color.recents_task_bar_default_background_color);
+ context.getColor(R.color.recents_task_bar_default_background_color);
+ mDefaultTaskViewBackgroundColor =
+ context.getColor(R.color.recents_task_view_default_background_color);
mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
@@ -556,10 +559,20 @@ public class RecentsTaskLoader {
}
/**
+ * Returns the task's background color if possible.
+ */
+ int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+ if (td != null && td.getBackgroundColor() != 0) {
+ return td.getBackgroundColor();
+ }
+ return mDefaultTaskViewBackgroundColor;
+ }
+
+ /**
* Returns the activity info for the given task key, retrieving one from the system if the
* task key is expired.
*/
- private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = taskKey.getComponent();
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1c277d5a3d92..8ed6dd78a357 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -48,11 +49,17 @@ public class Task {
/* The Task Key represents the unique primary key for the task */
public static class TaskKey {
+ @ViewDebug.ExportedProperty(category="recents")
public final int id;
+ @ViewDebug.ExportedProperty(category="recents")
public int stackId;
+ @ViewDebug.ExportedProperty(category="recents")
public final Intent baseIntent;
+ @ViewDebug.ExportedProperty(category="recents")
public final int userId;
+ @ViewDebug.ExportedProperty(category="recents")
public long firstActiveTime;
+ @ViewDebug.ExportedProperty(category="recents")
public long lastActiveTime;
private int mHashCode;
@@ -105,17 +112,21 @@ public class Task {
}
}
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
public TaskKey key;
/**
* The group will be computed separately from the initialization of the task
*/
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="group_")
public TaskGrouping group;
/**
* The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
* with any task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationTaskId;
+ @ViewDebug.ExportedProperty(category="recents")
public int affiliationColor;
/**
@@ -124,15 +135,23 @@ public class Task {
*/
public Drawable icon;
public Bitmap thumbnail;
+ @ViewDebug.ExportedProperty(category="recents")
public String title;
+ @ViewDebug.ExportedProperty(category="recents")
public String contentDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public String dismissDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
+ @ViewDebug.ExportedProperty(category="recents")
+ public int colorBackground;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean useLightOnPrimaryColor;
/**
* The bounds of the task, used only if it is a freeform task.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public Rect bounds;
/**
@@ -143,8 +162,12 @@ public class Task {
/**
* The state isLaunchTarget will be set for the correct task upon launching Recents.
*/
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isLaunchTarget;
+ @ViewDebug.ExportedProperty(category="recents")
public boolean isHistorical;
+ @ViewDebug.ExportedProperty(category="recents")
+ public boolean isSystemApp;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -154,8 +177,8 @@ public class Task {
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription,
- String dismissDescription, int colorPrimary, boolean isHistorical,
- boolean isLaunchTarget, Rect bounds,
+ String dismissDescription, int colorPrimary, int colorBackground,
+ boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -168,12 +191,14 @@ public class Task {
this.contentDescription = contentDescription;
this.dismissDescription = dismissDescription;
this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
+ this.colorBackground = colorBackground;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isLaunchTarget = isLaunchTarget;
this.isHistorical = isHistorical;
+ this.isSystemApp = isSystemApp;
}
/** Copies the other task. */
@@ -188,10 +213,12 @@ public class Task {
this.contentDescription = o.contentDescription;
this.dismissDescription = o.dismissDescription;
this.colorPrimary = o.colorPrimary;
+ this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
this.isLaunchTarget = o.isLaunchTarget;
this.isHistorical = o.isHistorical;
+ this.isSystemApp = o.isSystemApp;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
index 67a6a9f1d514..d433b6c00269 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java
@@ -96,6 +96,6 @@ public class TaskKeyLruCache<V> {
/** Trims the cache to a specific size */
final void trimToSize(int cacheSize) {
- mCache.resize(cacheSize);
+ mCache.trimToSize(cacheSize);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 584209510e45..8575c0d8035e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -19,18 +19,28 @@ package com.android.systemui.recents.views;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
+import com.android.systemui.recents.misc.Utilities;
+
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
+ private static final float MIN_ALPHA = 0.1f;
+ private static final float MAX_ALPHA = 0.8f;
+
View mSourceView;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mLastClipBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mAlpha = 1f;
- final float mMinAlpha = 0.25f;
public AnimateableViewBounds(View source, int cornerRadius) {
mSourceView = source;
@@ -47,7 +57,7 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
@Override
public void getOutline(View view, Outline outline) {
- outline.setAlpha(mMinAlpha + mAlpha / (1f - mMinAlpha));
+ outline.setAlpha(Utilities.mapRange(mAlpha, MIN_ALPHA, MAX_ALPHA));
if (mCornerRadius > 0) {
outline.setRoundRect(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
@@ -60,7 +70,9 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
}
}
- /** Sets the view outline alpha. */
+ /**
+ * Sets the view outline alpha.
+ */
void setAlpha(float alpha) {
if (Float.compare(alpha, mAlpha) != 0) {
mAlpha = alpha;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java
index 93878c52d6de..48e137028a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java
@@ -49,6 +49,8 @@ public class AnimationProps {
public static final int ALPHA = 4;
public static final int SCALE = 5;
public static final int BOUNDS = 6;
+ public static final int DIM_ALPHA = 7;
+ public static final int FOCUS_STATE = 8;
private SparseLongArray mPropStartDelay;
private SparseLongArray mPropDuration;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 511aa3c9c001..d8a3e766e257 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -152,10 +152,10 @@ public class FreeformWorkspaceLayoutAlgorithm {
transformOut.scale = 1f;
transformOut.alpha = 1f;
transformOut.translationZ = stackLayout.mMaxTranslationZ;
+ transformOut.dimAlpha = 0f;
transformOut.rect.set(ffRect);
transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
transformOut.visible = true;
- transformOut.p = 1f;
return transformOut;
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 42aaa9782712..c4db48552377 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
@@ -79,8 +82,6 @@ import com.android.systemui.statusbar.FlingAnimationUtils;
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
@@ -103,6 +104,8 @@ public class RecentsView extends FrameLayout {
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
+
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mSystemInsets = new Rect();
private int mDividerSize;
@@ -110,6 +113,7 @@ public class RecentsView extends FrameLayout {
private Animator mBackgroundScrimAnimator;
private RecentsTransitionHelper mTransitionHelper;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
private RecentsViewTouchHandler mTouchHandler;
private final FlingAnimationUtils mFlingAnimationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 346ce167cd17..016d9370364d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,6 +20,7 @@ import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
@@ -61,12 +62,18 @@ public class RecentsViewTouchHandler {
private RecentsView mRv;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
private Task mDragTask;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
private TaskView mTaskView;
+ @ViewDebug.ExportedProperty(category="recents")
private Point mTaskViewOffset = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private Point mDownPos = new Point();
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mDragRequested;
+ @ViewDebug.ExportedProperty(category="recents")
private boolean mIsDragging;
private float mDragSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 19ac1e7dd44d..360a13985526 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -25,13 +27,13 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.FloatProperty;
import android.util.Property;
+import android.view.ViewDebug;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -106,6 +108,27 @@ public class TaskStackLayoutAlgorithm {
// The scale factor to apply to the user movement in the stack to unfocus it
private static final float UNFOCUS_MULTIPLIER = 0.8f;
+ // The distribution of dim to apply to tasks in the stack
+ public static final float DIM_MAX_VALUE = 0.35f;
+ private static final Path UNFOCUSED_DIM_PATH = new Path();
+ private static final Path FOCUSED_DIM_PATH = new Path();
+ static {
+ // The unfocused dim interpolator peaks to 1 at 0.5 (the focused task), then slowly drops
+ // back to 0.5 at the front of the stack
+ UNFOCUSED_DIM_PATH.moveTo(0f, 0f);
+ UNFOCUSED_DIM_PATH.cubicTo(0f, 0.1f, 0.4f, 0.8f, 0.5f, 1f);
+ UNFOCUSED_DIM_PATH.cubicTo(0.6f, 1f, 0.9f, 0.6f, 1f, 0.5f);
+ // The focused dim interpolator peaks to 1 at 0.5 (the focused task), then drops back to 0
+ // at the front of the stack
+ FOCUSED_DIM_PATH.moveTo(0f, 0f);
+ FOCUSED_DIM_PATH.cubicTo(0.1f, 0f, 0.4f, 1f, 0.5f, 1f);
+ FOCUSED_DIM_PATH.cubicTo(0.6f, 1f, 0.9f, 0f, 1f, 0f);
+ }
+ private static final FreePathInterpolator UNFOCUSED_DIM_INTERPOLATOR =
+ new FreePathInterpolator(UNFOCUSED_DIM_PATH);
+ private static final FreePathInterpolator FOCUSED_DIM_INTERPOLATOR =
+ new FreePathInterpolator(FOCUSED_DIM_PATH);
+
// The various focus states
public static final float STATE_FOCUSED = 1f;
public static final float STATE_UNFOCUSED = 0f;
@@ -216,15 +239,20 @@ public class TaskStackLayoutAlgorithm {
private TaskStackLayoutAlgorithmCallbacks mCb;
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mTaskRect = new Rect();
// The freeform workspace bounds, inset from the top by the search bar, and is a fixed height
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mFreeformRect = new Rect();
// The stack bounds, inset from the top by the search bar, and runs to
// the bottom of the screen
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mStackRect = new Rect();
// This is the current system insets
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mSystemInsets = new Rect();
// This is the bounds of the history button above the stack rect
+ @ViewDebug.ExportedProperty(category="recents")
public Rect mHistoryButtonRect = new Rect();
// The visible ranges when the stack is focused and unfocused
@@ -232,14 +260,19 @@ public class TaskStackLayoutAlgorithm {
private Range mFocusedRange;
// The offset from the top when scrolled to the top of the stack
- private int mFocusedPeekHeight;
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mFocusedTopPeekHeight;
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mFocusedBottomPeekHeight;
// The offset from the top of the stack to the top of the bounds when the stack is scrolled to
// the end
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackTopOffset;
// The offset from the bottom of the stack to the bottom of the bounds when the stack is
// scrolled to the front
+ @ViewDebug.ExportedProperty(category="recents")
private int mStackBottomOffset;
// The paths defining the motion of the tasks when the stack is focused and unfocused
@@ -250,27 +283,36 @@ public class TaskStackLayoutAlgorithm {
// The state of the stack focus (0..1), which controls the transition of the stack from the
// focused to non-focused state
+ @ViewDebug.ExportedProperty(category="recents")
private float mFocusState;
// The animator used to reset the focused state
private ObjectAnimator mFocusStateAnimator;
// The smallest scroll progress, at this value, the back most task will be visible
+ @ViewDebug.ExportedProperty(category="recents")
float mMinScrollP;
// The largest scroll progress, at this value, the front most task will be visible above the
// navigation bar
+ @ViewDebug.ExportedProperty(category="recents")
float mMaxScrollP;
// The initial progress that the scroller is set when you first enter recents
+ @ViewDebug.ExportedProperty(category="recents")
float mInitialScrollP;
// The task progress for the front-most task in the stack
+ @ViewDebug.ExportedProperty(category="recents")
float mFrontMostTaskP;
// The last computed task counts
+ @ViewDebug.ExportedProperty(category="recents")
int mNumStackTasks;
+ @ViewDebug.ExportedProperty(category="recents")
int mNumFreeformTasks;
// The min/max z translations
+ @ViewDebug.ExportedProperty(category="recents")
int mMinTranslationZ;
+ @ViewDebug.ExportedProperty(category="recents")
int mMaxTranslationZ;
// Optimization, allows for quick lookup of task -> index
@@ -293,7 +335,10 @@ public class TaskStackLayoutAlgorithm {
mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
res.getFloat(R.integer.recents_layout_unfocused_range_max));
mFocusState = getDefaultFocusState();
- mFocusedPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_focused_peek_size);
+ mFocusedTopPeekHeight =
+ res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
+ mFocusedBottomPeekHeight =
+ res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_peek_size);
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
@@ -347,19 +392,19 @@ public class TaskStackLayoutAlgorithm {
// The freeform height is the visible height (not including system insets) - padding above
// freeform and below stack - gap between the freeform and stack
mState = state;
- mStackTopOffset = mFocusedPeekHeight + heightPadding;
+ mStackTopOffset = mFocusedTopPeekHeight + heightPadding;
mStackBottomOffset = mSystemInsets.bottom + heightPadding;
state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding,
mStackBottomOffset);
// The history button will take the full un-padded header space above the stack
mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding,
- mStackRect.right, mStackRect.top + mFocusedPeekHeight);
+ mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
// Anchor the task rect to the top-center of the non-freeform stack rect
float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right)
/ (taskStackBounds.height() - mSystemInsets.bottom);
int width = mStackRect.width();
- int minHeight = mStackRect.height() - mFocusedPeekHeight - mStackBottomOffset;
+ int minHeight = mStackRect.height() - mFocusedTopPeekHeight - mStackBottomOffset;
int height = (int) Math.min(width / aspect, minHeight);
mTaskRect.set(mStackRect.left, mStackRect.top,
mStackRect.left + width, mStackRect.top + height);
@@ -434,8 +479,8 @@ public class TaskStackLayoutAlgorithm {
mStackRect.height();
float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP,
- (mNumStackTasks - 1) - Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
+ mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+ Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
}
}
@@ -451,16 +496,16 @@ public class TaskStackLayoutAlgorithm {
mInitialScrollP = mMinScrollP;
} else if (getDefaultFocusState() > 0f) {
if (launchState.launchedFromHome) {
- mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, launchTaskIndex));
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else {
- mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP,
- launchTaskIndex - 1));
+ mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP,
+ mMaxScrollP);
}
} else {
float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height();
float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
- mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP,
- launchTaskIndex - mUnfocusedRange.getAbsoluteX(normX)));
+ mInitialScrollP = Utilities.clamp(launchTaskIndex -
+ mUnfocusedRange.getAbsoluteX(normX), mMinScrollP, mMaxScrollP);
}
}
}
@@ -495,12 +540,7 @@ public class TaskStackLayoutAlgorithm {
* Returns the default focus state.
*/
public float getDefaultFocusState() {
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (launchState.launchedWithAltTab || debugFlags.isInitialStatePaging()) {
- return STATE_FOCUSED;
- }
- return STATE_UNFOCUSED;
+ return STATE_FOCUSED;
}
/**
@@ -649,20 +689,9 @@ public class TaskStackLayoutAlgorithm {
// Compute the focused and unfocused offset
mUnfocusedRange.offset(stackScroll);
- float p = mUnfocusedRange.getNormalizedX(taskProgress);
- float yp = mUnfocusedCurveInterpolator.getInterpolation(p);
- float unfocusedP = p;
- int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height());
+ mFocusedRange.offset(stackScroll);
boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
- int focusedY = 0;
- boolean focusedVisible = true;
- if (mFocusState > 0f) {
- mFocusedRange.offset(stackScroll);
- p = mFocusedRange.getNormalizedX(taskProgress);
- yp = mFocusedCurveInterpolator.getInterpolation(p);
- focusedY = (int) (Math.max(0f, (1f - yp)) * mStackRect.height());
- focusedVisible = mFocusedRange.isInRange(taskProgress);
- }
+ boolean focusedVisible = mFocusedRange.isInRange(taskProgress);
// Skip if the task is not visible
if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
@@ -670,43 +699,48 @@ public class TaskStackLayoutAlgorithm {
return;
}
+ float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+ float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+
int x = (mStackRect.width() - mTaskRect.width()) / 2;
int y;
float z;
- float relP;
+ float dimAlpha;
if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1 && !ignoreSingleTaskCase) {
// When there is exactly one task, then decouple the task from the stack and just move
// in screen space
- p = (mMinScrollP - stackScroll) / mNumStackTasks;
+ float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
int centerYOffset = (mStackRect.top - mTaskRect.top) +
(mStackRect.height() - mTaskRect.height()) / 2;
- y = centerYOffset + getYForDeltaP(p, 0);
+ y = centerYOffset + getYForDeltaP(tmpP, 0);
z = mMaxTranslationZ;
- relP = 1f;
+ dimAlpha = 0f;
} else {
// Otherwise, update the task to the stack layout
- y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY));
- y += (mStackRect.top - mTaskRect.top);
- z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
- mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
- if (mNumStackTasks == 1) {
- relP = 1f;
- } else {
- relP = Math.min(mMaxScrollP, unfocusedP);
- }
+ int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation(
+ unfocusedRangeX)) * mStackRect.height());
+ int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
+ focusedRangeX)) * mStackRect.height());
+ float unfocusedDim = 1f - UNFOCUSED_DIM_INTERPOLATOR.getInterpolation(unfocusedRangeX);
+ float focusedDim = 1f - FOCUSED_DIM_INTERPOLATOR.getInterpolation(focusedRangeX);
+
+ y = (mStackRect.top - mTaskRect.top) +
+ (int) Utilities.mapRange(mFocusState, unfocusedY, focusedY);
+ z = Utilities.clamp01(unfocusedRangeX) * mMaxTranslationZ;
+ dimAlpha = Utilities.mapRange(mFocusState, unfocusedDim, focusedDim);
}
// Fill out the transform
transformOut.scale = 1f;
transformOut.alpha = 1f;
transformOut.translationZ = z;
+ transformOut.dimAlpha = DIM_MAX_VALUE * dimAlpha;
transformOut.rect.set(mTaskRect);
transformOut.rect.offset(x, y);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
(frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
- transformOut.p = relP;
}
/**
@@ -750,18 +784,16 @@ public class TaskStackLayoutAlgorithm {
* Creates a new path for the focused curve.
*/
private Path constructFocusedCurve() {
- int taskBarHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
-
// Initialize the focused curve. This curve is a piecewise curve composed of several
- // quadradic beziers that goes from (0,1) through (0.5, peek height offset),
- // (0.667, next task offset), (0.833, bottom task offset), and (1,0).
- float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height();
+ // linear pieces that goes from (0,1) through (0.5, peek height offset),
+ // (0.5, bottom task offsets), and (1,0).
+ float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
+ float bottomPeekHeightPct = (float) Math.max(mFocusedBottomPeekHeight, mStackRect.bottom -
+ mTaskRect.bottom) / mStackRect.height();
Path p = new Path();
p.moveTo(0f, 1f);
- p.lineTo(0.5f, 1f - peekHeightPct);
- p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mStackRect.height());
- p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mStackRect.height());
+ p.lineTo(0.5f, 1f - topPeekHeightPct);
+ p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
p.lineTo(1f, 0f);
return p;
}
@@ -778,7 +810,7 @@ public class TaskStackLayoutAlgorithm {
// there is a tangent at (0.5, peek height offset).
float cpoint1X = 0.4f;
float cpoint1Y = 1f;
- float peekHeightPct = (float) mFocusedPeekHeight / mStackRect.height();
+ float peekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
float b = 1f - slope * cpoint1X;
float cpoint2X = 0.75f;
@@ -799,10 +831,10 @@ public class TaskStackLayoutAlgorithm {
return;
}
- float min = mUnfocusedRange.relativeMin +
- mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin);
- float max = mUnfocusedRange.relativeMax +
- mFocusState * (mFocusedRange.relativeMax - mUnfocusedRange.relativeMax);
+ float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin,
+ mFocusedRange.relativeMin);
+ float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
+ mFocusedRange.relativeMax);
getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */,
true /* forceUpdate */);
getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fb3515a26380..2195b5e7eb3d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,8 +20,6 @@ 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 android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -40,11 +38,10 @@ import android.util.MutableBoolean;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
@@ -121,8 +118,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
LayoutInflater mInflater;
TaskStack mStack;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
TaskStackViewScroller mStackScroller;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
TaskStackViewTouchHandler mTouchHandler;
TaskStackAnimationHelper mAnimationHelper;
GradientDrawable mFreeformWorkspaceBackground;
@@ -134,25 +134,36 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
AnimationProps mDeferredTaskViewLayoutAnimation = null;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
DozeTrigger mUIDozeTrigger;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
Task mFocusedTask;
int mTaskCornerRadiusPx;
private int mDividerSize;
private int mStartTimerIndicatorDuration;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskViewsClipDirty = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mAwaitingFirstLayout = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInMeasureLayout = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mEnterAnimationComplete = false;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mScreenPinningEnabled;
// The stable stack bounds are the full bounds that we were measured with from RecentsView
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStableStackBounds = new Rect();
// The current stack bounds are dynamic and may change as the user drags and drops
+ @ViewDebug.ExportedProperty(category="recents")
private Rect mStackBounds = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
private int[] mTmpVisibleRange = new int[2];
private Rect mTmpRect = new Rect();
private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
@@ -549,7 +560,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
tv.updateViewPropertiesToTaskTransform(transform, AnimationProps.IMMEDIATE,
mRequestUpdateClippingListener);
} else {
- if (Float.compare(transform.p, 0f) <= 0) {
+ if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
tv.updateViewPropertiesToTaskTransform(
mLayoutAlgorithm.getBackOfStackTransform(),
AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
@@ -827,7 +838,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
boolean requestViewFocus, int timerIndicatorDuration) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
- Math.max(0, Math.min(mStack.getTaskCount() - 1, focusTaskIndex)) : -1;
+ Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
mStack.getStackTasks().get(newFocusedTaskIndex) : null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index d1bce55c324e..b7ff8bc89ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
+import android.view.ViewDebug;
import android.widget.OverScroller;
import com.android.systemui.Interpolators;
@@ -63,6 +64,7 @@ public class TaskStackViewScroller {
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScrollerCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
float mStackScrollP;
float mFlingDownScrollP;
int mFlingDownY;
@@ -150,8 +152,7 @@ public class TaskStackViewScroller {
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
- return Math.max(mLayoutAlgorithm.mMinScrollP,
- Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
+ return Utilities.clamp(scroll, mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP);
}
/** Returns the amount that the absolute value of how much the scroll is out of bounds. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 5d1bb66f0dac..b94a9f7d8687 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -28,8 +28,8 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewDebug;
import android.view.ViewParent;
-import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -44,7 +44,6 @@ import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.misc.RectFEvaluator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -69,6 +68,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
FlingAnimationUtils mFlingAnimUtils;
ValueAnimator mScrollFlingAnimator;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mIsScrolling;
float mDownScrollP;
int mDownX, mDownY;
@@ -545,7 +545,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// We only really need to interpolate the bounds, progress and translation
mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
fromTransform.rect, toTransform.rect));
- mTmpTransform.p = fromTransform.p + (toTransform.p - fromTransform.p) * dismissFraction;
+ mTmpTransform.dimAlpha = fromTransform.dimAlpha + (toTransform.dimAlpha -
+ fromTransform.dimAlpha) * dismissFraction;
mTmpTransform.translationZ = fromTransform.translationZ +
(toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 850e36e73e36..972b02aa3741 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,12 +31,12 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.IntProperty;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewOutlineProvider;
-import android.view.animation.AccelerateInterpolator;
+import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -79,57 +79,54 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
* The dim overlay is generally calculated from the task progress, but occasionally (like when
* launching) needs to be animated independently of the task progress.
*/
- public static final Property<TaskView, Integer> DIM =
- new IntProperty<TaskView>("dim") {
+ public static final Property<TaskView, Float> DIM_ALPHA =
+ new FloatProperty<TaskView>("dim") {
@Override
- public void setValue(TaskView tv, int dim) {
- tv.setDim(dim);
- }
-
- @Override
- public Integer get(TaskView tv) {
- return tv.getDim();
- }
- };
-
- public static final Property<TaskView, Float> TASK_PROGRESS =
- new FloatProperty<TaskView>("taskProgress") {
- @Override
- public void setValue(TaskView tv, float p) {
- tv.setTaskProgress(p);
+ public void setValue(TaskView tv, float dimAlpha) {
+ tv.setDimAlpha(dimAlpha);
}
@Override
public Float get(TaskView tv) {
- return tv.getTaskProgress();
+ return tv.getDimAlpha();
}
};
- float mTaskProgress;
- float mMaxDimScale;
- int mDimAlpha;
- AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
+ @ViewDebug.ExportedProperty(category="recents")
+ float mDimAlpha;
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
Task mTask;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTaskDataLoaded;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mClipViewInStack = true;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mTouchExplorationEnabled;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mIsDisabledInSafeMode;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
AnimateableViewBounds mViewBounds;
private AnimatorSet mTransformAnimation;
private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
View mContent;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
TaskViewThumbnail mThumbnailView;
+ @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
TaskViewHeader mHeaderView;
View mActionButtonView;
TaskViewCallbacks mCb;
+ @ViewDebug.ExportedProperty(category="recents")
Point mDownTouchPos = new Point();
+ private Toast mDisabledAppToast;
+
public TaskView(Context context) {
this(context, null);
}
@@ -146,7 +143,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
super(context, attrs, defStyleAttr, defStyleRes);
RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources();
- mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius));
if (config.fakeShadows) {
@@ -252,8 +248,8 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
mTmpAnimators.clear();
toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
if (toAnimation.isImmediate()) {
- if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
- setTaskProgress(toTransform.p);
+ if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
+ setDimAlpha(toTransform.dimAlpha);
}
// Manually call back to the animator listener and update callback
if (toAnimation.getListener() != null) {
@@ -264,9 +260,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
}
} else {
// Both the progress and the update are a function of the bounds movement of the task
- if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
- toTransform.p);
+ if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
+ toTransform.dimAlpha);
mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
}
if (updateCallback != null) {
@@ -284,7 +280,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
/** Resets this view's properties */
void resetViewProperties() {
cancelTransformAnimation();
- setDim(0);
+ setDimAlpha(0);
setVisibility(View.VISIBLE);
getViewBounds().reset();
getHeaderView().reset();
@@ -359,76 +355,58 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
}
}
- /** Sets the current task progress. */
- public void setTaskProgress(float p) {
- mTaskProgress = p;
- mViewBounds.setAlpha(p);
- updateDimFromTaskProgress();
- }
-
public TaskViewHeader getHeaderView() {
return mHeaderView;
}
- /** Returns the current task progress. */
- public float getTaskProgress() {
- return mTaskProgress;
- }
-
- /** Returns the current dim. */
- public void setDim(int dim) {
+ /**
+ * Sets the current dim.
+ */
+ public void setDimAlpha(float dimAlpha) {
RecentsConfiguration config = Recents.getConfiguration();
- mDimAlpha = dim;
+ int dimAlphaInt = (int) (dimAlpha * 255);
+ mDimAlpha = dimAlpha;
+ mViewBounds.setAlpha(1f - (dimAlpha / TaskStackLayoutAlgorithm.DIM_MAX_VALUE));
if (config.useHardwareLayers) {
// Defer setting hardware layers if we have not yet measured, or there is no dim to draw
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
- mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
+ mDimColorFilter.setColor(Color.argb(dimAlphaInt, 0, 0, 0));
mDimLayerPaint.setColorFilter(mDimColorFilter);
mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
}
} else {
- float dimAlpha = mDimAlpha / 255.0f;
mThumbnailView.setDimAlpha(dimAlpha);
mHeaderView.setDimAlpha(dimAlpha);
}
}
- /** Returns the current dim. */
- public int getDim() {
+ /**
+ * Returns the current dim.
+ */
+ public float getDimAlpha() {
return mDimAlpha;
}
- /** Animates the dim to the task progress. */
- void animateDimToProgress(int duration, Animator.AnimatorListener animListener) {
+ /**
+ * Animates the dim to the given value.
+ */
+ void animateDimAlpha(float toDimAlpha, AnimationProps animation) {
// Animate the dim into view as well
- int toDim = getDimFromTaskProgress();
- if (toDim != getDim()) {
- ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), toDim);
- anim.setDuration(duration);
- if (animListener != null) {
- anim.addListener(animListener);
+ if (Float.compare(toDimAlpha, getDimAlpha()) != 0) {
+ Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+ DIM_ALPHA, getDimAlpha(), toDimAlpha));
+ if (animation.getListener() != null) {
+ anim.addListener(animation.getListener());
}
anim.start();
} else {
- animListener.onAnimationEnd(null);
+ if (animation.getListener() != null) {
+ animation.getListener().onAnimationEnd(null);
+ }
}
}
- /** Compute the dim as a function of the scale of this view. */
- int getDimFromTaskProgress() {
- float x = mTaskProgress < 0
- ? 1f
- : mDimInterpolator.getInterpolation(1f - mTaskProgress);
- float dim = mMaxDimScale * x;
- return (int) (dim * 255);
- }
-
- /** Update the dim as a function of the scale of this view. */
- void updateDimFromTaskProgress() {
- setDim(getDimFromTaskProgress());
- }
-
/**
* Explicitly sets the focused state of this task.
*/
@@ -515,15 +493,18 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
@Override
public void onPrepareLaunchTargetForEnterAnimation() {
// These values will be animated in when onStartLaunchTargetEnterAnimation() is called
- setDim(0);
+ setDimAlpha(0);
mActionButtonView.setAlpha(0f);
}
@Override
public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
ReferenceCountedTrigger postAnimationTrigger) {
+ // Un-dim the view before/while launching the target
+ AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT)
+ .setListener(postAnimationTrigger.decrementOnAnimationEnd());
postAnimationTrigger.increment();
- animateDimToProgress(duration, postAnimationTrigger.decrementOnAnimationEnd());
+ animateDimAlpha(0, animation);
if (screenPinningEnabled) {
showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
@@ -533,12 +514,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
@Override
public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
ReferenceCountedTrigger postAnimationTrigger) {
- if (mDimAlpha > 0) {
- ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), 0);
- anim.setDuration(duration);
- anim.setInterpolator(Interpolators.ALPHA_OUT);
- anim.start();
- }
+ // Un-dim the view before/while launching the target
+ AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
+ animateDimAlpha(0, animation);
postAnimationTrigger.increment();
hideActionButton(true /* fadeOut */, duration,
@@ -549,15 +527,17 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
/**** TaskCallbacks Implementation ****/
public void onTaskBound(Task t) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
mTask = t;
mTask.addCallback(this);
+ mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
}
@Override
public void onTaskDataLoaded(Task task) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask);
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mTaskDataLoaded = true;
}
@@ -572,13 +552,24 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
@Override
public void onTaskStackIdChanged() {
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled);
+ mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
}
/**** View.OnClickListener Implementation ****/
@Override
public void onClick(final View v) {
+ if (mIsDisabledInSafeMode) {
+ Context context = getContext();
+ String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
+ if (mDisabledAppToast != null) {
+ mDisabledAppToast.cancel();
+ }
+ mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+ mDisabledAppToast.show();
+ return;
+ }
+
boolean screenPinningRequested = false;
if (v == mActionButtonView) {
// Reset the translation of the action button before we animate it out
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index c91a833e943a..bb56a520fbdd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -36,6 +36,7 @@ import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewAnimationUtils;
+import android.view.ViewDebug;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -74,6 +75,8 @@ public class TaskViewHeader extends FrameLayout
private Paint mHighlightPaint = new Paint();
private Paint mBackgroundPaint = new Paint();
+ private int mColor;
+ private float mDimAlpha;
public HighlightColorDrawable() {
mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
@@ -83,15 +86,19 @@ public class TaskViewHeader extends FrameLayout
}
public void setColorAndDim(int color, float dimAlpha) {
- mBackgroundPaint.setColor(color);
-
- ColorUtils.colorToHSL(color, mTmpHSL);
- // TODO: Consider using the saturation of the color to adjust the lightness as well
- mTmpHSL[2] = Math.min(1f,
- mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
- mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
-
- invalidateSelf();
+ if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
+ mColor = color;
+ mDimAlpha = dimAlpha;
+ mBackgroundPaint.setColor(color);
+
+ ColorUtils.colorToHSL(color, mTmpHSL);
+ // TODO: Consider using the saturation of the color to adjust the lightness as well
+ mTmpHSL[2] = Math.min(1f,
+ mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+ mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+
+ invalidateSelf();
+ }
}
@Override
@@ -121,6 +128,10 @@ public class TaskViewHeader extends FrameLayout
public int getOpacity() {
return PixelFormat.OPAQUE;
}
+
+ public int getColor() {
+ return mColor;
+ }
}
Task mTask;
@@ -139,9 +150,11 @@ public class TaskViewHeader extends FrameLayout
ProgressBar mFocusTimerIndicator;
// Header drawables
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
int mHighlightHeight;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
@@ -153,6 +166,7 @@ public class TaskViewHeader extends FrameLayout
Drawable mDarkInfoIcon;
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
+ int mDisabledTaskBarBackgroundColor;
int mMoveTaskTargetStackId = INVALID_STACK_ID;
// Header background
@@ -195,6 +209,8 @@ public class TaskViewHeader extends FrameLayout
mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
+ mDisabledTaskBarBackgroundColor =
+ context.getColor(R.color.recents_task_bar_disabled_background_color);
// Configure the background and dim
mBackground = new HighlightColorDrawable();
@@ -331,17 +347,17 @@ public class TaskViewHeader extends FrameLayout
*/
void setDimAlpha(float dimAlpha) {
mDimAlpha = dimAlpha;
- updateBackgroundColor(dimAlpha);
+ updateBackgroundColor(mBackground.getColor(), dimAlpha);
}
/**
* Updates the background and highlight colors for this header.
*/
- private void updateBackgroundColor(float dimAlpha) {
+ private void updateBackgroundColor(int color, float dimAlpha) {
if (mTask != null) {
- mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+ mBackground.setColorAndDim(color, dimAlpha);
// TODO: Consider using the saturation of the color to adjust the lightness as well
- ColorUtils.colorToHSL(mTask.colorPrimary, mTmpHSL);
+ ColorUtils.colorToHSL(color, mTmpHSL);
mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
@@ -350,12 +366,15 @@ public class TaskViewHeader extends FrameLayout
}
/** Binds the bar view to the task */
- public void rebindToTask(Task t, boolean touchExplorationEnabled) {
+ public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
- updateBackgroundColor(mDimAlpha);
+ int primaryColor = disabledInSafeMode
+ ? mDisabledTaskBarBackgroundColor
+ : t.colorPrimary;
+ updateBackgroundColor(primaryColor, mDimAlpha);
if (t.icon != null) {
mIconView.setImageDrawable(t.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index f90951e4ccd1..0fec9c38b5a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -21,13 +21,17 @@ import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.model.Task;
@@ -39,27 +43,40 @@ import com.android.systemui.recents.model.Task;
*/
public class TaskViewThumbnail extends View {
+
+ private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
+ private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
+
private Task mTask;
// Drawing
+ @ViewDebug.ExportedProperty(category="recents")
Rect mThumbnailRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
int mCornerRadius;
+ @ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
+ Paint mBgFillPaint = new Paint();
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
// Task bar clipping, the top of this thumbnail can be clipped against the opaque header
// bar that overlaps this thumbnail
View mTaskBar;
+ @ViewDebug.ExportedProperty(category="recents")
Rect mClipRect = new Rect();
// Visibility optimization, if the thumbnail height is less than the height of the header
// bar for the task view, then just mark this thumbnail view as invisible
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInvisible;
+ @ViewDebug.ExportedProperty(category="recents")
+ boolean mDisabledInSafeMode;
+
public TaskViewThumbnail(Context context) {
this(context, null);
}
@@ -79,6 +96,7 @@ public class TaskViewThumbnail extends View {
mDrawPaint.setAntiAlias(true);
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
+ mBgFillPaint.setColor(Color.WHITE);
}
/**
@@ -100,10 +118,39 @@ public class TaskViewThumbnail extends View {
if (mInvisible) {
return;
}
- // Draw the thumbnail with the rounded corners
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
- mCornerRadius,
- mCornerRadius, mDrawPaint);
+
+ int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
+ mThumbnailRect.height());
+ if (thumbnailHeight >= mTaskViewRect.height()) {
+ // The thumbnail fills the full task view bounds, so just draw it
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ } else {
+ int count = 0;
+ if (thumbnailHeight > 0) {
+ // The thumbnail only covers part of the task view bounds, so fill in the
+ // non-thumbnail space with the default background color. This is the equivalent of
+ // the GL border texture mode.
+ count = canvas.save();
+
+ // Since we only want the top corners to be rounded, draw slightly beyond the
+ // thumbnail height, but clip to the thumbnail height
+ canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+ mCornerRadius, mCornerRadius, mDrawPaint);
+ }
+
+ // In the remaining space, draw the background color
+ canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
+ Region.Op.REPLACE);
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
+ mBgFillPaint);
+
+ if (thumbnailHeight > 0) {
+ canvas.restoreToCount(count);
+ }
+ }
}
/** Sets the thumbnail to a given bitmap. */
@@ -128,9 +175,27 @@ public class TaskViewThumbnail extends View {
}
int mul = (int) ((1.0f - mDimAlpha) * 255);
if (mBitmapShader != null) {
- mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setColor(0xffffffff);
+ if (mDisabledInSafeMode) {
+ // Brightness: C-new = C-old*(1-amount) + amount
+ TMP_FILTER_COLOR_MATRIX.setSaturation(0);
+ float scale = 1f - mDimAlpha;
+ float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = mDimAlpha;
+ mat[9] = mDimAlpha;
+ mat[14] = mDimAlpha;
+ TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
+ mDrawPaint.setColorFilter(filter);
+ mBgFillPaint.setColorFilter(filter);
+ } else {
+ mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+ mDrawPaint.setColorFilter(mLightingColorFilter);
+ mDrawPaint.setColor(0xFFffffff);
+ mBgFillPaint.setColorFilter(mLightingColorFilter);
+ }
} else {
int grey = mul;
mDrawPaint.setColorFilter(null);
@@ -196,10 +261,14 @@ public class TaskViewThumbnail extends View {
}
/** Binds the thumbnail view to the task */
- void rebindToTask(Task t) {
+ void rebindToTask(Task t, boolean disabledInSafeMode) {
mTask = t;
+ mDisabledInSafeMode = disabledInSafeMode;
if (t.thumbnail != null) {
setThumbnail(t.thumbnail);
+ if (t.colorBackground != 0) {
+ mBgFillPaint.setColor(t.colorBackground);
+ }
} else {
setThumbnail(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 32878b0afcb7..d3d2dbed99d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -86,13 +86,10 @@ public class TaskViewTransform {
public float translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
+ public float dimAlpha = 0f;
public boolean visible = false;
- // This is the relative task progress of this task, relative to the stack scroll at which this
- // transform was computed
- public float p = 0f;
-
// This is a window-space rect used for positioning the task in the stack and freeform workspace
public RectF rect = new RectF();
@@ -104,7 +101,7 @@ public class TaskViewTransform {
scale = tv.getScaleX();
alpha = tv.getAlpha();
visible = true;
- p = tv.getTaskProgress();
+ dimAlpha = tv.getDimAlpha();
rect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
}
@@ -116,7 +113,7 @@ public class TaskViewTransform {
scale = other.scale;
alpha = other.alpha;
visible = other.visible;
- p = other.p;
+ dimAlpha = other.dimAlpha;
rect.set(other.rect);
}
@@ -127,9 +124,9 @@ public class TaskViewTransform {
translationZ = 0;
scale = 1f;
alpha = 1f;
+ dimAlpha = 0f;
visible = false;
rect.setEmpty();
- p = 0f;
}
/** Convenience functions to compare against current property values */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7d37ad22223f..2bebac2c5136 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -38,6 +38,7 @@ import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
@@ -116,7 +117,7 @@ public abstract class BaseStatusBar extends SystemUI implements
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener {
public static final String TAG = "StatusBar";
- public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_REMOTE_INPUT =
@@ -194,6 +195,7 @@ public abstract class BaseStatusBar extends SystemUI implements
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
private UserManager mUserManager;
+ private int mDensity;
// UI-specific methods
@@ -227,6 +229,7 @@ public abstract class BaseStatusBar extends SystemUI implements
protected int mState;
protected boolean mBouncerShowing;
protected boolean mShowLockscreenNotifications;
+ protected boolean mAllowLockscreenRemoteInput;
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
protected DismissView mDismissView;
@@ -398,11 +401,26 @@ public abstract class BaseStatusBar extends SystemUI implements
}
p = p.getParent();
}
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
- if (riv == null) {
+ if (riv == null || row == null) {
return false;
}
+ row.setUserExpanded(true);
+
+ if (isLockscreenPublicMode() && !mAllowLockscreenRemoteInput) {
+ onLockedRemoteInput(row, view);
+ return true;
+ }
+
riv.setVisibility(View.VISIBLE);
int cx = view.getLeft() + view.getWidth() / 2;
int cy = view.getTop() + view.getHeight() / 2;
@@ -617,6 +635,10 @@ public abstract class BaseStatusBar extends SystemUI implements
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
mSettingsObserver,
UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -633,18 +655,22 @@ public abstract class BaseStatusBar extends SystemUI implements
mLocale = currentConfig.locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
mFontScale = currentConfig.fontScale;
+ mDensity = currentConfig.densityDpi;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
// Connect in to the status bar manager service
mCommandQueue = new CommandQueue(this);
- int[] switches = new int[8];
+ int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
ArrayList<String> iconSlots = new ArrayList<>();
ArrayList<StatusBarIcon> icons = new ArrayList<>();
+ Rect fullscreenStackBounds = new Rect();
+ Rect dockedStackBounds = new Rect();
try {
- mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders);
+ mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
+ fullscreenStackBounds, dockedStackBounds);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
@@ -653,7 +679,8 @@ public abstract class BaseStatusBar extends SystemUI implements
mSettingsObserver.onChange(false); // set up
disable(switches[0], switches[6], false /* animate */);
- setSystemUiVisibility(switches[1], 0xffffffff);
+ setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+ fullscreenStackBounds, dockedStackBounds);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
@@ -813,8 +840,13 @@ public abstract class BaseStatusBar extends SystemUI implements
final Locale locale = mContext.getResources().getConfiguration().locale;
final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
final float fontScale = newConfig.fontScale;
-
- if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
+ final int density = newConfig.densityDpi;
+ if (density != mDensity || mFontScale != fontScale) {
+ reInflateViews();
+ mDensity = density;
+ mFontScale = fontScale;
+ }
+ if (! locale.equals(mLocale) || ld != mLayoutDirection) {
if (DEBUG) {
Log.v(TAG, String.format(
"config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
@@ -826,6 +858,21 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
+ protected void reInflateViews() {
+ ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ for (int i = 0; i < activeNotifications.size(); i++) {
+ Entry entry = activeNotifications.get(i);
+ boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
+ entry.row.reInflateViews();
+ if (exposedGuts) {
+ mNotificationGutsExposed = entry.row.getGuts();
+ bindGuts(entry.row);
+ }
+ entry.cacheContentViews(mContext, null /* updatedNotification */);
+ inflateViews(entry, mStackScroller);
+ }
+ }
+
protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
final String _pkg = n.getPackageName();
@@ -1273,6 +1320,8 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
+ protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
+
@Override
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
}
@@ -1909,6 +1958,10 @@ public abstract class BaseStatusBar extends SystemUI implements
mShowLockscreenNotifications = show;
}
+ protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
+ mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
+ }
+
private void updateLockscreenNotificationSetting() {
final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
@@ -1918,7 +1971,14 @@ public abstract class BaseStatusBar extends SystemUI implements
null /* admin */, mCurrentUserId);
final boolean allowedByDpm = (dpmFlags
& DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+
+ final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+ 0,
+ mCurrentUserId) != 0;
+
setShowLockscreenNotifications(show && allowedByDpm);
+ setLockScreenAllowRemoteInput(remoteInput);
}
protected abstract void setAreThereNotifications();
@@ -2080,25 +2140,44 @@ public abstract class BaseStatusBar extends SystemUI implements
return false;
}
- if (isSnoozedPackage(sbn)) {
- if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+ boolean inUse = mPowerManager.isScreenOn()
+ && (!mStatusBarKeyguardViewManager.isShowing()
+ || mStatusBarKeyguardViewManager.isOccluded())
+ && !mStatusBarKeyguardViewManager.isInputRestricted();
+ try {
+ inUse = inUse && !mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.d(TAG, "failed to query dream manager", e);
+ }
+
+ if (!inUse) {
+ if (DEBUG) {
+ Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+ }
return false;
}
- if (entry.hasJustLaunchedFullScreenIntent()) {
- if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
- if (sbn.getNotification().fullScreenIntent != null
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
return false;
}
+ if (sbn.getNotification().fullScreenIntent != null) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ return false;
+ } else {
+ return true;
+ }
+ }
- if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
- if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+ if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
return false;
}
@@ -2107,17 +2186,7 @@ public abstract class BaseStatusBar extends SystemUI implements
return false;
}
- boolean inUse = mPowerManager.isScreenOn()
- && (!mStatusBarKeyguardViewManager.isShowing()
- || mStatusBarKeyguardViewManager.isOccluded())
- && !mStatusBarKeyguardViewManager.isInputRestricted();
- try {
- inUse = inUse && !mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.d(TAG, "failed to query dream manager", e);
- }
- if (DEBUG) Log.d(TAG, "peek if device in use: " + inUse);
- return inUse;
+ return true;
}
protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 71347c4ba611..3b960eef84c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -17,12 +17,14 @@
package com.android.systemui.statusbar;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Pair;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -94,7 +96,8 @@ public class CommandQueue extends IStatusBar.Stub {
public void animateExpandNotificationsPanel();
public void animateCollapsePanels(int flags);
public void animateExpandSettingsPanel(String obj);
- public void setSystemUiVisibility(int vis, int mask);
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
public void topAppWindowChanged(boolean visible);
public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
@@ -169,11 +172,19 @@ public class CommandQueue extends IStatusBar.Stub {
}
}
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
- mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = vis;
+ args.argi2 = fullscreenStackVis;
+ args.argi3 = dockedStackVis;
+ args.argi4 = mask;
+ args.arg1 = fullscreenStackBounds;
+ args.arg2 = dockedStackBounds;
+ mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
}
}
@@ -377,7 +388,10 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.animateExpandSettingsPanel((String) msg.obj);
break;
case MSG_SET_SYSTEMUI_VISIBILITY:
- mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mCallbacks.setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
+ args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+ args.recycle();
break;
case MSG_TOP_APP_WINDOW_CHANGED:
mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 84b2031491fd..7422902e593e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -50,11 +51,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
- private final int mNotificationMinHeightLegacy;
- private final int mMaxHeadsUpHeightLegacy;
- private final int mMaxHeadsUpHeight;
- private final int mNotificationMinHeight;
- private final int mNotificationMaxHeight;
+ private int mNotificationMinHeightLegacy;
+ private int mMaxHeadsUpHeightLegacy;
+ private int mMaxHeadsUpHeight;
+ private int mNotificationMinHeight;
+ private int mNotificationMaxHeight;
/** Does this row contain layouts that can adapt to row expansion */
private boolean mExpandable;
@@ -507,6 +508,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mHeadsUpManager = headsUpManager;
}
+ public void reInflateViews() {
+ initDimens();
+ if (mIsSummaryWithChildren) {
+ removeView(mNotificationHeader);
+ mNotificationHeader = null;
+ recreateNotificationHeader();
+ if (mChildrenContainer != null) {
+ mChildrenContainer.reInflateViews();
+ }
+ }
+ if (mGuts != null) {
+ View oldGuts = mGuts;
+ int index = indexOfChild(oldGuts);
+ removeView(oldGuts);
+ mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
+ R.layout.notification_guts, this, false);
+ mGuts.setVisibility(oldGuts.getVisibility());
+ addView(mGuts, index);
+ }
+ mPrivateLayout.reInflateViews();
+ mPublicLayout.reInflateViews();
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -514,6 +538,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
mFalsingManager = FalsingManager.getInstance(context);
+ initDimens();
+ }
+
+ private void initDimens() {
mNotificationMinHeightLegacy = getResources().getDimensionPixelSize(
R.dimen.notification_min_height_legacy);
mNotificationMinHeight = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 76e522e336ca..d5361dd75e83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -248,14 +248,16 @@ public class NotificationContentView extends FrameLayout {
public void reset(boolean resetActualHeight) {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
+ removeView(mContractedChild);
}
if (mExpandedChild != null) {
mExpandedChild.animate().cancel();
+ removeView(mExpandedChild);
}
if (mHeadsUpChild != null) {
mHeadsUpChild.animate().cancel();
+ removeView(mHeadsUpChild);
}
- removeAllViews();
mContractedChild = null;
mExpandedChild = null;
mHeadsUpChild = null;
@@ -494,7 +496,8 @@ public class NotificationContentView extends FrameLayout {
return VISIBLE_TYPE_EXPANDED;
}
} else {
- if (viewHeight <= mContractedChild.getHeight() || noExpandedChild) {
+ if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
+ && (!mIsChildInGroup || !mContainingNotification.isExpanded()))) {
return VISIBLE_TYPE_CONTRACTED;
} else {
return VISIBLE_TYPE_EXPANDED;
@@ -569,6 +572,9 @@ public class NotificationContentView extends FrameLayout {
if (mIsChildInGroup) {
mSingleLineView = mHybridViewManager.bindFromNotification(
mSingleLineView, mStatusBarNotification.getNotification());
+ } else if (mSingleLineView != null) {
+ removeView(mSingleLineView);
+ mSingleLineView = null;
}
}
@@ -685,4 +691,12 @@ public class NotificationContentView extends FrameLayout {
public void requestSelectLayout(boolean needsAnimation) {
selectLayout(needsAnimation, false);
}
+
+ public void reInflateViews() {
+ if (mIsChildInGroup && mSingleLineView != null) {
+ removeView(mSingleLineView);
+ mSingleLineView = null;
+ updateSingleLineView();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index dd6d6f381c02..7346becdb0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -113,15 +113,15 @@ public class NotificationGuts extends LinearLayout {
? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
com.android.internal.R.string.default_notification_topic_label))
: sbn.getNotification().getTopic();
- boolean doesAppUseTopics = false;
+ boolean doesUserUseTopics = false;
try {
- doesAppUseTopics =
- mINotificationManager.doesAppUseTopics(sbn.getPackageName(), sbn.getUid());
+ doesUserUseTopics =
+ mINotificationManager.doesUserUseTopics(sbn.getPackageName(), sbn.getUid());
} catch (RemoteException e) {}
- final boolean appUsesTopics = doesAppUseTopics;
+ final boolean userUsesTopics = doesUserUseTopics;
mApplyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
- if (appUsesTopics) {
+ if (userUsesTopics) {
mApplyToTopic.setChecked(true);
}
final View applyToApp = row.findViewById(R.id.apply_to_app);
@@ -156,7 +156,7 @@ public class NotificationGuts extends LinearLayout {
updateTitleAndSummary(progress);
if (fromUser) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_MODIFY_IMPORTANCE_SLIDER);
- if (appUsesTopics) {
+ if (userUsesTopics) {
mApplyToTopic.setVisibility(View.VISIBLE);
mApplyToTopic.setText(
mContext.getString(R.string.apply_to_topic, mTopic.getLabel()));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 6801e5f1017d..9aa5ea0c3032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -21,6 +21,7 @@ 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.Drawable;
import android.telephony.SubscriptionInfo;
@@ -80,6 +81,7 @@ public class SignalClusterView
private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
private int mIconTint = Color.WHITE;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
ViewGroup mEthernetGroup, mWifiGroup;
View mNoSimsCombo;
@@ -490,23 +492,31 @@ public class SignalClusterView
}
}
- public void setIconTint(int tint, float darkIntensity) {
- boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
+ || !mTintArea.equals(tintArea);
mIconTint = tint;
mDarkIntensity = darkIntensity;
+ mTintArea.set(tintArea);
if (changed && isAttachedToWindow()) {
applyIconTint();
}
}
private void applyIconTint() {
- setTint(mVpn, mIconTint);
- setTint(mAirplane, mIconTint);
- applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
- applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
- applyDarkIntensity(mDarkIntensity, mEthernet, mEthernetDark);
+ setTint(mVpn, StatusBarIconController.getTint(mTintArea, mVpn, mIconTint));
+ setTint(mAirplane, StatusBarIconController.getTint(mTintArea, mAirplane, mIconTint));
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity),
+ mNoSims, mNoSimsDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
+ mWifi, mWifiDark);
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
+ mEthernet, mEthernetDark);
for (int i = 0; i < mPhoneStates.size(); i++) {
- mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
+ mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
}
}
@@ -613,9 +623,11 @@ public class SignalClusterView
}
}
- public void setIconTint(int tint, float darkIntensity) {
- applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
- setTint(mMobileType, tint);
+ public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
+ applyDarkIntensity(
+ StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
+ mMobile, mMobileDark);
+ setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 60c191126116..aa001ed2057c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -45,4 +45,10 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
mInvertHelper.update(dark);
}
}
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ mView.setAlpha(visible ? 1.0f : 0.0f);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index a2b4c5d24442..328f8b55b0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -105,6 +105,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
@Override
public void setVisible(boolean visible) {
+ mView.animate().cancel();
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 5832d86628af..67d31be7f511 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -293,14 +293,19 @@ public class TransformState {
mTransformedView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformedView.setAlpha(visible ? 1.0f : 0.0f);
if (visible) {
- mTransformedView.setTranslationX(0);
- mTransformedView.setTranslationY(0);
- mTransformedView.setScaleX(1.0f);
- mTransformedView.setScaleY(1.0f);
+ resetTransformedView();
}
}
public void prepareFadeIn() {
+ resetTransformedView();
+ }
+
+ private void resetTransformedView() {
+ mTransformedView.setTranslationX(0);
+ mTransformedView.setTranslationY(0);
+ mTransformedView.setScaleX(1.0f);
+ mTransformedView.setScaleY(1.0f);
}
public static TransformState obtain() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
new file mode 100644
index 000000000000..f98b9e586a1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -0,0 +1,126 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightStatusBarController {
+
+ private final StatusBarIconController mIconController;
+ private final BatteryController mBatteryController;
+ private FingerprintUnlockController mFingerprintUnlockController;
+
+ private int mFullscreenStackVisibility;
+ private int mDockedStackVisibility;
+ private boolean mFullscreenLight;
+ private boolean mDockedLight;
+
+ private final Rect mLastFullscreenBounds = new Rect();
+ private final Rect mLastDockedBounds = new Rect();
+
+ public LightStatusBarController(StatusBarIconController iconController,
+ BatteryController batteryController) {
+ mIconController = iconController;
+ mBatteryController = batteryController;
+ }
+
+ public void setFingerprintUnlockController(
+ FingerprintUnlockController fingerprintUnlockController) {
+ mFingerprintUnlockController = fingerprintUnlockController;
+ }
+
+ public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+ int statusBarMode) {
+ int oldFullscreen = mFullscreenStackVisibility;
+ int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+ int diffFullscreen = newFullscreen ^ oldFullscreen;
+ int oldDocked = mDockedStackVisibility;
+ int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+ int diffDocked = newDocked ^ oldDocked;
+ if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ || sbModeChanged
+ || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+ || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+ mFullscreenLight = isLight(newFullscreen, statusBarMode);
+ mDockedLight = isLight(newDocked, statusBarMode);
+ update(fullscreenStackBounds, dockedStackBounds);
+ }
+ mFullscreenStackVisibility = newFullscreen;
+ mDockedStackVisibility = newDocked;
+ mLastFullscreenBounds.set(fullscreenStackBounds);
+ mLastDockedBounds.set(dockedStackBounds);
+ }
+
+ private boolean isLight(int vis, int statusBarMode) {
+ boolean isTransparentBar = (statusBarMode == MODE_TRANSPARENT
+ || statusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
+ boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+ boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
+ return allowLight && light;
+ }
+
+ private boolean animateChange() {
+ if (mFingerprintUnlockController == null) {
+ return false;
+ }
+ int unlockMode = mFingerprintUnlockController.getMode();
+ return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ private void update(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+ // If both are light or fullscreen is light and there is no docked stack, all icons get
+ // dark.
+ if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDarkArea(null);
+ mIconController.setIconsDark(true, animateChange());
+
+ }
+
+ // If no one is light or the fullscreen is not light and there is no docked stack,
+ // all icons become white.
+ else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+ mIconController.setIconsDark(false, animateChange());
+
+ }
+
+ // Not the same for every stack, magic!
+ else {
+ Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+ if (bounds.isEmpty()) {
+ mIconController.setIconsDarkArea(null);
+ } else {
+ mIconController.setIconsDarkArea(bounds);
+ }
+ mIconController.setIconsDark(true, animateChange());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 03a597c46bc5..6e345f088b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -32,6 +33,7 @@ public class NotificationIconAreaController {
protected View mNotificationIconArea;
private IconMerger mNotificationIcons;
private ImageView mMoreIcon;
+ private final Rect mTintArea = new Rect();
public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
mPhoneStatusBar = phoneStatusBar;
@@ -67,6 +69,20 @@ public class NotificationIconAreaController {
}
/**
+ * See {@link StatusBarIconController#setIconsDarkArea}.
+ *
+ * @param tintArea the area in which to tint the icons, specified in screen coordinates
+ */
+ public void setTintArea(Rect tintArea) {
+ if (tintArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(tintArea);
+ }
+ applyNotificationIconsTint();
+ }
+
+ /**
* Sets the color that should be used to tint any icons in the notification area. If this
* method is not called, the default tint is {@link Color#WHITE}.
*/
@@ -145,7 +161,8 @@ public class NotificationIconAreaController {
boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
if (colorize) {
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(
+ StatusBarIconController.getTint(mTintArea, v, mIconTint)));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f822bd535851..8f0f51f502b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.StatusBarManager;
@@ -35,13 +34,11 @@ import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
-
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
@@ -51,7 +48,6 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.qs.QSContainer;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -91,13 +87,10 @@ public class NotificationPanelView extends PanelView implements
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
- protected BaseStatusBarHeader mHeader;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
private QSContainer mQsContainer;
- private QSPanel mQsPanel;
private KeyguardStatusView mKeyguardStatusView;
- private ObservableScrollView mScrollView;
private TextView mClockView;
private View mReserveNotificationSpace;
private View mQsNavbarScrim;
@@ -168,15 +161,12 @@ public class NotificationPanelView extends PanelView implements
* If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
* need to take this into account in our panel height calculation.
*/
- private int mScrollYOverride = -1;
private boolean mQsAnimatorExpand;
private boolean mIsLaunchTransitionFinished;
private boolean mIsLaunchTransitionRunning;
private Runnable mLaunchAnimationEndRunnable;
private boolean mOnlyAffordanceInThisMotion;
private boolean mKeyguardStatusViewAnimating;
- private boolean mHeaderAnimating;
- private ObjectAnimator mQsContainerAnimator;
private ValueAnimator mQsSizeChangeAnimator;
private boolean mShadeEmpty;
@@ -223,19 +213,11 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- ViewStub stub = (ViewStub) findViewById(R.id.status_bar_header);
- stub.setLayoutResource(R.layout.quick_status_bar_expanded_header);
- mHeader = (BaseStatusBarHeader) stub.inflate();
- mHeader.setOnClickListener(this);
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
mQsContainer = (QSContainer) findViewById(R.id.quick_settings_container);
- mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
+ mQsContainer.getHeader().setOnClickListener(this);
mClockView = (TextView) findViewById(R.id.clock_view);
- mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
- mScrollView.setListener(this);
- mScrollView.setFocusable(false);
- mReserveNotificationSpace = findViewById(R.id.reserve_notification_space);
mNotificationContainerParent = (NotificationsQuickSettingsContainer)
findViewById(R.id.notification_container_parent);
mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -243,7 +225,7 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.setOnHeightChangedListener(this);
mNotificationStackScroller.setOverscrollTopChangedListener(this);
mNotificationStackScroller.setOnEmptySpaceClickListener(this);
- mNotificationStackScroller.setScrollView(mScrollView);
+ mNotificationStackScroller.setQsContainer(mQsContainer);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
@@ -285,12 +267,12 @@ public class NotificationPanelView extends PanelView implements
public void updateResources() {
int panelWidth = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mHeader.getLayoutParams();
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsContainer.getLayoutParams();
if (lp.width != panelWidth) {
lp.width = panelWidth;
lp.gravity = panelGravity;
- mHeader.setLayoutParams(lp);
- mHeader.post(mUpdateHeader);
+ mQsContainer.setLayoutParams(lp);
+ mQsContainer.post(mUpdateHeader);
}
lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
@@ -299,13 +281,6 @@ public class NotificationPanelView extends PanelView implements
lp.gravity = panelGravity;
mNotificationStackScroller.setLayoutParams(lp);
}
-
- lp = (FrameLayout.LayoutParams) mScrollView.getLayoutParams();
- if (lp.width != panelWidth) {
- lp.width = panelWidth;
- lp.gravity = panelGravity;
- mScrollView.setLayoutParams(lp);
- }
}
@Override
@@ -318,8 +293,8 @@ public class NotificationPanelView extends PanelView implements
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mHeader.getCollapsedHeight() + mQsPeekHeight;
- mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getDesiredHeight();
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getHeader().getHeight();
+ mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
positionClockAndNotifications();
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
@@ -361,7 +336,7 @@ public class NotificationPanelView extends PanelView implements
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQsContainer.setHeightOverride(height - mHeader.getExpandedHeight());
+ mQsContainer.setHeightOverride(height);
}
});
mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
@@ -381,7 +356,7 @@ public class NotificationPanelView extends PanelView implements
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
- int bottom = mHeader.getCollapsedHeight();
+ int bottom = mQsContainer.getHeader().getHeight();
stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
? bottom + mQsPeekHeight
: mKeyguardStatusBar.getHeight();
@@ -485,7 +460,7 @@ public class NotificationPanelView extends PanelView implements
public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
mQsExpansionEnabled = qsExpansionEnabled;
- mHeader.setClickable(qsExpansionEnabled);
+ mQsContainer.setHeaderClickable(qsExpansionEnabled);
}
@Override
@@ -676,17 +651,6 @@ public class NotificationPanelView extends PanelView implements
}
}
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-
- // Block request when interacting with the scroll view so we can still intercept the
- // scrolling when QS is expanded.
- if (mScrollView.isHandlingTouchEvent()) {
- return;
- }
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
float vel = getCurrentVelocity();
final boolean expandsQs = flingExpandsQs(vel);
@@ -808,7 +772,7 @@ public class NotificationPanelView extends PanelView implements
}
private boolean isInQsArea(float x, float y) {
- return (x >= mScrollView.getX() && x <= mScrollView.getX() + mScrollView.getWidth()) &&
+ return (x >= mQsContainer.getX() && x <= mQsContainer.getX() + mQsContainer.getWidth()) &&
(y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -948,7 +912,7 @@ public class NotificationPanelView extends PanelView implements
amount = 0f;
}
float rounded = amount >= 1f ? amount : 0f;
- mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+ setOverScrolling(rounded != 0f && isRubberbanded);
mQsExpansionFromOverscroll = rounded != 0f;
mLastOverscroll = rounded;
updateQsState();
@@ -964,12 +928,17 @@ public class NotificationPanelView extends PanelView implements
@Override
public void run() {
mStackScrollerOverscrolling = false;
- mQsExpansionFromOverscroll = false;
+ setOverScrolling(false);
updateQsState();
}
}, false /* isClick */);
}
+ private void setOverScrolling(boolean overscrolling) {
+ mStackScrollerOverscrolling = overscrolling;
+ mQsContainer.setOverscrolling(overscrolling);
+ }
+
private void onQsExpansionStarted() {
onQsExpansionStarted(0);
}
@@ -979,11 +948,7 @@ public class NotificationPanelView extends PanelView implements
cancelHeightAnimator();
// Reset scroll position and apply that position to the expanded height.
- float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
- if (mScrollView.getScrollY() != 0) {
- mScrollYOverride = mScrollView.getScrollY();
- }
- mScrollView.scrollTo(0, 0);
+ float height = mQsExpansionHeight - overscrollAmount;
setQsExpansion(height);
requestPanelHeightUpdate();
}
@@ -995,9 +960,7 @@ public class NotificationPanelView extends PanelView implements
updateQsState();
requestPanelHeightUpdate();
mFalsingManager.setQsExpanded(expanded);
- mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
mStatusBar.setQsExpanded(expanded);
- mQsPanel.setExpanded(expanded);
mNotificationContainerParent.setQsExpanded(expanded);
}
}
@@ -1011,15 +974,18 @@ public class NotificationPanelView extends PanelView implements
mStatusBarState = statusBarState;
mKeyguardShowing = keyguardShowing;
+ mQsContainer.setKeyguardShowing(mKeyguardShowing);
if (goingToFullShade || (oldState == StatusBarState.KEYGUARD
&& statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
- animateHeaderSlidingIn();
+ long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
+ ? 0 : mStatusBar.calculateGoingToFullShadeDelay();
+ mQsContainer.animateHeaderSlidingIn(delay);
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- animateHeaderSlidingOut();
+ mQsContainer.animateHeaderSlidingOut();
} else {
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
@@ -1050,95 +1016,6 @@ public class NotificationPanelView extends PanelView implements
}
};
- private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
- = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeaderAnimating = false;
- mQsContainerAnimator = null;
- mQsContainer.removeOnLayoutChangeListener(mQsContainerAnimatorUpdater);
- }
- };
-
- private final OnLayoutChangeListener mQsContainerAnimatorUpdater
- = new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- int oldHeight = oldBottom - oldTop;
- int height = bottom - top;
- if (height != oldHeight && mQsContainerAnimator != null) {
- PropertyValuesHolder[] values = mQsContainerAnimator.getValues();
- float newEndValue = mHeader.getCollapsedHeight() + mQsPeekHeight - height - top;
- float newStartValue = -height - top;
- values[0].setFloatValues(newStartValue, newEndValue);
- mQsContainerAnimator.setCurrentPlayTime(mQsContainerAnimator.getCurrentPlayTime());
- }
- }
- };
-
- private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
- = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
- ? 0
- : mStatusBar.calculateGoingToFullShadeDelay();
- mHeader.setTranslationY(-mHeader.getCollapsedHeight() - mQsPeekHeight);
- mHeader.animate()
- .translationY(0f)
- .setStartDelay(delay)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- mQsContainer.setY(-mQsContainer.getHeight());
- mQsContainerAnimator = ObjectAnimator.ofFloat(mQsContainer, View.TRANSLATION_Y,
- mQsContainer.getTranslationY(),
- mHeader.getCollapsedHeight() + mQsPeekHeight - mQsContainer.getHeight()
- - mQsContainer.getTop());
- mQsContainerAnimator.setStartDelay(delay);
- mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
- mQsContainerAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
- mQsContainerAnimator.start();
- mQsContainer.addOnLayoutChangeListener(mQsContainerAnimatorUpdater);
- return true;
- }
- };
-
- private void animateHeaderSlidingIn() {
- // If the QS is already expanded we don't need to slide in the header as it's already
- // visible.
- if (!mQsExpanded) {
- mHeaderAnimating = true;
- getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
- }
- }
-
- private void animateHeaderSlidingOut() {
- mHeaderAnimating = true;
- mHeader.animate().y(-mHeader.getHeight())
- .setStartDelay(0)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeader.animate().setListener(null);
- mHeaderAnimating = false;
- updateQsState();
- }
- })
- .start();
- mQsContainer.animate()
- .y(-mQsContainer.getHeight())
- .setStartDelay(0)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- }
-
private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
@Override
public void run() {
@@ -1262,19 +1139,10 @@ public class NotificationPanelView extends PanelView implements
}
private void updateQsState() {
- boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
- mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
- ? View.VISIBLE
- : View.INVISIBLE);
- mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
- || (mQsExpanded && !mStackScrollerOverscrolling));
+ mQsContainer.setExpanded(mQsExpanded);
mNotificationStackScroller.setScrollingEnabled(
mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
- mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsContainer.setVisibility(
- mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
- mScrollView.setTouchEnabled(mQsExpanded);
updateEmptyShadeView();
mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded
&& !mStackScrollerOverscrolling && mQsScrimEnabled
@@ -1298,11 +1166,10 @@ public class NotificationPanelView extends PanelView implements
}
}
mQsExpansionHeight = height;
- mHeader.setExpansion(getHeaderExpansionFraction());
- setQsTranslation(height);
+ updateQsExpansion();
requestScrollerTopPaddingUpdate(false /* animate */);
if (mKeyguardShowing) {
- updateHeaderKeyguard();
+ updateHeaderKeyguardAlpha();
}
if (mStatusBarState == StatusBarState.SHADE_LOCKED
|| mStatusBarState == StatusBarState.KEYGUARD) {
@@ -1329,6 +1196,10 @@ public class NotificationPanelView extends PanelView implements
}
}
+ private void updateQsExpansion() {
+ mQsContainer.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
+ }
+
private String getKeyguardOrLockScreenString() {
if (mStatusBarState == StatusBarState.KEYGUARD) {
return getContext().getString(R.string.accessibility_desc_lock_screen);
@@ -1337,23 +1208,6 @@ public class NotificationPanelView extends PanelView implements
}
}
- private float getHeaderExpansionFraction() {
- if (!mKeyguardShowing) {
- return getQsExpansionFraction();
- } else {
- return 1f;
- }
- }
-
- private void setQsTranslation(float height) {
- if (!mHeaderAnimating) {
- mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation());
- }
- if (mKeyguardShowing && !mHeaderAnimating) {
- mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0));
- }
- }
-
private float calculateQsTopPadding() {
if (mKeyguardShowing
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -1372,7 +1226,7 @@ public class NotificationPanelView extends PanelView implements
mQsMinExpansionHeight, max);
} else if (mQsSizeChangeAnimator != null) {
return (int) mQsSizeChangeAnimator.getAnimatedValue();
- } else if (mKeyguardShowing && mScrollYOverride == -1) {
+ } else if (mKeyguardShowing) {
// We can only do the smoother transition on Keyguard when we also are not collapsing
// from a scrolled quick settings.
@@ -1386,7 +1240,6 @@ public class NotificationPanelView extends PanelView implements
private void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
- mScrollView.getScrollY(),
mAnimateNextTopPaddingChange || animate,
mKeyguardShowing
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
@@ -1428,7 +1281,6 @@ public class NotificationPanelView extends PanelView implements
boolean isClick) {
float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
if (target == mQsExpansionHeight) {
- mScrollYOverride = -1;
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
@@ -1438,7 +1290,6 @@ public class NotificationPanelView extends PanelView implements
if (belowFalsingThreshold) {
vel = 0;
}
- mScrollView.setBlockFlinging(true);
ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
if (isClick) {
animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
@@ -1458,8 +1309,6 @@ public class NotificationPanelView extends PanelView implements
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mScrollView.setBlockFlinging(false);
- mScrollYOverride = -1;
mQsExpansionAnimator = null;
if (onFinishRunnable != null) {
onFinishRunnable.run();
@@ -1478,11 +1327,11 @@ public class NotificationPanelView extends PanelView implements
if (!mQsExpansionEnabled || mCollapsedOnDown) {
return false;
}
- View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
+ View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer.getHeader();
boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
- return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y);
+ return onHeader || (yDiff < 0 && isInQsArea(x, y));
} else {
return onHeader;
}
@@ -1494,7 +1343,7 @@ public class NotificationPanelView extends PanelView implements
return mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mNotificationStackScroller.isScrolledToBottom();
} else {
- return mScrollView.isScrolledToBottom();
+ return true;
}
}
@@ -1571,11 +1420,7 @@ public class NotificationPanelView extends PanelView implements
* collapsing QS / the panel when QS was scrolled
*/
private int getTempQsMaxExpansion() {
- int qsTempMaxExpansion = mQsMaxExpansionHeight;
- if (mScrollYOverride != -1) {
- qsTempMaxExpansion -= mScrollYOverride;
- }
- return qsTempMaxExpansion;
+ return mQsMaxExpansionHeight;
}
private int calculatePanelHeightShade() {
@@ -1613,20 +1458,12 @@ public class NotificationPanelView extends PanelView implements
+ notificationHeight;
if (totalHeight > mNotificationStackScroller.getHeight()) {
float fullyCollapsedHeight = maxQsHeight
- + mNotificationStackScroller.getMinStackHeight()
- - getScrollViewScrollY();
+ + mNotificationStackScroller.getMinStackHeight();
totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
}
return (int) totalHeight;
}
- private int getScrollViewScrollY() {
- if (mScrollYOverride != -1 && !mQsTracking) {
- return mScrollYOverride;
- } else {
- return mScrollView.getScrollY();
- }
- }
private void updateNotificationTranslucency() {
float alpha = 1f;
if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
@@ -1678,18 +1515,9 @@ public class NotificationPanelView extends PanelView implements
*/
private void updateHeader() {
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
- updateHeaderKeyguard();
- } else {
- updateHeaderShade();
- }
-
- }
-
- private void updateHeaderShade() {
- if (!mHeaderAnimating) {
- mHeader.setTranslationY(getHeaderTranslation());
+ updateHeaderKeyguardAlpha();
}
- setQsTranslation(mQsExpansionHeight);
+ updateQsExpansion();
}
private float getHeaderTranslation() {
@@ -1744,11 +1572,6 @@ public class NotificationPanelView extends PanelView implements
&& !mDozing ? VISIBLE : INVISIBLE);
}
- private void updateHeaderKeyguard() {
- updateHeaderKeyguardAlpha();
- setQsTranslation(mQsExpansionHeight);
- }
-
private void updateKeyguardBottomAreaAlpha() {
float alpha = Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction());
mKeyguardBottomArea.setAlpha(alpha);
@@ -1781,7 +1604,6 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.onExpansionStopped();
mHeadsUpManager.onExpandingFinished();
mIsExpanding = false;
- mScrollYOverride = -1;
if (isFullyCollapsed()) {
DejankUtils.postAfterTraversal(new Runnable() {
@Override
@@ -1811,9 +1633,8 @@ public class NotificationPanelView extends PanelView implements
}
private void setListening(boolean listening) {
- mHeader.setListening(listening);
+ mQsContainer.setListening(listening);
mKeyguardStatusBar.setListening(listening);
- mQsPanel.setListening(listening);
}
@Override
@@ -1931,7 +1752,7 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onClick(View v) {
- if (v == mHeader) {
+ if (v == mQsContainer.getHeader()) {
onQsExpansionStarted();
if (mQsExpanded) {
flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
@@ -2149,24 +1970,16 @@ public class NotificationPanelView extends PanelView implements
return mConflictingQsExpansionGesture && mQsExpanded;
}
- public void notifyVisibleChildrenChanged() {
- if (mNotificationStackScroller.getNotGoneChildCount() != 0) {
- mReserveNotificationSpace.setVisibility(View.VISIBLE);
- } else {
- mReserveNotificationSpace.setVisibility(View.GONE);
- }
- }
-
public boolean isQsExpanded() {
return mQsExpanded;
}
public boolean isQsDetailShowing() {
- return mQsPanel.isShowingDetail();
+ return mQsContainer.getQsPanel().isShowingDetail();
}
public void closeQsDetail() {
- mQsPanel.closeDetail();
+ mQsContainer.getQsPanel().closeDetail();
}
@Override
@@ -2254,7 +2067,7 @@ public class NotificationPanelView extends PanelView implements
private final Runnable mUpdateHeader = new Runnable() {
@Override
public void run() {
- mHeader.updateEverything();
+ mQsContainer.getHeader().updateEverything();
}
};
@@ -2400,8 +2213,7 @@ public class NotificationPanelView extends PanelView implements
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
- mScrollView.setTranslationX(translation);
- mHeader.setTranslationX(translation);
+ mQsContainer.setTranslationX(translation);
}
private void updateStackHeight(float stackHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index fd28b095c09d..7cc720df1c27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -33,7 +33,7 @@ import com.android.systemui.R;
public class NotificationsQuickSettingsContainer extends FrameLayout
implements ViewStub.OnInflateListener {
- private View mScrollView;
+ private View mQsContainer;
private View mUserSwitcher;
private View mStackScroller;
private View mKeyguardStatusBar;
@@ -47,7 +47,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mScrollView = findViewById(R.id.scroll_view);
+ mQsContainer = findViewById(R.id.quick_settings_container);
mStackScroller = findViewById(R.id.notification_stack_scroller);
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
@@ -58,7 +58,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- reloadWidth(mScrollView);
+ reloadWidth(mQsContainer);
reloadWidth(mStackScroller);
}
@@ -80,11 +80,11 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
- View stackQsTop = mQsExpanded ? mStackScroller : mScrollView;
- View stackQsBottom = !mQsExpanded ? mStackScroller : mScrollView;
+ View stackQsTop = mQsExpanded ? mStackScroller : mQsContainer;
+ View stackQsBottom = !mQsExpanded ? mStackScroller : mQsContainer;
// Invert the order of the scroll view and user switcher such that the notifications receive
// touches first but the panel gets drawn above.
- if (child == mScrollView) {
+ if (child == mQsContainer) {
return super.drawChild(canvas, userSwitcherVisible && statusBarVisible ? mUserSwitcher
: statusBarVisible ? mKeyguardStatusBar
: userSwitcherVisible ? mUserSwitcher
@@ -104,7 +104,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
return super.drawChild(canvas,
stackQsTop,
drawingTime);
- }else {
+ } else {
return super.drawChild(canvas, child, drawingTime);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 50a49a1dbd2c..278dfe6c9314 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -69,6 +69,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
@@ -88,6 +89,7 @@ import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -296,6 +298,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
FingerprintUnlockController mFingerprintUnlockController;
+ LightStatusBarController mLightStatusBarController;
int mNaturalBarHeight = -1;
@@ -340,6 +343,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private long mKeyguardFadingAwayDelay;
private long mKeyguardFadingAwayDuration;
+ // RemoteInputView to be activated after unlock
+ private View mPendingRemoteInputView;
+
int mMaxAllowedKeyguardNotifications;
boolean mExpandedVisible;
@@ -361,6 +367,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// tracking calls to View.setSystemUiVisibility()
int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private final Rect mLastFullscreenStackBounds = new Rect();
+ private final Rect mLastDockedStackBounds = new Rect();
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
@@ -754,19 +762,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
- mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_no_notifications, mStackScroller, false);
- mStackScroller.setEmptyShadeView(mEmptyShadeView);
- 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) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
- clearAllNotifications();
- }
- });
- mStackScroller.setDismissView(mDismissView);
+ inflateEmptyShadeView();
+ inflateDismissView();
mExpandedContents = mStackScroller;
mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
@@ -855,6 +852,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mAccessibilityController = new AccessibilityController(mContext);
mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
mNextAlarmController = new NextAlarmController(mContext);
+ mLightStatusBarController = new LightStatusBarController(mIconController,
+ mBatteryController);
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
@@ -930,6 +929,34 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mStatusBarView;
}
+ @Override
+ protected void reInflateViews() {
+ super.reInflateViews();
+ inflateDismissView();
+ updateClearAll();
+ inflateEmptyShadeView();
+ updateEmptyShadeView();
+ }
+
+ private void inflateEmptyShadeView() {
+ mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_no_notifications, mStackScroller, false);
+ mStackScroller.setEmptyShadeView(mEmptyShadeView);
+ }
+
+ private void inflateDismissView() {
+ 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) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+ clearAllNotifications();
+ }
+ });
+ mStackScroller.setDismissView(mDismissView);
+ }
+
protected void createUserSwitcher() {
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
(ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
@@ -1072,6 +1099,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+ mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
}
@Override
@@ -1483,8 +1511,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
private void updateNotificationShadeForChildren() {
+ // First let's remove all children which don't belong in the parents
ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
- boolean orderChanged = false;
for (int i = 0; i < mStackScroller.getChildCount(); i++) {
View view = mStackScroller.getChildAt(i);
if (!(view instanceof ExpandableNotificationRow)) {
@@ -1496,7 +1524,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
- // lets first remove all undesired children
if (children != null) {
toRemove.clear();
for (ExpandableNotificationRow childRow : children) {
@@ -1509,8 +1536,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.notifyGroupChildRemoved(remove);
}
}
+ }
+
+ // Let's now add all notification children which are missing
+ boolean orderChanged = false;
+ for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+ View view = mStackScroller.getChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
- // We now add all the children which are not in there already
for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
childIndex++) {
ExpandableNotificationRow childView = orderedChildren.get(childIndex);
@@ -1600,12 +1640,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
- protected void updateRowStates() {
- super.updateRowStates();
- mNotificationPanel.notifyVisibleChildrenChanged();
- }
-
- @Override
protected void setAreThereNotifications() {
if (SPEW) {
@@ -2227,7 +2261,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
@Override
public void maybeEscalateHeadsUp() {
- TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries();
+ Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
for (HeadsUpManager.HeadsUpEntry entry : entries) {
final StatusBarNotification sbn = entry.entry.notification;
final Notification notification = sbn.getNotification();
@@ -2513,7 +2547,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
@@ -2522,6 +2557,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
+ boolean sbModeChanged = false;
if (diff != 0) {
// we never set the recents bit via this method, so save the prior state to prevent
// clobbering the bit below
@@ -2555,7 +2591,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
oldVal, newVal, mNavigationBarView.getBarTransitions(),
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
- final boolean sbModeChanged = sbMode != -1;
+ sbModeChanged = sbMode != -1;
final boolean nbModeChanged = nbMode != -1;
boolean checkBarModes = false;
if (sbModeChanged && sbMode != mStatusBarMode) {
@@ -2582,18 +2618,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
- if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) {
- boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT
- || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
- boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
- boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
- boolean animate = mFingerprintUnlockController == null
- || (mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
- mIconController.setIconsDark(allowLight && light, animate);
- }
// restore the recents bit
if (wasRecentsVisible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -2602,6 +2626,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
+
+ mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
+ mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
}
private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
@@ -2729,9 +2756,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void setLightsOn(boolean on) {
Log.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+ mLastFullscreenStackBounds, mLastDockedStackBounds);
} else {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+ View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
+ mLastDockedStackBounds);
}
}
@@ -3553,6 +3583,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
mDraggedDownRow = null;
}
+ mPendingRemoteInputView = null;
mAssistManager.onLockscreenShown();
}
@@ -3669,6 +3700,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public boolean hideKeyguard() {
boolean staying = mLeaveOpenOnKeyguardHide;
setBarState(StatusBarState.SHADE);
+ View viewToClick = null;
if (mLeaveOpenOnKeyguardHide) {
mLeaveOpenOnKeyguardHide = false;
long delay = calculateGoingToFullShadeDelay();
@@ -3677,6 +3709,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mDraggedDownRow.setUserLocked(false);
mDraggedDownRow = null;
}
+ viewToClick = mPendingRemoteInputView;
+ mPendingRemoteInputView = null;
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
@@ -3694,6 +3728,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
updateKeyguardState(staying, false /* fromShadeLocked */);
+ if (viewToClick != null) {
+ viewToClick.callOnClick();
+ }
+
// Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
// visibilities so next time we open the panel we know the correct height already.
if (mQSPanel != null) {
@@ -4057,6 +4095,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mLeaveOpenOnKeyguardHide = true;
showBouncer();
mDraggedDownRow = row;
+ mPendingRemoteInputView = null;
} else {
mNotificationPanel.animateToFullShade(0 /* delay */);
setBarState(StatusBarState.SHADE_LOCKED);
@@ -4065,6 +4104,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
+ protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
+ mLeaveOpenOnKeyguardHide = true;
+ showBouncer();
+ mPendingRemoteInputView = clicked;
+ }
+
+ @Override
public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mState == StatusBarState.KEYGUARD && nowExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 45aae2dd61f0..f61f31e814f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -22,6 +22,7 @@ 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.Icon;
import android.os.Bundle;
import android.os.Handler;
@@ -58,8 +59,8 @@ import java.util.ArrayList;
public class StatusBarIconController extends StatusBarIconList implements Tunable {
public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
-
public static final String ICON_BLACKLIST = "icon_blacklist";
+ public static final int DEFAULT_ICON_TINT = Color.WHITE;
private Context mContext;
private PhoneStatusBar mPhoneStatusBar;
@@ -79,8 +80,11 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
private int mIconSize;
private int mIconHPadding;
- private int mIconTint = Color.WHITE;
+ private int mIconTint = DEFAULT_ICON_TINT;
private float mDarkIntensity;
+ private final Rect mTintArea = new Rect();
+ private static final Rect sTmpRect = new Rect();
+ private static final int[] sTmpInt2 = new int[2];
private boolean mTransitionPending;
private boolean mTintChangePending;
@@ -395,6 +399,25 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
}
}
+ /**
+ * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area.
+ *
+ * @param darkArea the area in which icons should change it's tint, in logical screen
+ * coordinates
+ */
+ public void setIconsDarkArea(Rect darkArea) {
+ if (darkArea == null && mTintArea.isEmpty()) {
+ return;
+ }
+ if (darkArea == null) {
+ mTintArea.setEmpty();
+ } else {
+ mTintArea.set(darkArea);
+ }
+ applyIconTint();
+ mNotificationIconAreaController.setTintArea(darkArea);
+ }
+
public void setIconsDark(boolean dark, boolean animate) {
if (!animate) {
setIconTintInternal(dark ? 1.0f : 0.0f);
@@ -446,14 +469,60 @@ public class StatusBarIconController extends StatusBarIconList implements Tunabl
mPendingDarkIntensity = darkIntensity;
}
+ /**
+ * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
+ * the screen {@param tintArea} in which to apply that tint
+ */
+ public static int getTint(Rect tintArea, View view, int color) {
+ if (isInArea(tintArea, view)) {
+ return color;
+ } else {
+ return DEFAULT_ICON_TINT;
+ }
+ }
+
+ /**
+ * @return the dark intensity to apply to {@param view} depending on the desired dark
+ * {@param intensity} and the screen {@param tintArea} in which to apply that intensity
+ */
+ public static float getDarkIntensity(Rect tintArea, View view, float intensity) {
+ if (isInArea(tintArea, view)) {
+ return intensity;
+ } else {
+ return 0f;
+ }
+ }
+
+ /**
+ * @return true if more than half of the {@param view} area are in {@param area}, false
+ * otherwise
+ */
+ private static boolean isInArea(Rect area, View view) {
+ if (area.isEmpty()) {
+ return true;
+ }
+ sTmpRect.set(area);
+ view.getLocationOnScreen(sTmpInt2);
+ int left = sTmpInt2[0];
+
+ int intersectStart = Math.max(left, area.left);
+ int intersectEnd = Math.min(left + view.getWidth(), area.right);
+ int intersectAmount = Math.max(0, intersectEnd - intersectStart);
+
+ boolean coversFullStatusBar = area.top <= 0;
+ boolean majorityOfWidth = 2 * intersectAmount > view.getWidth();
+ return majorityOfWidth && coversFullStatusBar;
+ }
+
private void applyIconTint() {
for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
- v.setImageTintList(ColorStateList.valueOf(mIconTint));
+ v.setImageTintList(ColorStateList.valueOf(getTint(mTintArea, v, mIconTint)));
}
- mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
- mBatteryMeterView.setDarkIntensity(mDarkIntensity);
- mClock.setTextColor(mIconTint);
+ mSignalCluster.setIconTint(mIconTint, mDarkIntensity, mTintArea);
+ mBatteryMeterView.setDarkIntensity(
+ isInArea(mTintArea, mBatteryMeterView) ? mDarkIntensity : 0);
+ mClock.setTextColor(getTint(mTintArea, mClock, mIconTint));
}
public void appTransitionPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index f06552283219..ab817127564c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -39,10 +39,10 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Stack;
-import java.util.TreeSet;
/**
* A manager which handles heads up notifications which is a special mode where
@@ -90,7 +90,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private int mSnoozeLengthMs;
private ContentObserver mSettingsObserver;
private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
- private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
private HashSet<String> mSwipedOutKeys = new HashSet<>();
private int mUser;
private Clock mClock;
@@ -230,7 +229,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private void removeHeadsUpEntry(NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
- mSortedEntries.remove(remove);
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
@@ -345,12 +343,21 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
return mHeadsUpEntries.get(key).entry;
}
- public TreeSet<HeadsUpEntry> getSortedEntries() {
- return mSortedEntries;
+ public Collection<HeadsUpEntry> getAllEntries() {
+ return mHeadsUpEntries.values();
}
public HeadsUpEntry getTopEntry() {
- return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
+ if (mHeadsUpEntries.isEmpty()) {
+ return null;
+ }
+ HeadsUpEntry topEntry = null;
+ for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
+ if (topEntry == null || entry.compareTo(topEntry) == -1) {
+ topEntry = entry;
+ }
+ }
+ return topEntry;
}
/**
@@ -374,26 +381,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
return;
}
if (mHasPinnedNotification) {
- int minX = 0;
- int maxX = 0;
- int maxY = 0;
- for (HeadsUpEntry entry : mSortedEntries) {
- ExpandableNotificationRow row = entry.entry.row;
- if (row.isPinned()) {
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- row.getLocationOnScreen(mTmpTwoArray);
- minX = mTmpTwoArray[0];
- maxX = mTmpTwoArray[0] + row.getWidth();
- maxY = row.getIntrinsicHeight();
- break;
+ ExpandableNotificationRow topEntry = getTopEntry().entry.row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
}
}
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(minX, 0, maxX, maxY);
@@ -413,7 +412,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
- for (HeadsUpEntry entry: mSortedEntries) {
+ for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
}
int N = mSnoozedPackages.size();
@@ -633,7 +632,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
}
public void updateEntry(boolean updatePostTime) {
- mSortedEntries.remove(HeadsUpEntry.this);
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
@@ -648,7 +646,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
}
- mSortedEntries.add(HeadsUpEntry.this);
}
private boolean isSticky() {
@@ -658,6 +655,13 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
@Override
public int compareTo(HeadsUpEntry o) {
+ boolean isPinned = entry.row.isPinned();
+ boolean otherPinned = o.entry.row.isPinned();
+ if (isPinned && !otherPinned) {
+ return -1;
+ } else if (!isPinned && otherPinned) {
+ return 1;
+ }
boolean selfFullscreen = hasFullScreenIntent(entry);
boolean otherFullscreen = hasFullScreenIntent(o.entry);
if (selfFullscreen && !otherFullscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5cfcd8981890..49aec423db1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -43,16 +43,16 @@ public class NotificationChildrenContainer extends ViewGroup {
private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
- private final int mChildPadding;
- private final int mDividerHeight;
- private final int mMaxNotificationHeight;
private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
- private final int mNotificationHeaderHeight;
- private final int mNotificationAppearDistance;
- private final int mNotificatonTopPadding;
private final HybridNotificationViewManager mHybridViewManager;
- private final float mCollapsedBottompadding;
+ private int mChildPadding;
+ private int mDividerHeight;
+ private int mMaxNotificationHeight;
+ private int mNotificationHeaderHeight;
+ private int mNotificationAppearDistance;
+ private int mNotificatonTopPadding;
+ private float mCollapsedBottompadding;
private ViewInvertHelper mOverflowInvertHelper;
private boolean mChildrenExpanded;
private ExpandableNotificationRow mNotificationParent;
@@ -76,6 +76,11 @@ public class NotificationChildrenContainer extends ViewGroup {
public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ initDimens();
+ mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
+ }
+
+ private void initDimens() {
mChildPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_padding);
mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
@@ -89,7 +94,6 @@ public class NotificationChildrenContainer extends ViewGroup {
mNotificatonTopPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_container_top_padding);
mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
- mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
}
@Override
@@ -461,4 +465,16 @@ public class NotificationChildrenContainer extends ViewGroup {
mOverflowInvertHelper.setInverted(dark, fade, delay);
}
}
+
+ public void reInflateViews() {
+ initDimens();
+ for (int i = 0; i < mDividers.size(); i++) {
+ View prevDivider = mDividers.get(i);
+ int index = indexOfChild(prevDivider);
+ removeView(prevDivider);
+ View divider = inflateDivider();
+ addView(divider, index);
+ mDividers.set(i, divider);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 49e9c3db9f30..bf4245b5e7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -213,7 +213,6 @@ public class NotificationStackScrollLayout extends ViewGroup
* animating.
*/
private boolean mOnlyScrollingInThisMotion;
- private ViewGroup mScrollView;
private boolean mInterceptDelegateEnabled;
private boolean mDelegateToScrollView;
private boolean mDisallowScrollingInThisMotion;
@@ -281,6 +280,7 @@ public class NotificationStackScrollLayout extends ViewGroup
setDimAmount((Float) animation.getAnimatedValue());
}
};
+ private ViewGroup mQsContainer;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -630,12 +630,8 @@ public class NotificationStackScrollLayout extends ViewGroup
mLongPressListener = listener;
}
- public void setScrollView(ViewGroup scrollView) {
- mScrollView = scrollView;
- }
-
- public void setInterceptDelegateEnabled(boolean interceptDelegateEnabled) {
- mInterceptDelegateEnabled = interceptDelegateEnabled;
+ public void setQsContainer(ViewGroup qsContainer) {
+ mQsContainer = qsContainer;
}
public void onChildDismissed(View v) {
@@ -883,13 +879,6 @@ public class NotificationStackScrollLayout extends ViewGroup
public boolean onTouchEvent(MotionEvent ev) {
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked()== MotionEvent.ACTION_UP;
- if (mDelegateToScrollView) {
- if (isCancelOrUp) {
- mDelegateToScrollView = false;
- }
- transformTouchEvent(ev, this, mScrollView);
- return mScrollView.onTouchEvent(ev);
- }
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
@@ -929,6 +918,9 @@ public class NotificationStackScrollLayout extends ViewGroup
if (!isScrollingEnabled()) {
return false;
}
+ if (ev.getY() < mQsContainer.getBottom()) {
+ return false;
+ }
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
@@ -1776,15 +1768,13 @@ public class NotificationStackScrollLayout extends ViewGroup
* account.
*
* @param qsHeight the top padding imposed by the quick settings panel
- * @param scrollY how much the notifications are scrolled inside the QS/notifications scroll
- * container
* @param animate whether to animate the change
* @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
* {@code qsHeight} is the final top padding
*/
- public void updateTopPadding(float qsHeight, int scrollY, boolean animate,
+ public void updateTopPadding(float qsHeight, boolean animate,
boolean ignoreIntrinsicPadding) {
- float start = qsHeight - scrollY;
+ float start = qsHeight;
float stackHeight = getHeight() - start;
int minStackHeight = getMinStackHeight();
if (stackHeight <= minStackHeight) {
@@ -1867,15 +1857,6 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mInterceptDelegateEnabled) {
- transformTouchEvent(ev, this, mScrollView);
- if (mScrollView.onInterceptTouchEvent(ev)) {
- mDelegateToScrollView = true;
- removeLongPressCallback();
- return true;
- }
- transformTouchEvent(ev, mScrollView, this);
- }
initDownStates(ev);
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
@@ -2157,7 +2138,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateAnimationState(View child) {
- updateAnimationState((mAnimationsEnabled || isPinnedHeadsUp(child)) && mIsExpanded, child);
+ updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@@ -2893,13 +2874,23 @@ public class NotificationStackScrollLayout extends ViewGroup
}
public void setDismissView(DismissView dismissView) {
+ int index = -1;
+ if (mDismissView != null) {
+ index = indexOfChild(mDismissView);
+ removeView(mDismissView);
+ }
mDismissView = dismissView;
- addView(mDismissView);
+ addView(mDismissView, index);
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+ int index = -1;
+ if (mEmptyShadeView != null) {
+ index = indexOfChild(mEmptyShadeView);
+ removeView(mEmptyShadeView);
+ }
mEmptyShadeView = emptyShadeView;
- addView(mEmptyShadeView);
+ addView(mEmptyShadeView, index);
}
public void updateEmptyShadeView(boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 784f610e40a1..110258c360dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.os.IBinder;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -68,7 +69,8 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- public void setSystemUiVisibility(int vis, int mask) {
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index b79a7ba69b4e..ad70853f13ba 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -110,9 +110,25 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
// The minimal score for accepting a predicted gesture.
private static final float MIN_PREDICTION_SCORE = 2.0f;
+ // Distance a finger must travel before we decide if it is a gesture or not.
private static final int GESTURE_CONFIRM_MM = 10;
- private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 1000;
- private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 500;
+
+ // Time threshold used to determine if an interaction is a gesture or not.
+ // If the first movement of 1cm takes longer than this value, we assume it's
+ // a slow movement, and therefore not a gesture.
+ //
+ // This value was determined by measuring the time for the first 1cm
+ // movement when gesturing, and touch exploring. Based on user testing,
+ // all gestures started with the initial movement taking less than 100ms.
+ // When touch exploring, the first movement almost always takes longer than
+ // 200ms. From this data, 150ms seems the best value to decide what
+ // kind of interaction it is.
+ private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+
+ // Time threshold used to determine if a gesture should be cancelled. If
+ // the finger pauses for longer than this delay, the ongoing gesture is
+ // cancelled.
+ private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 500;
AccessibilityGestureDetector(Context context, Listener listener) {
mListener = listener;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 830b0f27f31b..f8bf59d29f48 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -87,6 +87,7 @@ import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StringBuilderPrinter;
@@ -142,6 +143,7 @@ import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
@@ -786,8 +788,9 @@ public class BackupManagerService {
case MSG_OP_COMPLETE:
{
try {
- BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- task.operationComplete(msg.arg1);
+ Pair<BackupRestoreTask, Long> taskWithResult =
+ (Pair<BackupRestoreTask, Long>) msg.obj;
+ taskWithResult.first.operationComplete(taskWithResult.second);
} catch (ClassCastException e) {
Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
}
@@ -1044,7 +1047,7 @@ public class BackupManagerService {
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
- mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
+ mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
@@ -2460,7 +2463,7 @@ public class BackupManagerService {
void execute();
// An operation that wanted a callback has completed
- void operationComplete(int result);
+ void operationComplete(long result);
// An operation that wanted a callback has timed out
void handleTimeout();
@@ -3095,7 +3098,7 @@ public class BackupManagerService {
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
// The agent reported back to us!
if (mBackupData == null) {
@@ -3494,7 +3497,7 @@ public class BackupManagerService {
*/
int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
- long expectedSize();
+ long getExpectedSizeOrErrorCode();
};
class FullBackupEngine {
@@ -4532,7 +4535,7 @@ public class BackupManagerService {
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
- final AtomicInteger mResult = new AtomicInteger();
+ final AtomicLong mResult = new AtomicLong();
final CountDownLatch mLatch = new CountDownLatch(1);
final IBackupTransport mTransport;
@@ -4554,7 +4557,11 @@ public class BackupManagerService {
// now wait to get our result back
mLatch.await();
- int totalSize = mResult.get();
+ long totalSize = mResult.get();
+ // If preflight timeouted, mResult will contain error code as int.
+ if (totalSize < 0) {
+ return (int) totalSize;
+ }
if (MORE_DEBUG) {
Slog.v(TAG, "Got preflight response; size=" + totalSize);
}
@@ -4581,7 +4588,7 @@ public class BackupManagerService {
}
@Override
- public void operationComplete(int result) {
+ public void operationComplete(long result) {
// got the callback, and our preflightFullBackup() method is waiting for the result
if (MORE_DEBUG) {
Slog.i(TAG, "Preflight op complete, result=" + result);
@@ -4600,7 +4607,7 @@ public class BackupManagerService {
}
@Override
- public long expectedSize() {
+ public long getExpectedSizeOrErrorCode() {
try {
mLatch.await();
return mResult.get();
@@ -4649,7 +4656,7 @@ public class BackupManagerService {
}
long expectedSize() {
- return mPreflight.expectedSize();
+ return mPreflight.getExpectedSizeOrErrorCode();
}
}
}
@@ -8558,7 +8565,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
@Override
- public void operationComplete(int unusedResult) {
+ public void operationComplete(long unusedResult) {
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -9643,9 +9650,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
- // NB: this cannot distinguish between results > 2 gig
- msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
mBackupHandler.sendMessage(msg);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index bbf881be102f..e74526338a8f 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -54,7 +54,7 @@ public class Trampoline extends IBackupManager.Stub {
public Trampoline(Context context) {
mContext = context;
- File dir = new File(Environment.getSecureDataDirectory(), "backup");
+ File dir = new File(Environment.getDataDirectory(), "backup");
dir.mkdirs();
mSuppressFile = new File(dir, BACKUP_SUPPRESS_FILENAME);
mGlobalDisable = SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index a4455e91f294..07d472dc5375 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -882,6 +884,11 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
+ if (isApplicationSuspended(packageName, uid)) {
+ Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
final int mode = checkRestrictionLocked(code, usage, uid, packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
@@ -891,6 +898,23 @@ public class AppOpsService extends IAppOpsService.Stub {
return checkOperation(code, uid, packageName);
}
+ private boolean isApplicationSuspended(String pkg, int uid) {
+ int userId = UserHandle.getUserId(uid);
+
+ ApplicationInfo ai;
+ try {
+ ai = AppGlobals.getPackageManager().getApplicationInfo(pkg, 0, userId);
+ if (ai == null) {
+ Log.w(TAG, "No application info for package " + pkg + " and user " + userId);
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Could not talk to package manager service");
+ }
+
+ return ((ai.flags & FLAG_SUSPENDED) != 0);
+ }
+
private int checkRestrictionLocked(int code, int usage, int uid, String packageName) {
final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
if (usageRestrictions != null) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4dbb49005c28..c318140ae7e8 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +35,7 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_CONTACTS;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -124,7 +126,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+ mLockSettingsService.maybeShowEncryptionNotifications();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
// TODO
}
@@ -176,22 +178,48 @@ public class LockSettingsService extends ILockSettings.Stub {
* If the account is credential-encrypted, show notification requesting the user to unlock
* the device.
*/
- private void maybeShowEncryptionNotification(UserHandle userHandle) {
- if (UserHandle.ALL.equals(userHandle)) {
- final List<UserInfo> users = mUserManager.getUsers();
- for (int i = 0; i < users.size(); i++) {
- UserHandle user = users.get(i).getUserHandle();
- if (!mUserManager.isUserUnlocked(user)) {
- showEncryptionNotification(user);
+ private void maybeShowEncryptionNotifications() {
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ UserInfo user = users.get(i);
+ UserHandle userHandle = user.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ if (!user.isManagedProfile()) {
+ showEncryptionNotification(userHandle);
+ } else {
+ UserInfo parent = mUserManager.getProfileParent(user.id);
+ if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+ // Only show notifications for managed profiles once their parent
+ // user is unlocked.
+ showEncryptionNotificationForProfile(userHandle);
+ }
}
}
- } else if (!mUserManager.isUserUnlocked(userHandle)){
- showEncryptionNotification(userHandle);
}
}
+ private void showEncryptionNotificationForProfile(UserHandle user) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.user_encrypted_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.profile_encrypted_message);
+ CharSequence detail = r.getText(
+ com.android.internal.R.string.profile_encrypted_detail);
+
+ final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
+ final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+ if (unlockIntent == null) {
+ return;
+ }
+ unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
private void showEncryptionNotification(UserHandle user) {
- if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Resources r = mContext.getResources();
CharSequence title = r.getText(
com.android.internal.R.string.user_encrypted_title);
@@ -203,6 +231,12 @@ public class LockSettingsService extends ILockSettings.Stub {
PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
PendingIntent.FLAG_UPDATE_CURRENT);
+ showEncryptionNotification(user, title, message, detail, intent);
+ }
+
+ private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
+ CharSequence detail, PendingIntent intent) {
+ if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
Notification notification = new Notification.Builder(mContext)
.setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
.setWhen(0)
@@ -230,8 +264,21 @@ public class LockSettingsService extends ILockSettings.Stub {
hideEncryptionNotification(new UserHandle(userId));
}
- public void onUnlockUser(int userHandle) {
- hideEncryptionNotification(new UserHandle(userHandle));
+ public void onUnlockUser(int userId) {
+ hideEncryptionNotification(new UserHandle(userId));
+
+ // Now we have unlocked the parent user we should show notifications
+ // about any profiles that exist.
+ List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ for (int i = 0; i < profiles.size(); i++) {
+ UserInfo profile = profiles.get(i);
+ if (profile.isManagedProfile()) {
+ UserHandle userHandle = profile.getUserHandle();
+ if (!mUserManager.isUserUnlocked(userHandle)) {
+ showEncryptionNotificationForProfile(userHandle);
+ }
+ }
+ }
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cbd477a9500b..d09edc88ae8a 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1473,7 +1473,7 @@ class MountService extends IMountService.Stub
}
mSettingsFile = new AtomicFile(
- new File(Environment.getSystemSecureDirectory(), "storage.xml"));
+ new File(Environment.getDataSystemDirectory(), "storage.xml"));
synchronized (mLock) {
readSettingsLocked();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 95f57346dcfc..799d0bda895f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2086,7 +2086,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
final int oldUidFirewallRule = uidFirewallRules.get(uid, FIREWALL_RULE_DEFAULT);
if (DBG) {
Slog.d(TAG, "oldRule = " + oldUidFirewallRule
- + ", newRule=" + rule + " for uid=" + uid);
+ + ", newRule=" + rule + " for uid=" + uid + " on chain " + chain);
}
if (oldUidFirewallRule == rule) {
if (DBG) Slog.d(TAG, "!!!!! Skipping change");
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b984e1938758..879bb6f72895 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -234,12 +234,24 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
// safety as scores should never be compared across apps; in practice, Settings should
// only be allowing valid apps to be set as scorers, so failure here should be rare.
clearInternal();
+ // Get the scorer that is about to be replaced, if any, so we can notify it directly.
+ NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
- if (result) {
+ if (result) { // new scorer successfully set
registerPackageReceiverIfNeeded();
Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
- intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ if (prevScorer != null) { // Directly notify the old scorer.
+ intent.setPackage(prevScorer.mPackageName);
+ // TODO: Need to update when we support per-user scorers. http://b/23422763
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+
+ if (packageName != null) { // Then notify the new scorer
+ intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+ intent.setPackage(packageName);
+ // TODO: Need to update when we support per-user scorers. http://b/23422763
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
}
return result;
} finally {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2683be66b347..ca1e3719c037 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3809,7 +3809,7 @@ public class AccountManagerService
}
private static String getDatabaseName(int userId) {
- File systemDir = Environment.getSystemSecureDirectory();
+ File systemDir = Environment.getDataSystemDirectory();
File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index bef6f0aabf5d..1b9d968f18db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -62,7 +62,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
- static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
+ static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
static final boolean DEBUG_POWER = DEBUG_ALL || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
@@ -85,7 +85,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
- static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true;
+ static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 104217a63104..50619015bff9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2851,31 +2851,39 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void setFocusedStack(int stackId) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
- synchronized (ActivityManagerService.this) {
- ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack != null) {
- ActivityRecord r = stack.topRunningActivityLocked();
- if (r != null) {
- setFocusedActivityLocked(r, "setFocusedStack");
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (setFocusedActivityLocked(r, "setFocusedStack")) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void setFocusedTask(int taskId) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
- long callingId = Binder.clearCallingIdentity();
+ final long callingId = Binder.clearCallingIdentity();
try {
- synchronized (ActivityManagerService.this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task != null) {
- final ActivityRecord r = task.topRunningActivityLocked();
- if (setFocusedActivityLocked(r, "setFocusedTask")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
+ synchronized (this) {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ return;
+ }
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (setFocusedActivityLocked(r, "setFocusedTask")) {
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
} finally {
@@ -20794,7 +20802,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
} catch (RemoteException e) {
}
- if (pkgUid == -1) {
+ if (userId != UserHandle.USER_ALL && pkgUid == -1) {
throw new IllegalArgumentException(
"Cannot kill dependents of non-existing package " + packageName);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8560a9eeb494..1103ea4874d2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -768,7 +768,6 @@ final class ActivityStack {
r.state = ActivityState.RESUMED;
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to RESUMED: " + r + " (starting new instance)");
- r.stopped = false;
mResumedActivity = r;
r.task.touchActiveTime();
mRecentTasks.addLocked(r.task);
@@ -1127,7 +1126,8 @@ final class ActivityStack {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
} else if (prev.app != null) {
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending stop: " + prev);
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ + " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (mStackSupervisor.mWaitingVisibleActivities.remove(prev)) {
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Complete pause, no longer waiting: " + prev);
@@ -1142,7 +1142,8 @@ final class ActivityStack {
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.state = ActivityState.STOPPING;
- } else if (!hasVisibleBehindActivity() || mService.isSleepingOrShuttingDown()) {
+ } else if ((!prev.visible && !hasVisibleBehindActivity())
+ || mService.isSleepingOrShuttingDown()) {
// If we were visible then resumeTopActivities will release resources before
// stopping.
addToStopping(prev);
@@ -1226,9 +1227,11 @@ final class ActivityStack {
* this function updates the rest of our state to match that fact.
*/
private void completeResumeLocked(ActivityRecord next) {
+ next.visible = true;
next.idle = false;
next.results = null;
next.newIntents = null;
+ next.stopped = false;
if (next.isHomeActivity()) {
ProcessRecord app = next.task.mActivities.get(0).app;
@@ -1727,6 +1730,8 @@ final class ActivityStack {
// This activity is not currently visible, but is running. Tell it to become visible.
if (r.state == ActivityState.RESUMED || r == starting) {
+ Slog.d(TAG_VISIBILITY, "Not making visible, r=" + r + " state=" + r.state
+ + " starting=" + starting);
return;
}
@@ -2289,7 +2294,6 @@ final class ActivityStack {
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
- next.visible = true;
completeResumeLocked(next);
} catch (Exception e) {
// If any exception gets thrown, toss away this
@@ -2300,8 +2304,6 @@ final class ActivityStack {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
- next.stopped = false;
-
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
@@ -4281,6 +4283,12 @@ final class ActivityStack {
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
+
+ if (DEBUG_STATES && !r.visible) {
+ Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + r
+ + " called by " + Debug.getCallers(4));
+ }
+
relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -4431,9 +4439,21 @@ final class ActivityStack {
}
if (andResume) {
- r.results = null;
- r.newIntents = null;
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "Resumed after relaunch " + r);
+ }
r.state = ActivityState.RESUMED;
+ // Relaunch-resume could happen either when the app is already in the front,
+ // or while it's being brought to front. In the latter case, it's marked RESUMED
+ // but not yet visible (or stopped). We need to complete the resume here as the
+ // code in resumeTopActivityInnerLocked to complete the resume might be skipped.
+ if (!r.visible || r.stopped) {
+ mWindowManager.setAppVisibility(r.appToken, true);
+ completeResumeLocked(r);
+ } else {
+ r.results = null;
+ r.newIntents = null;
+ }
} else {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
r.state = ActivityState.PAUSED;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 26108a362273..93a36eb1d3d8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1046,10 +1046,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+ return resolveIntent(intent, resolvedType, userId, 0);
+ }
+
+ ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
try {
return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ PackageManager.MATCH_DEFAULT_ONLY | flags
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
} catch (RemoteException e) {
}
return null;
@@ -1938,9 +1942,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
preserveWindows);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- ensureActivitiesVisibleLocked(r, 0, preserveWindows);
if (!updated) {
resumeFocusedStackTopActivityLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1ed749fdd9da..9b2bca009da6 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -1,3 +1,19 @@
+/*
+ * 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.am;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 28be456628ae..cca6fc5a85d5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,3 +1,19 @@
+/*
+ * 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.am;
import static android.app.Activity.RESULT_CANCELED;
@@ -74,7 +90,9 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -84,6 +102,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
@@ -582,6 +601,22 @@ class ActivityStarter {
intent = new Intent(intent);
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+ if (rInfo == null) {
+ UserInfo userInfo = mSupervisor.getUserInfo(userId);
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ // Special case for managed profiles, if attempting to launch non-cryto aware
+ // app in a locked managed profile from an unlocked parent allow it to resolve
+ // as user will be sent via confirm credentials to unlock the profile.
+ UserManager userManager = UserManager.get(mService.mContext);
+ UserInfo parent = userManager.getProfileParent(userId);
+ if (parent != null
+ && userManager.isUserUnlocked(parent.getUserHandle())
+ && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+ rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ }
+ }
+ }
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 98a7ead339df..16fd909b329a 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -70,6 +70,7 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
@@ -122,9 +123,6 @@ final class TaskRecord {
private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
private static final String ATTR_CALLING_UID = "calling_uid";
private static final String ATTR_CALLING_PACKAGE = "calling_package";
- // TODO(b/26847884): Currently needed while migrating to resize_mode.
- // Can be removed at some later point.
- private static final String ATTR_RESIZEABLE = "resizeable";
private static final String ATTR_RESIZE_MODE = "resize_mode";
private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -1011,6 +1009,7 @@ final class TaskRecord {
String label = null;
String iconFilename = null;
int colorPrimary = 0;
+ int colorBackground = 0;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.taskDescription != null) {
@@ -1023,9 +1022,13 @@ final class TaskRecord {
if (colorPrimary == 0) {
colorPrimary = r.taskDescription.getPrimaryColor();
}
+ if (colorBackground == 0) {
+ colorBackground = r.taskDescription.getBackgroundColor();
+ }
}
}
- lastTaskDescription = new TaskDescription(label, colorPrimary, iconFilename);
+ lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
+ colorBackground);
// Update the task affiliation color if we are the parent of the group
if (taskId == mAffiliatedTaskId) {
mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
@@ -1169,7 +1172,7 @@ final class TaskRecord {
int nextTaskId = INVALID_TASK_ID;
int callingUid = -1;
String callingPackage = "";
- int resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean privileged = false;
Rect bounds = null;
@@ -1231,11 +1234,10 @@ final class TaskRecord {
callingUid = Integer.valueOf(attrValue);
} else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
callingPackage = attrValue;
- } else if (ATTR_RESIZEABLE.equals(attrName)) {
- resizeMode = Boolean.valueOf(attrValue)
- ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_CROP_WINDOWS;
} else if (ATTR_RESIZE_MODE.equals(attrName)) {
resizeMode = Integer.valueOf(attrValue);
+ resizeMode = (resizeMode == RESIZE_MODE_CROP_WINDOWS)
+ ? RESIZE_MODE_FORCE_RESIZEABLE : resizeMode;
} else if (ATTR_PRIVILEGED.equals(attrName)) {
privileged = Boolean.valueOf(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a355fa4750eb..90ebe1878a6a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -165,15 +165,15 @@ final class UserController {
void register(ContentResolver resolver) {
resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(UserHandle.USER_ALL);
}
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, int userId) {
if (mUserSetupComplete.equals(uri)) {
synchronized (mService) {
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
}
}
}
@@ -297,6 +297,22 @@ final class UserController {
null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
userId);
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
+
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -655,7 +671,7 @@ final class UserController {
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
- updateCurrentUserSetupCompleteLocked();
+ updateUserSetupCompleteLocked(userId);
if (foreground) {
mCurrentUserId = userId;
@@ -870,11 +886,16 @@ final class UserController {
mUserSwitchObservers.finishBroadcast();
}
- void updateCurrentUserSetupCompleteLocked() {
+ void updateUserSetupCompleteLocked(int userId) {
final ContentResolver cr = mService.mContext.getContentResolver();
- final boolean setupComplete =
- Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
- mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+ for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
+ int startedUser = mStartedUsers.keyAt(i);
+ if (startedUser == userId || userId == UserHandle.USER_ALL) {
+ final boolean setupComplete =
+ Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, startedUser) != 0;
+ mSetupCompletedUsers.put(startedUser, setupComplete);
+ }
+ }
}
boolean isUserSetupCompleteLocked(int userId) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8dec5870427c..0d974344a432 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -62,6 +62,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -70,41 +71,42 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
-import android.os.Messenger;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Log;
-import android.util.Slog;
import android.util.Pair;
-
+import android.util.Slog;
import android.util.SparseArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
+import com.android.server.backup.AccountSyncSettingsBackupHelper;
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.EndPoint;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Random;
-import java.util.List;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Map;
-import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
/**
* Implementation details:
@@ -451,10 +453,48 @@ public class SyncManager {
}
}
}
+ ensureDefaultPeriodicSyncsH();
}
});
}
+ private void ensureDefaultPeriodicSyncsH() {
+
+ long defaultPeriod = SyncStorageEngine.DEFAULT_POLL_FREQUENCY_SECONDS;
+ long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(defaultPeriod);
+
+ List<AuthorityInfo> authorities = mSyncStorageEngine.getAllAuthorities();
+ List<SyncOperation> syncs = getAllPendingSyncsFromCache();
+ for (AuthorityInfo authority: authorities) {
+ boolean foundPeriodicSync = false;
+ for (SyncOperation op: syncs) {
+ if (op.isPeriodic && authority.target.matchesSpec(op.target)) {
+ foundPeriodicSync = true;
+ break;
+ }
+ }
+ if (!foundPeriodicSync) {
+ EndPoint target = authority.target;
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(
+ target.provider, target.account.type),
+ target.userId);
+ if (syncAdapterInfo == null) {
+ continue;
+ }
+ scheduleSyncOperationH(
+ new SyncOperation(target, syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(),
+ SyncOperation.REASON_PERIODIC, SyncStorageEngine.SOURCE_PERIODIC,
+ new Bundle(), syncAdapterInfo.type.allowParallelSyncs(),
+ true /* periodic */, SyncOperation.NO_JOB_ID, defaultPeriod * 1000L,
+ defaultFlex * 1000L)
+ );
+ }
+ }
+ }
+
private synchronized void verifyJobScheduler() {
if (mJobScheduler != null) {
return;
@@ -510,12 +550,12 @@ public class SyncManager {
mSyncStorageEngine.setPeriodicSyncAddedListener(
new SyncStorageEngine.PeriodicSyncAddedListener() {
- @Override
- public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
- long flex) {
- updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
- }
- });
+ @Override
+ public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
+ long flex) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ });
mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
@Override
@@ -750,8 +790,8 @@ public class SyncManager {
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
- long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
+ String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
+ long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -941,7 +981,7 @@ public class SyncManager {
* flexMillis will be updated.
*/
public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
pollFrequency, flex, extras);
mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
@@ -995,7 +1035,7 @@ public class SyncManager {
}
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
@@ -1053,7 +1093,7 @@ public class SyncManager {
public final SyncResult syncResult;
SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
}
@@ -1066,7 +1106,7 @@ public class SyncManager {
public final Bundle extras;
UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
this.target = target;
this.pollFrequency = pollFrequency;
this.flex = flex;
@@ -1297,7 +1337,7 @@ public class SyncManager {
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
- new ComponentName(mContext, SyncJobService.class))
+ new ComponentName(mContext, SyncJobService.class))
.setExtras(syncOperation.toJobInfoExtras())
.setRequiredNetworkType(networkType)
.setPersisted(true)
@@ -1502,7 +1542,7 @@ public class SyncManager {
* for this sync. This is used to attribute the wakelock hold to that application.
*/
public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
- int syncAdapterUid) {
+ int syncAdapterUid) {
super();
mSyncAdapterUid = syncAdapterUid;
mSyncOperation = syncOperation;
@@ -1731,7 +1771,7 @@ public class SyncManager {
new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
@Override
public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
return lhs.type.authority.compareTo(rhs.type.authority);
}
});
@@ -2546,6 +2586,7 @@ public class SyncManager {
Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
}
deferActiveSyncH(asc);
+ break;
}
}
}
@@ -2571,6 +2612,7 @@ public class SyncManager {
}
private void updateRunningAccountsH(EndPoint syncTargets) {
+ AccountAndUser[] oldAccounts = mRunningAccounts;
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Accounts list: ");
@@ -2594,6 +2636,17 @@ public class SyncManager {
}
}
+ // On account add, check if there are any settings to be restored.
+ for (AccountAndUser aau : mRunningAccounts) {
+ if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
+ }
+ AccountSyncSettingsBackupHelper.accountAdded(mContext);
+ break;
+ }
+ }
+
List<SyncOperation> ops = getAllPendingSyncsFromCache();
for (SyncOperation op: ops) {
if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
@@ -2617,7 +2670,7 @@ public class SyncManager {
* @param flexMillis new flex time in milliseconds.
*/
private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
- long flexMillis) {
+ long flexMillis) {
if (!(pollFrequencyMillis == syncOperation.periodMillis
&& flexMillis == syncOperation.flexMillis)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2632,7 +2685,7 @@ public class SyncManager {
}
private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
- Bundle extras) {
+ Bundle extras) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled.
final long pollFrequencyMillis = pollFrequency * 1000L;
@@ -2820,7 +2873,7 @@ public class SyncManager {
}
private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
- IBinder syncAdapter) {
+ IBinder syncAdapter) {
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
@@ -2888,7 +2941,7 @@ public class SyncManager {
}
private void runSyncFinishedOrCanceledH(SyncResult syncResult,
- ActiveSyncContext activeSyncContext) {
+ ActiveSyncContext activeSyncContext) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3022,7 +3075,7 @@ public class SyncManager {
}
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes, int userId) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -3098,7 +3151,7 @@ public class SyncManager {
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
- int upstreamActivity, int downstreamActivity, long elapsedTime) {
+ int upstreamActivity, int downstreamActivity, long elapsedTime) {
EventLog.writeEvent(2720,
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
@@ -3262,4 +3315,4 @@ public class SyncManager {
return mContext;
}
}
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f8e3e48f8026..cefaa8ddd831 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -80,7 +80,7 @@ public class SyncStorageEngine extends Handler {
private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
/** Default time for a periodic sync. */
- private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
+ static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
/** Percentage of period that is flex by default, if no flexMillis is set. */
private static final double DEFAULT_FLEX_PERCENT_SYNC = 0.04;
@@ -438,9 +438,7 @@ public class SyncStorageEngine extends Handler {
if (sSyncStorageEngine != null) {
return;
}
- // This call will return the correct directory whether Encrypted File Systems is
- // enabled or not.
- File dataDir = Environment.getSecureDataDirectory();
+ File dataDir = Environment.getDataDirectory();
sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
}
@@ -859,6 +857,16 @@ public class SyncStorageEngine extends Handler {
}
}
+ List<AuthorityInfo> getAllAuthorities() {
+ List<AuthorityInfo> authorities = new ArrayList<AuthorityInfo>();
+ synchronized (mAuthorities) {
+ for (int i = 0; i < mAuthorities.size(); i++) {
+ authorities.add(mAuthorities.valueAt(i));
+ }
+ }
+ return authorities;
+ }
+
/**
* Returns true if there is currently a sync operation being actively processed for the given
* target.
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 62114cd0c5c8..7e19c66b1ad6 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -52,7 +52,7 @@ public class IntentFirewall {
static final String TAG = "IntentFirewall";
// e.g. /data/system/ifw or /data/secure/system/ifw
- private static final File RULES_DIR = new File(Environment.getSystemSecureDirectory(), "ifw");
+ private static final File RULES_DIR = new File(Environment.getDataSystemDirectory(), "ifw");
private static final int LOG_PACKAGES_MAX_LENGTH = 150;
private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e5e86ac44a26..402837281609 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -330,7 +330,7 @@ public class JobSchedulerService extends com.android.server.SystemService
private void cancelJobImpl(JobStatus cancelled) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
- stopTrackingJob(cancelled);
+ stopTrackingJob(cancelled, true /* writeBack */);
synchronized (mJobs) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
@@ -509,12 +509,12 @@ public class JobSchedulerService extends com.android.server.SystemService
* Called when we want to remove a JobStatus object that we've finished executing. Returns the
* object removed.
*/
- private boolean stopTrackingJob(JobStatus jobStatus) {
+ private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
boolean removed;
boolean rocking;
synchronized (mJobs) {
// Remove from store as well as controllers.
- removed = mJobs.remove(jobStatus);
+ removed = mJobs.remove(jobStatus, writeBack);
rocking = mReadyToRock;
}
if (removed && rocking) {
@@ -645,7 +645,9 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
- if (!stopTrackingJob(jobStatus)) {
+ // Do not write back immediately if this is a periodic job. The job may get lost if system
+ // shuts down before it is added back.
+ if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index f796164ef8ce..f6a6778aa496 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -159,9 +159,10 @@ public class JobStore {
/**
* Remove the provided job. Will also delete the job if it was persisted.
+ * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
* @return Whether or not the job existed to be removed.
*/
- public boolean remove(JobStatus jobStatus) {
+ public boolean remove(JobStatus jobStatus, boolean writeBack) {
boolean removed = mJobSet.remove(jobStatus);
if (!removed) {
if (DEBUG) {
@@ -169,7 +170,7 @@ public class JobStore {
}
return false;
}
- if (jobStatus.isPersisted()) {
+ if (writeBack && jobStatus.isPersisted()) {
maybeWriteStatusToDiskAsync();
}
return removed;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ffc52b31a7d5..c1eb844f09fa 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1557,15 +1557,13 @@ public class GnssLocationProvider implements LocationProviderInterface {
* called from native code to update SV info
*/
private void reportSvStatus() {
- int svCount = native_read_sv_status(mPrnWithFlags, mSnrs, mSvElevations, mSvAzimuths,
- mConstellationTypes);
+ int svCount = native_read_sv_status(mSvidWithFlags, mSnrs, mSvElevations, mSvAzimuths);
mListenerHelper.onSvStatusChanged(
svCount,
- mPrnWithFlags,
+ mSvidWithFlags,
mSnrs,
mSvElevations,
- mSvAzimuths,
- mConstellationTypes);
+ mSvAzimuths);
if (VERBOSE) {
Log.v(TAG, "SV count: " + svCount);
@@ -1573,19 +1571,19 @@ public class GnssLocationProvider implements LocationProviderInterface {
// Calculate number of sets used in fix.
int usedInFixCount = 0;
for (int i = 0; i < svCount; i++) {
- if ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
++usedInFixCount;
}
if (VERBOSE) {
- Log.v(TAG, "prn: " + (mPrnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH) +
+ Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
" snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
? " " : " E") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
? " " : " A") +
- ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
? "" : "U"));
}
}
@@ -2398,14 +2396,13 @@ public class GnssLocationProvider implements LocationProviderInterface {
}
// for GPS SV statistics
- private static final int MAX_SVS = 512;
+ private static final int MAX_SVS = 64;
// preallocated arrays, to avoid memory allocation in reportStatus()
- private int mPrnWithFlags[] = new int[MAX_SVS];
+ private int mSvidWithFlags[] = new int[MAX_SVS];
private float mSnrs[] = new float[MAX_SVS];
private float mSvElevations[] = new float[MAX_SVS];
private float mSvAzimuths[] = new float[MAX_SVS];
- private int mConstellationTypes[] = new int[MAX_SVS];
private int mSvCount;
// preallocated to avoid memory allocation in reportNmea()
private byte[] mNmeaBuffer = new byte[120];
@@ -2426,7 +2423,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
// returns number of SVs
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] prnWithFlags, float[] snrs, float[] elevations,
- float[] azimuths, int[] constellationTypes);
+ float[] azimuths);
private native int native_read_nmea(byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 9840c61ec516..0b3111cd726c 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -77,8 +77,7 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
final int[] prnWithFlags,
final float[] snrs,
final float[] elevations,
- final float[] azimuths,
- final int[] constellationTypes) {
+ final float[] azimuths) {
Operation operation = new Operation() {
@Override
public void execute(IGnssStatusListener listener) throws RemoteException {
@@ -87,8 +86,7 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
prnWithFlags,
snrs,
elevations,
- azimuths,
- constellationTypes);
+ azimuths);
}
};
foreach(operation);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b2e6adfd028a..426ce41298e7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -339,6 +339,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final AppOpsManager mAppOps;
private final MyPackageMonitor mPackageMonitor;
+ private final IPackageManager mIPm;
+
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
@@ -369,6 +371,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
Context.DEVICE_IDLE_CONTROLLER));
mTime = checkNotNull(time, "missing TrustedTime");
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mIPm = AppGlobals.getPackageManager();
HandlerThread thread = new HandlerThread(TAG);
thread.start();
@@ -1864,31 +1867,58 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void addRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
+ return;
+ }
+ Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
mRestrictBackgroundWhitelistUids.append(uid, true);
- updateRulesForGlobalChangeLocked(true);
+ changed = mRestrictBackground && !oldStatus;
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, false);
+ }
writePolicyLocked();
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
}
@Override
public void removeRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ if (!isUidValidForRules(uid)) return;
+ final boolean changed;
synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ }
+ if (changed) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
}
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
}
- private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ if (!oldStatus) {
+ if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
+ return false;
+ }
+ Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
+ final boolean changed = mRestrictBackground && oldStatus;
mRestrictBackgroundWhitelistUids.delete(uid);
if (updateNow) {
- updateRulesForGlobalChangeLocked(true);
+ if (changed && hasInternetPermissions(uid)) {
+ setUidNetworkRules(uid, true);
+ }
writePolicyLocked();
}
+ return changed;
}
@Override
@@ -2298,13 +2328,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
uidRules.clear();
// Fully update the app idle firewall chain.
+ final IPackageManager ipm = AppGlobals.getPackageManager();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
for (int uid : idleUids) {
if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
- uidRules.put(uid, FIREWALL_RULE_DENY);
+ // quick check: if this uid doesn't have INTERNET permission, it
+ // doesn't have network access anyway, so it is a waste to mess
+ // with it here.
+ if (hasInternetPermissions(uid)) {
+ uidRules.put(uid, FIREWALL_RULE_DENY);
+ }
}
}
}
@@ -2408,22 +2444,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
- * Applies network rules to bandwidth and firewall controllers based on uid policy.
- * @param uid The uid for which to apply the latest policy
+ * Checks if an uid has INTERNET permissions.
+ * <p>
+ * Useful for the cases where the lack of network access can simplify the rules.
*/
- void updateRulesForUidLocked(int uid) {
- if (!isUidValidForRules(uid)) return;
-
- // quick check: if this uid doesn't have INTERNET permission, it doesn't have
- // network access anyway, so it is a waste to mess with it here.
- final IPackageManager ipm = AppGlobals.getPackageManager();
+ private boolean hasInternetPermissions(int uid) {
try {
- if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
+ if (mIPm.checkUidPermission(Manifest.permission.INTERNET, uid)
!= PackageManager.PERMISSION_GRANTED) {
- return;
+ return false;
}
} catch (RemoteException e) {
}
+ return true;
+ }
+
+ /**
+ * Applies network rules to bandwidth and firewall controllers based on uid policy.
+ * @param uid The uid for which to apply the latest policy
+ */
+ void updateRulesForUidLocked(int uid) {
+ if (!isUidValidForRules(uid) || !hasInternetPermissions(uid)) return;
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
@@ -2598,7 +2639,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
-
return true;
}
case MSG_ADVISE_PERSIST_THRESHOLD: {
@@ -2831,13 +2871,5 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
removeRestrictBackgroundWhitelistedUidLocked(uid, true);
}
}
-
- @Override
- public void onPackageRemovedAllUsers(String packageName, int uid) {
- if (LOGV) Slog.v(TAG, "onPackageRemovedAllUsers: " + packageName + " ->" + uid);
- synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index ce18818c1fba..9820a1261684 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -234,9 +234,13 @@ public class ConditionProviders extends ManagedServices {
final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
r.info = info;
r.condition = c;
- if (mCallback != null) {
- mCallback.onConditionChanged(c.id, c);
- }
+ }
+ }
+ final int N = conditions.length;
+ for (int i = 0; i < N; i++) {
+ final Condition c = conditions[i];
+ if (mCallback != null) {
+ mCallback.onConditionChanged(c.id, c);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 36ddbcf50f07..0519cf237053 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1350,9 +1350,9 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public boolean doesAppUseTopics(String pkg, int uid) {
+ public boolean doesUserUseTopics(String pkg, int uid) {
enforceSystemOrSystemUI("Caller not system or systemui");
- return mRankingHelper.doesAppUseTopics(pkg, uid);
+ return mRankingHelper.doesUserUseTopics(pkg, uid);
}
/**
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 17bb9075b0eb..9773474c2273 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -36,7 +36,7 @@ public interface RankingConfig {
int getImportance(String packageName, int uid, Notification.Topic topic);
- boolean doesAppUseTopics(String packageName, int uid);
+ boolean doesUserUseTopics(String packageName, int uid);
boolean hasBannedTopics(String packageName, int uid);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 6554bf91cf18..91eab10e1258 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -521,15 +521,14 @@ public class RankingHelper implements RankingConfig {
}
@Override
- public boolean doesAppUseTopics(String pkgName, int uid) {
+ public boolean doesUserUseTopics(String pkgName, int uid) {
final Record r = getOrCreateRecord(pkgName, uid);
- int numTopics = r.topics.size();
- if (numTopics == 0
- || (numTopics == 1 && r.topics.containsKey(Notification.TOPIC_DEFAULT))) {
- return false;
- } else {
- return true;
+ for (Topic topic : r.topics.values()) {
+ if (topic.importance != Ranking.IMPORTANCE_UNSPECIFIED
+ && r.importance != topic.importance)
+ return true;
}
+ return false;
}
private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d82bb3d1ed9f..c6613f54d63c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -201,8 +201,7 @@ public class LauncherAppsService extends SystemService {
long ident = Binder.clearCallingIdentity();
try {
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return new ParceledListSlice<>(apps);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -220,7 +219,7 @@ public class LauncherAppsService extends SystemService {
long ident = Binder.clearCallingIdentity();
try {
ResolveInfo app = mPm.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return app;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -239,7 +238,7 @@ public class LauncherAppsService extends SystemService {
try {
IPackageManager pm = AppGlobals.getPackageManager();
PackageInfo info = pm.getPackageInfo(packageName,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null && info.applicationInfo.enabled;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -277,7 +276,7 @@ public class LauncherAppsService extends SystemService {
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
return info != null;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -303,7 +302,7 @@ public class LauncherAppsService extends SystemService {
try {
IPackageManager pm = AppGlobals.getPackageManager();
ActivityInfo info = pm.getActivityInfo(component,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
if (!info.exported) {
throw new SecurityException("Cannot launch non-exported components "
+ component);
@@ -313,7 +312,7 @@ public class LauncherAppsService extends SystemService {
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
final int size = apps.size();
for (int i = 0; i < size; ++i) {
ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index da62a2d6fc84..94b3b2d613cf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -55,8 +55,6 @@ import static com.android.server.pm.Installer.DEXOPT_OTA;
public class OtaDexoptService extends IOtaDexopt.Stub {
private final static String TAG = "OTADexopt";
private final static boolean DEBUG_DEXOPT = true;
- // Apps used in the last 7 days.
- private final static long DEXOPT_LRU_THRESHOLD_IN_MINUTES = 7 * 24 * 60;
private final Context mContext;
private final PackageDexOptimizer mPackageDexOptimizer;
@@ -94,69 +92,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
if (mDexoptPackages != null) {
throw new IllegalStateException("already called prepare()");
}
-
- mDexoptPackages = new LinkedList<>();
-
- ArrayList<PackageParser.Package> pkgs;
synchronized (mPackageManagerService.mPackages) {
- pkgs = new ArrayList<PackageParser.Package>(mPackageManagerService.mPackages.values());
- }
-
- // Sort apps by importance for dexopt ordering. Important apps are given more priority
- // in case the device runs out of space.
-
- // Give priority to core apps.
- for (PackageParser.Package pkg : pkgs) {
- if (pkg.coreApp) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding core app " + mDexoptPackages.size() + ": " + pkg.packageName);
- }
- mDexoptPackages.add(pkg);
- }
- }
- pkgs.removeAll(mDexoptPackages);
-
- // Give priority to system apps that listen for pre boot complete.
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- for (PackageParser.Package pkg : pkgs) {
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding pre boot system app " + mDexoptPackages.size() + ": " +
- pkg.packageName);
- }
- mDexoptPackages.add(pkg);
- }
- }
- pkgs.removeAll(mDexoptPackages);
-
- // Filter out packages that aren't recently used, add all remaining apps.
- // TODO: add a property to control this?
- if (mPackageManagerService.isHistoricalPackageUsageAvailable()) {
- filterRecentlyUsedApps(pkgs, DEXOPT_LRU_THRESHOLD_IN_MINUTES * 60 * 1000);
- }
- mDexoptPackages.addAll(pkgs);
-
- // Now go ahead and also add the libraries required for these packages.
- // TODO: Think about interleaving things.
- Set<PackageParser.Package> dependencies = new HashSet<>();
- for (PackageParser.Package p : mDexoptPackages) {
- dependencies.addAll(mPackageManagerService.findSharedNonSystemLibraries(p));
- }
- if (!dependencies.isEmpty()) {
- dependencies.removeAll(mDexoptPackages);
- }
- mDexoptPackages.addAll(dependencies);
-
- if (DEBUG_DEXOPT) {
- StringBuilder sb = new StringBuilder();
- for (PackageParser.Package pkg : mDexoptPackages) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(pkg.packageName);
- }
- Log.i(TAG, "Packages to be optimized: " + sb.toString());
+ mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+ mPackageManagerService.mPackages.values(), mPackageManagerService);
}
}
@@ -228,29 +166,6 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
return pkgNames;
}
- private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
- long dexOptLRUThresholdInMills) {
- // Filter out packages that aren't recently used.
- int total = pkgs.size();
- int skipped = 0;
- long now = System.currentTimeMillis();
- for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
- PackageParser.Package pkg = i.next();
- long then = pkg.mLastPackageUsageTimeInMills;
- if (then + dexOptLRUThresholdInMills < now) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
- }
- i.remove();
- skipped++;
- }
- }
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
- }
- }
-
private static class OTADexoptPackageDexOptimizer extends
PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 23a58d06eed1..928c19fa84be 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -212,8 +212,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mCallbacks = new Callbacks(mInstallThread.getLooper());
mSessionsFile = new AtomicFile(
- new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
- mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions");
+ new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
+ mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
mSessionsDir.mkdirs();
synchronized (mSessions) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e47d5144cce0..7b6247cce8e2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -99,7 +99,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
@@ -141,7 +140,6 @@ import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManagerInternal;
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;
@@ -265,7 +263,6 @@ import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -1421,160 +1418,33 @@ public class PackageManagerService extends IPackageManager.Stub {
PostInstallData data = mRunningInstalls.get(msg.arg1);
mRunningInstalls.delete(msg.arg1);
- boolean deleteOld = false;
if (data != null) {
InstallArgs args = data.args;
- PackageInstalledInfo res = data.res;
-
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- //TODO: Broadcast for child packages too
- final String packageName = res.pkg.applicationInfo.packageName;
- res.removedInfo.sendBroadcast(false, true, false);
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.uid);
-
- // Now that we successfully installed the package, grant runtime
- // permissions if requested before broadcasting the install.
- if ((args.installFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
- && res.pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.M) {
- grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
- args.installGrantPermissions);
- }
-
- synchronized (mPackages) {
- mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
- }
-
- // Determine the set of users who are adding this
- // package for the first time vs. those who are seeing
- // an update.
- int[] firstUsers;
- int[] updateUsers = new int[0];
- if (res.origUsers == null || res.origUsers.length == 0) {
- firstUsers = res.newUsers;
- } else {
- firstUsers = new int[0];
- for (int i=0; i<res.newUsers.length; i++) {
- int user = res.newUsers[i];
- boolean isNew = true;
- for (int j=0; j<res.origUsers.length; j++) {
- if (res.origUsers[j] == user) {
- isNew = false;
- break;
- }
- }
- if (isNew) {
- int[] newFirst = new int[firstUsers.length+1];
- System.arraycopy(firstUsers, 0, newFirst, 0,
- firstUsers.length);
- newFirst[firstUsers.length] = user;
- firstUsers = newFirst;
- } else {
- int[] newUpdate = new int[updateUsers.length+1];
- System.arraycopy(updateUsers, 0, newUpdate, 0,
- updateUsers.length);
- newUpdate[updateUsers.length] = user;
- updateUsers = newUpdate;
- }
- }
- }
- // don't broadcast for ephemeral installs/updates
- final boolean isEphemeral = isEphemeral(res.pkg);
- if (!isEphemeral) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/, null /*targetPackage*/,
- null /*finishedReceiver*/, firstUsers);
- }
- final boolean update = res.removedInfo.removedPackage != null;
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- if (!isEphemeral) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/, null /*targetPackage*/,
- null /*finishedReceiver*/, updateUsers);
- }
- if (update) {
- if (!isEphemeral) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUsers);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null /*package*/, null /*extras*/, 0 /*flags*/,
- packageName /*targetPackage*/,
- null /*finishedReceiver*/, updateUsers);
- }
-
- // treat asec-hosted packages like removable media on upgrade
- if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg
- + " is ASEC-hosted -> AVAILABLE");
- }
- int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
- ArrayList<String> pkgList = new ArrayList<String>(1);
- pkgList.add(packageName);
- sendResourcesChangedBroadcast(true, true,
- pkgList,uidArray, null);
- }
- }
- if (res.removedInfo.args != null) {
- // Remove the replaced package's older resources safely now
- deleteOld = true;
- }
-
-
- // Work that needs to happen on first install within each user
- if (firstUsers.length > 0) {
- for (int userId : firstUsers) {
- synchronized (mPackages) {
- // If this app is a browser and it's newly-installed for
- // some users, clear any default-browser state in those
- // users. The app's nature doesn't depend on the user,
- // so we can just check its browser nature in any user
- // and generalize.
- if (packageIsBrowser(packageName, firstUsers[0])) {
- mSettings.setDefaultBrowserPackageNameLPw(
- null, userId);
- }
-
- // We may also need to apply pending (restored) runtime
- // permission grants within these users.
- mSettings.applyPendingPermissionGrantsLPw(
- packageName, userId);
- }
- }
- }
- // Log current value of "unknown sources" setting
- EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
- getUnknownSourcesSettings());
- }
- // Force a gc to clear up things
- Runtime.getRuntime().gc();
- // We delete after a gc for applications on sdcard.
- if (deleteOld) {
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(true);
- }
- }
- if (args.observer != null) {
- try {
- Bundle extras = extrasForInstallResult(res);
- args.observer.onPackageInstalled(res.name, res.returnCode,
- res.returnMsg, extras);
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
+ PackageInstalledInfo parentRes = data.res;
+
+ final boolean grantPermissions = (args.installFlags
+ & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
+ final String[] grantedPermissions = args.installGrantPermissions;
+
+ // Handle the parent package
+ handlePackagePostInstall(parentRes, grantPermissions, grantedPermissions,
+ args.observer);
+
+ // Handle the child packages
+ final int childCount = (parentRes.addedChildPackages != null)
+ ? parentRes.addedChildPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
+ handlePackagePostInstall(childRes, grantPermissions, grantedPermissions,
+ args.observer);
}
+
+ // Log tracing if needed
if (args.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
args.traceCookie);
}
- return;
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
@@ -1761,6 +1631,185 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
+ String[] grantedPermissions, IPackageInstallObserver2 installObserver) {
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ // Send the removed broadcasts
+ if (res.removedInfo != null) {
+ res.removedInfo.sendPackageRemovedBroadcasts();
+ }
+
+ // Now that we successfully installed the package, grant runtime
+ // permissions if requested before broadcasting the install.
+ if (grantPermissions && res.pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M) {
+ grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
+ }
+
+ final boolean update = res.removedInfo != null
+ && res.removedInfo.removedPackage != null;
+
+ // If this is the first time we have child packages for a disabled privileged
+ // 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);
+ }
+ }
+
+ synchronized (mPackages) {
+ mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
+ }
+
+ final String packageName = res.pkg.applicationInfo.packageName;
+ Bundle extras = new Bundle(1);
+ extras.putInt(Intent.EXTRA_UID, res.uid);
+
+ // Determine the set of users who are adding this package for
+ // the first time vs. those who are seeing an update.
+ int[] firstUsers = EMPTY_INT_ARRAY;
+ int[] updateUsers = EMPTY_INT_ARRAY;
+ if (res.origUsers == null || res.origUsers.length == 0) {
+ firstUsers = res.newUsers;
+ } else {
+ for (int newUser : res.newUsers) {
+ boolean isNew = true;
+ for (int origUser : res.origUsers) {
+ if (origUser == newUser) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
+ } else {
+ updateUsers = ArrayUtils.appendInt(updateUsers, newUser);
+ }
+ }
+ }
+
+ // Send installed broadcasts if the install/update is not ephemeral
+ if (!isEphemeral(res.pkg)) {
+ // Send added for users that see the package for the first time
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/, null /*targetPackage*/,
+ null /*finishedReceiver*/, firstUsers);
+
+ // Send added for users that don't see the package for the first time
+ if (update) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/, null /*targetPackage*/,
+ null /*finishedReceiver*/, updateUsers);
+
+ // Send replaced for users that don't see the package for the first time
+ if (update) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUsers);
+ sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUsers);
+ }
+
+ // Send broadcast package appeared if forward locked/external for all users
+ // treat asec-hosted packages like removable media on upgrade
+ if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + res.pkg
+ + " is ASEC-hosted -> AVAILABLE");
+ }
+ final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
+ ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(packageName);
+ sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
+ }
+ }
+
+ // Work that needs to happen on first install within each user
+ if (firstUsers != null && firstUsers.length > 0) {
+ synchronized (mPackages) {
+ for (int userId : firstUsers) {
+ // If this app is a browser and it's newly-installed for some
+ // users, clear any default-browser state in those users. The
+ // app's nature doesn't depend on the user, so we can just check
+ // its browser nature in any user and generalize.
+ if (packageIsBrowser(packageName, userId)) {
+ mSettings.setDefaultBrowserPackageNameLPw(null, userId);
+ }
+
+ // We may also need to apply pending (restored) runtime
+ // permission grants within these users.
+ mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+ }
+ }
+ }
+
+ // Log current value of "unknown sources" setting
+ EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+ getUnknownSourcesSettings());
+
+ // Force a gc to clear up things
+ Runtime.getRuntime().gc();
+
+ // Remove the replaced package's older resources safely now
+ // We delete after a gc for applications on sdcard.
+ if (res.removedInfo != null && res.removedInfo.args != null) {
+ synchronized (mInstallLock) {
+ res.removedInfo.args.doPostDeleteLI(true);
+ }
+ }
+ }
+
+ // If someone is watching installs - notify them
+ if (installObserver != null) {
+ try {
+ Bundle extras = extrasForInstallResult(res);
+ installObserver.onPackageInstalled(res.name, res.returnCode,
+ res.returnMsg, extras);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+
+ 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) {
@@ -1815,18 +1864,10 @@ public class PackageManagerService extends IPackageManager.Stub {
}
};
- private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId,
+ private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
String[] grantedPermissions) {
- if (userId >= UserHandle.USER_SYSTEM) {
+ for (int userId : userIds) {
grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
- } else if (userId == UserHandle.USER_ALL) {
- final int[] userIds;
- synchronized (mPackages) {
- userIds = UserManagerService.getInstance().getUserIds();
- }
- for (int someUserId : userIds) {
- grantRequestedRuntimePermissionsForUser(pkg, someUserId, grantedPermissions);
- }
}
// We could have touched GID membership, so flush out packages.list
@@ -2229,7 +2270,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
- removePackageSettingLI(scannedPkg, true);
+ removePackageLI(scannedPkg, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
@@ -6576,7 +6617,7 @@ public class PackageManagerService extends IPackageManager.Stub {
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
+ " signatures don't match existing userdata copy; removing");
- deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false, null);
+ deletePackageLI(pkg.packageName, null, true, null, 0, null, false, null);
ps = null;
} else {
/*
@@ -6778,30 +6819,17 @@ public class PackageManagerService extends IPackageManager.Stub {
// Extract pacakges only if profile-guided compilation is enabled because
// otherwise BackgroundDexOptService will not dexopt them later.
if (mUseJitProfiles) {
- ArraySet<String> pkgs = getOptimizablePackages();
- if (pkgs != null) {
- for (String pkg : pkgs) {
- performDexOpt(pkg, null /* instructionSet */, false /* useProfiles */,
- true /* extractOnly */, false /* force */);
- }
+ List<PackageParser.Package> pkgs;
+ synchronized (mPackages) {
+ pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
- }
- }
-
- private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, userId);
- } catch (RemoteException e) {
- }
- ArraySet<String> pkgNames = new ArraySet<String>();
- if (ris != null) {
- for (ResolveInfo ri : ris) {
- pkgNames.add(ri.activityInfo.packageName);
+ for (PackageParser.Package pkg : pkgs) {
+ if (PackageDexOptimizer.canOptimizePackage(pkg)) {
+ performDexOpt(pkg.packageName, null /* instructionSet */,
+ false /* useProfiles */, true /* extractOnly */, false /* force */);
+ }
}
}
- return pkgNames;
}
@Override
@@ -8733,11 +8761,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private void removePackageSettingLI(PackageParser.Package pkg, boolean chatty) {
+ private void removePackageLI(PackageParser.Package pkg, boolean chatty) {
// Remove the parent package setting
PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps != null) {
- removePackageSettingLI(ps, chatty);
+ removePackageLI(ps, chatty);
}
// Remove the child package setting
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -8745,12 +8773,12 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package childPkg = pkg.childPackages.get(i);
ps = (PackageSetting) childPkg.mExtras;
if (ps != null) {
- removePackageSettingLI(ps, chatty);
+ removePackageLI(ps, chatty);
}
}
}
- void removePackageSettingLI(PackageSetting ps, boolean chatty) {
+ void removePackageLI(PackageSetting ps, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + ps.name);
@@ -10451,16 +10479,21 @@ public class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
- private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting, int userId) {
+ private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
+ int userId) {
+ final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
+ sendPackageAddedForUser(packageName, isSystem, pkgSetting.appId, userId);
+ }
+
+ private void sendPackageAddedForUser(String packageName, boolean isSystem,
+ int appId, int userId) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+ extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, appId));
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, 0, null, null, new int[] {userId});
try {
IActivityManager am = ActivityManagerNative.getDefault();
- final boolean isSystem =
- isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
if (isSystem && am.isUserRunning(userId, 0)) {
// The just-installed/enabled app is bundled on the system, so presumed
// to be able to run automatically without needing an explicit launch.
@@ -10533,7 +10566,7 @@ public class PackageManagerService extends IPackageManager.Stub {
info.removedPackage = packageName;
info.removedUsers = new int[] {userId};
info.uid = UserHandle.getUid(userId, pkgSetting.appId);
- info.sendBroadcast(false, false, false);
+ info.sendPackageRemovedBroadcasts();
}
private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended) {
@@ -11084,10 +11117,10 @@ public class PackageManagerService extends IPackageManager.Stub {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
- res.returnCode = currentStatus;
+ res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
- res.removedInfo = new PackageRemovedInfo();
+ res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
@@ -11099,7 +11132,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.
- final boolean update = res.removedInfo.removedPackage != null;
+ final boolean update = res.removedInfo != null
+ && res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
@@ -12688,25 +12722,42 @@ public class PackageManagerService extends IPackageManager.Stub {
int returnCode;
String returnMsg;
PackageRemovedInfo removedInfo;
+ ArrayMap<String, PackageInstalledInfo> addedChildPackages;
public void setError(int code, String msg) {
- returnCode = code;
- returnMsg = msg;
+ setReturnCode(code);
+ setReturnMessage(msg);
Slog.w(TAG, msg);
}
public void setError(String msg, PackageParserException e) {
- returnCode = e.error;
- returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+ setReturnCode(e.error);
+ setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
Slog.w(TAG, msg, e);
}
public void setError(String msg, PackageManagerException e) {
returnCode = e.error;
- returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+ setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
Slog.w(TAG, msg, e);
}
+ public void setReturnCode(int returnCode) {
+ this.returnCode = returnCode;
+ final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ addedChildPackages.valueAt(i).returnCode = returnCode;
+ }
+ }
+
+ private void setReturnMessage(String returnMsg) {
+ this.returnMsg = returnMsg;
+ final int childCount = (addedChildPackages != null) ? addedChildPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ addedChildPackages.valueAt(i).returnMsg = returnMsg;
+ }
+ }
+
// In some error cases we want to convey more info back to the observer
String origPackage;
String origPermission;
@@ -12724,9 +12775,6 @@ public class PackageManagerService extends IPackageManager.Stub {
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
- // TODO: b/23350563
- final boolean dataDirExists = Environment
- .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_SYSTEM, pkgName).exists();
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -12751,21 +12799,17 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
- prepareAppDataAfterInstall(newPackage);
+ updateSettingsLI(newPackage, installerPackageName, null, res, user);
- // delete the partially installed application. the data directory will have to be
- // restored if it was already existing
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
- deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
- dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
- res.removedInfo, true, null);
- }
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ prepareAppDataAfterInstall(newPackage);
+ } else {
+ // Remove package from internal structures, but keep around any
+ // data that might have already existed
+ deletePackageLI(pkgName, UserHandle.ALL, false, null,
+ PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
+ }
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -12810,14 +12854,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
- UserHandle user, String installerPackageName, String volumeUuid,
- PackageInstalledInfo res) {
+ UserHandle user, String installerPackageName, PackageInstalledInfo res) {
final boolean isEphemeral = (parseFlags & PackageParser.PARSE_IS_EPHEMERAL) != 0;
final PackageParser.Package oldPackage;
final String pkgName = pkg.packageName;
final int[] allUsers;
- final boolean[] perUserInstalled;
// First find the old package info and check signatures
synchronized(mPackages) {
@@ -12826,7 +12868,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isEphemeral && !oldIsEphemeral) {
// can't downgrade from full to ephemeral
Slog.w(TAG, "Can't replace app with ephemeral: " + pkgName);
- res.returnCode = PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID;
+ res.setReturnCode(PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID);
return;
}
if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
@@ -12850,26 +12892,58 @@ public class PackageManagerService extends IPackageManager.Stub {
// In case of rollback, remember per-user/profile install state
allUsers = sUserManager.getUserIds();
- perUserInstalled = new boolean[allUsers.length];
- for (int i = 0; i < allUsers.length; i++) {
- perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
+ }
+
+ // Update what is removed
+ res.removedInfo = new PackageRemovedInfo();
+ res.removedInfo.uid = oldPackage.applicationInfo.uid;
+ res.removedInfo.removedPackage = oldPackage.packageName;
+ res.removedInfo.isUpdate = true;
+ final int childCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+ if (res.addedChildPackages != null) {
+ PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
+ if (childRes != null) {
+ childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ childRes.removedInfo.isUpdate = true;
+ childPackageUpdated = true;
+ }
+ }
+ if (!childPackageUpdated) {
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo();
+ childRemovedRes.removedPackage = childPkg.packageName;
+ childRemovedRes.isUpdate = false;
+ childRemovedRes.dataRemoved = true;
+ synchronized (mPackages) {
+ PackageSetting childPs = mSettings.peekPackageLPr(childPkg.packageName);
+ if (childPs != null) {
+ childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);
+ }
+ }
+ if (res.removedInfo.removedChildPackages == null) {
+ res.removedInfo.removedChildPackages = new ArrayMap<>();
+ }
+ res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes);
}
}
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, installerPackageName, res);
} else {
replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, installerPackageName, res);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- PackageInstalledInfo res) {
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ deletedPackage);
@@ -12881,7 +12955,7 @@ public class PackageManagerService extends IPackageManager.Stub {
? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
// First delete the existing package while retaining the data directory
- if (!deletePackageLI(pkgName, null, true, null, null, PackageManager.DELETE_KEEP_DATA,
+ if (!deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA,
res.removedInfo, true, pkg)) {
// If the existing package wasn't successfully deleted
res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
@@ -12906,8 +12980,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, allUsers,
- perUserInstalled, res, user);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
prepareAppDataAfterInstall(newPackage);
addedPkg = true;
} catch (PackageManagerException e) {
@@ -12920,8 +12993,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Revert all internal state mutations and added folders for the failed install
if (addedPkg) {
- deletePackageLI(pkgName, null, true, allUsers, perUserInstalled,
- PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
+ deletePackageLI(pkgName, null, true, allUsers, PackageManager.DELETE_KEEP_DATA,
+ res.removedInfo, true, null);
}
// Restore the old package
@@ -12955,13 +13028,34 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
}
+ } else {
+ synchronized (mPackages) {
+ PackageSetting ps = mSettings.peekPackageLPr(pkg.packageName);
+ if (ps != null) {
+ res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;
+ if (res.removedInfo.removedChildPackages != null) {
+ final int childCount = res.removedInfo.removedChildPackages.size();
+ // Iterate in reverse as we may modify the collection
+ for (int i = childCount - 1; i >= 0; i--) {
+ String childPackageName = res.removedInfo.removedChildPackages.keyAt(i);
+ if (res.addedChildPackages.containsKey(childPackageName)) {
+ res.removedInfo.removedChildPackages.removeAt(i);
+ } else {
+ PackageRemovedInfo childInfo = res.removedInfo
+ .removedChildPackages.valueAt(i);
+ childInfo.removedForAllUsers = mPackages.get(
+ childInfo.removedPackage) == null;
+ }
+ }
+ }
+ }
+ }
}
}
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- PackageInstalledInfo res) {
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
@@ -12977,12 +13071,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Kill package processes including services, providers, etc.
killPackage(deletedPackage, "replace sys pkg");
- // Report the result for the parent package only
- res.removedInfo.uid = deletedPackage.applicationInfo.uid;
- res.removedInfo.removedPackage = deletedPackage.packageName;
-
// Remove existing system package
- removePackageSettingLI(deletedPackage, true);
+ removePackageLI(deletedPackage, true);
disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
if (!disabledSystem) {
@@ -13000,7 +13090,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Successfully disabled the old package. Now proceed with re-installation
deleteCodeCacheDirsLI(pkg);
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
@@ -13015,7 +13105,8 @@ public class PackageManagerService extends IPackageManager.Stub {
System.currentTimeMillis());
// Check for shared user id changes
- String invalidPackageName = getParentOrChildPackageChangedSharedUser(deletedPackage, newPackage);
+ String invalidPackageName = getParentOrChildPackageChangedSharedUser(
+ deletedPackage, newPackage);
if (invalidPackageName != null) {
res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Forbidding shared user change from " + deletedPkgSetting.sharedUser
@@ -13043,18 +13134,20 @@ public class PackageManagerService extends IPackageManager.Stub {
if (childPackageDeleted) {
PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
deletedChildPkg.packageName);
- if (ps != null) {
- removePackageDataLI(ps, allUsers, perUserInstalled, null, 0, false);
+ if (ps != null && res.removedInfo.removedChildPackages != null) {
+ PackageRemovedInfo removedChildRes = res.removedInfo
+ .removedChildPackages.get(deletedChildPkg.packageName);
+ removePackageDataLI(ps, allUsers, removedChildRes, 0, false);
+ removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;
}
}
}
- updateSettingsLI(newPackage, installerPackageName, allUsers,
- perUserInstalled, res, user);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
prepareAppDataAfterInstall(newPackage);
}
} catch (PackageManagerException e) {
- res.returnCode = INSTALL_FAILED_INTERNAL_ERROR;
+ res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -13235,22 +13328,23 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) {
+ int[] allUsers, PackageInstalledInfo res, UserHandle user) {
// Update the parent package setting
- updateSettingsInternalLI(newPackage, installerPackageName, allUsers, perUserInstalled,
+ updateSettingsInternalLI(newPackage, installerPackageName, allUsers, res.origUsers,
res, user);
// Update the child packages setting
final int childCount = (newPackage.childPackages != null)
? newPackage.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = newPackage.childPackages.get(i);
- updateSettingsInternalLI(childPackage, installerPackageName, allUsers, perUserInstalled,
- res, user);
+ PackageInstalledInfo childRes = res.addedChildPackages.get(childPackage.packageName);
+ updateSettingsInternalLI(childPackage, installerPackageName, allUsers,
+ childRes.origUsers, childRes, user);
}
}
private void updateSettingsInternalLI(PackageParser.Package newPackage,
- String installerPackageName, int[] allUsers, boolean[] perUserInstalled,
+ String installerPackageName, int[] allUsers, int[] installedForUsers,
PackageInstalledInfo res, UserHandle user) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
@@ -13274,26 +13368,30 @@ public class PackageManagerService extends IPackageManager.Stub {
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final int userId = user.getIdentifier();
if (ps != null) {
if (isSystemApp(newPackage)) {
- // NB: implicit assumption that system package upgrades apply to all users
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
+ // Enable system package for requested users
if (res.origUsers != null) {
- for (int userHandle : res.origUsers) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- userHandle, installerPackageName);
+ for (int origUserId : res.origUsers) {
+ if (userId == UserHandle.USER_ALL || userId == origUserId) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ origUserId, installerPackageName);
+ }
}
}
// Also convey the prior install/uninstall state
- if (allUsers != null && perUserInstalled != null) {
- for (int i = 0; i < allUsers.length; i++) {
+ if (allUsers != null && installedForUsers != null) {
+ for (int currentUserId : allUsers) {
+ final boolean installed = ArrayUtils.contains(
+ installedForUsers, currentUserId);
if (DEBUG_INSTALL) {
- Slog.d(TAG, " user " + allUsers[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + currentUserId + " => " + installed);
}
- ps.setInstalled(perUserInstalled[i], allUsers[i]);
+ ps.setInstalled(installed, currentUserId);
}
// these install state changes will be persisted in the
// upcoming call to mSettings.writeLPr().
@@ -13301,7 +13399,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
- int userId = user.getIdentifier();
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
@@ -13312,7 +13409,7 @@ public class PackageManagerService extends IPackageManager.Stub {
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
@@ -13343,11 +13440,12 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
- // moving a complete application; perfom an initial scan on the new install location
+ // moving a complete application; perform an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
+
// Result object to be returned
- res.returnCode = PackageManager.INSTALL_SUCCEEDED;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
@@ -13355,7 +13453,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (ephemeral && (forwardLocked || onExternal)) {
Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
+ " external=" + onExternal);
- res.returnCode = PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID;
+ res.setReturnCode(PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID);
return;
}
@@ -13380,6 +13478,33 @@ public class PackageManagerService extends IPackageManager.Stub {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ // If we are installing a clustered package add results for the children
+ if (pkg.childPackages != null) {
+ synchronized (mPackages) {
+ final int childCount = pkg.childPackages.size();
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ PackageInstalledInfo childRes = new PackageInstalledInfo();
+ childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ childRes.pkg = childPkg;
+ childRes.name = childPkg.packageName;
+ PackageSetting childPs = mSettings.peekPackageLPr(childPkg.packageName);
+ if (childPs != null) {
+ childRes.origUsers = childPs.queryInstalledUsers(
+ sUserManager.getUserIds(), true);
+ }
+ if ((mPackages.containsKey(childPkg.packageName))) {
+ childRes.removedInfo = new PackageRemovedInfo();
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ }
+ if (res.addedChildPackages == null) {
+ res.addedChildPackages = new ArrayMap<>();
+ }
+ res.addedChildPackages.put(childPkg.packageName, childRes);
+ }
+ }
+ }
+
// If package doesn't declare API override, mark that we have an install
// time CPU ABI override.
if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {
@@ -13611,7 +13736,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
- installerPackageName, volumeUuid, res);
+ installerPackageName, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
@@ -13621,6 +13746,17 @@ public class PackageManagerService extends IPackageManager.Stub {
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
+
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
+ PackageSetting childPs = mSettings.peekPackageLPr(childPkg.packageName);
+ if (childPs != null) {
+ childRes.newUsers = childPs.queryInstalledUsers(
+ sUserManager.getUserIds(), true);
+ }
+ }
}
}
@@ -13637,10 +13773,17 @@ public class PackageManagerService extends IPackageManager.Stub {
MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
- mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
- final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+ Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);
mHandler.sendMessage(msg);
+
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+ msg.obj = new IFVerificationParams(childPkg, replacing, userId, verifierUid);
+ mHandler.sendMessage(msg);
+ }
}
private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
@@ -13959,60 +14102,37 @@ public class PackageManagerService extends IPackageManager.Stub {
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
- boolean removedForAllUsers = false;
- boolean systemUpdate = false;
-
PackageParser.Package uninstalledPkg;
// for the uninstall-updates case and restricted profiles, remember the per-
- // userhandle installed state
+ // user handle installed state
int[] allUsers;
- boolean[] perUserInstalled;
synchronized (mPackages) {
uninstalledPkg = mPackages.get(packageName);
PackageSetting ps = mSettings.mPackages.get(packageName);
- allUsers = sUserManager.getUserIds();
- perUserInstalled = new boolean[allUsers.length];
- for (int i = 0; i < allUsers.length; i++) {
- perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;
+ if (ps == null || uninstalledPkg == null) {
+ Slog.w(TAG, "Not removing non-existent package " + packageName);
+ return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
+ allUsers = sUserManager.getUserIds();
+ info.origUsers = ps.queryInstalledUsers(allUsers, true);
}
synchronized (mInstallLock) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
- res = deletePackageLI(packageName, removeForUser, true, allUsers, perUserInstalled,
+ res = deletePackageLI(packageName, removeForUser, true, allUsers,
flags | REMOVE_CHATTY, info, true, null);
- systemUpdate = info.isRemovedPackageSystemUpdate;
synchronized (mPackages) {
if (res) {
- if (!systemUpdate && mPackages.get(packageName) == null) {
- removedForAllUsers = true;
- }
mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPkg);
}
}
- if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
- + " removedForAllUsers=" + removedForAllUsers);
}
if (res) {
- info.sendBroadcast(true, systemUpdate, removedForAllUsers);
-
- // If the removed package was a system update, the old system package
- // was re-enabled; we need to broadcast this information
- if (systemUpdate) {
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
- ? info.removedAppId : info.uid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
-
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0, null, null, null);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0, null, null, null);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, 0, packageName, null, null);
- }
+ info.sendPackageRemovedBroadcasts();
+ info.sendSystemPackageUpdatedBroadcasts();
+ info.sendSystemPackageAppearedBroadcasts();
}
// Force a gc here.
Runtime.getRuntime().gc();
@@ -14031,25 +14151,78 @@ public class PackageManagerService extends IPackageManager.Stub {
String removedPackage;
int uid = -1;
int removedAppId = -1;
+ int[] origUsers;
int[] removedUsers = null;
boolean isRemovedPackageSystemUpdate = false;
+ boolean isUpdate;
+ boolean dataRemoved;
+ boolean removedForAllUsers;
// Clean up resources deleted packages.
InstallArgs args = null;
+ ArrayMap<String, PackageRemovedInfo> removedChildPackages;
+ ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
- void sendBroadcast(boolean fullRemove, boolean replacing, boolean removedForAllUsers) {
- Bundle extras = new Bundle(1);
+ void sendPackageRemovedBroadcasts() {
+ sendPackageRemovedBroadcastInternal();
+ final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
+ childInfo.sendPackageRemovedBroadcastInternal();
+ }
+ }
+
+ void sendSystemPackageUpdatedBroadcasts() {
+ if (isRemovedPackageSystemUpdate) {
+ sendSystemPackageUpdatedBroadcastsInternal();
+ final int childCount = (removedChildPackages != null)
+ ? removedChildPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
+ if (childInfo.isRemovedPackageSystemUpdate) {
+ childInfo.sendSystemPackageUpdatedBroadcastsInternal();
+ }
+ }
+ }
+ }
+
+ void sendSystemPackageAppearedBroadcasts() {
+ final int packageCount = (appearedChildPackages != null)
+ ? appearedChildPackages.size() : 0;
+ for (int i = 0; i < packageCount; i++) {
+ PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i);
+ for (int userId : installedInfo.newUsers) {
+ sendPackageAddedForUser(installedInfo.name, true,
+ UserHandle.getAppId(installedInfo.uid), userId);
+ }
+ }
+ }
+
+ private void sendSystemPackageUpdatedBroadcastsInternal() {
+ Bundle extras = new Bundle(2);
extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
- if (replacing) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage,
+ extras, 0, null, null, null);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
+ extras, 0, null, null, null);
+ sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
+ null, 0, removedPackage, null, null);
+ }
+
+ private void sendPackageRemovedBroadcastInternal() {
+ Bundle extras = new Bundle(2);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+ extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
+ if (isUpdate || isRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
extras, 0, null, null, removedUsers);
- if (fullRemove && !replacing) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, 0, null, null, removedUsers);
+ if (dataRemoved && !isRemovedPackageSystemUpdate) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ removedPackage, extras, 0, null, null, removedUsers);
}
}
if (removedAppId >= 0) {
@@ -14065,12 +14238,11 @@ public class PackageManagerService extends IPackageManager.Stub {
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
*/
- private void removePackageDataLI(PackageSetting ps,
- int[] allUserHandles, boolean[] perUserInstalled,
+ private void removePackageDataLI(PackageSetting ps, int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = ps.name;
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
- removePackageSettingLI(ps, (flags&REMOVE_CHATTY) != 0);
+ removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
// reader
@@ -14085,6 +14257,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
removeDataDirsLI(ps.volumeUuid, packageName);
+ if (outInfo != null) {
+ outInfo.dataRemoved = true;
+ }
schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
}
// writer
@@ -14126,16 +14301,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
- if (allUserHandles != null && perUserInstalled != null) {
+ if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
- for (int i = 0; i < allUserHandles.length; i++) {
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + allUserHandles[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + userId + " => " + installed);
}
- ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
+ ps.setInstalled(installed, userId);
}
}
}
@@ -14167,16 +14342,15 @@ public class PackageManagerService extends IPackageManager.Stub {
* Tries to delete system package.
*/
private boolean deleteSystemPackageLI(PackageParser.Package deletedPkg,
- PackageSetting deletedPs, int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo, boolean writeSettings,
- PackageParser.Package replacingPackage) {
+ PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
+ boolean writeSettings) {
if (deletedPkg.parentPackage != null) {
Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
return false;
}
final boolean applyUserRestrictions
- = (allUserHandles != null) && (perUserInstalled != null);
+ = (allUserHandles != null) && (outInfo.origUsers != null);
final PackageSetting disabledPs;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
@@ -14199,14 +14373,31 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
Slog.d(TAG, "Remembering install states:");
- for (int i = 0; i < allUserHandles.length; i++) {
- Slog.d(TAG, " u=" + allUserHandles[i] + " inst=" + perUserInstalled[i]);
+ for (int userId : allUserHandles) {
+ final boolean finstalled = ArrayUtils.contains(outInfo.origUsers, userId);
+ Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
}
}
}
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
+ if (outInfo.removedChildPackages != null) {
+ final int childCount = (deletedPkg.childPackages != null)
+ ? deletedPkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = deletedPkg.childPackages.get(i).packageName;
+ if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
+ .contains(childPackageName)) {
+ PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
+ childPackageName);
+ if (childInfo != null) {
+ childInfo.isRemovedPackageSystemUpdate = true;
+ }
+ }
+ }
+ }
+
if (disabledPs.versionCode < deletedPs.versionCode) {
// Delete data for downgrades
flags &= ~PackageManager.DELETE_KEEP_DATA;
@@ -14214,8 +14405,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// Preserve data by setting flag
flags |= PackageManager.DELETE_KEEP_DATA;
}
+
boolean ret = deleteInstalledPackageLI(deletedPkg, true, flags, allUserHandles,
- perUserInstalled, outInfo, writeSettings, replacingPackage);
+ outInfo, writeSettings, disabledPs.pkg);
if (!ret) {
return false;
}
@@ -14261,14 +14453,14 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
- for (int i = 0; i < allUserHandles.length; i++) {
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + allUserHandles[i]
- + " => " + perUserInstalled[i]);
+ Slog.d(TAG, " user " + userId + " => " + installed);
}
- ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
+ ps.setInstalled(installed, userId);
- mSettings.writeRuntimePermissionsForUserLPr(allUserHandles[i], false);
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
// Regardless of writeSettings we need to ensure that this restriction
// state propagation is persisted
@@ -14284,7 +14476,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean deleteInstalledPackageLI(PackageParser.Package pkg,
boolean deleteCodeAndResources, int flags, int[] allUserHandles,
- boolean[] perUserInstalled, PackageRemovedInfo outInfo, boolean writeSettings,
+ PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
PackageSetting ps = null;
@@ -14302,11 +14494,26 @@ public class PackageManagerService extends IPackageManager.Stub {
if (outInfo != null) {
outInfo.uid = ps.appId;
}
+
+ if (outInfo != null && outInfo.removedChildPackages != null) {
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = ps.childPackageNames.get(i);
+ PackageSetting childPs = mSettings.mPackages.get(childPackageName);
+ if (childPs == null) {
+ return false;
+ }
+ PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
+ childPackageName);
+ if (childInfo != null) {
+ childInfo.uid = childPs.appId;
+ }
+ }
+ }
}
// Delete package data from internal structures and also remove data if flag is set
- removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags,
- writeSettings);
+ removePackageDataLI(ps, allUserHandles, outInfo, flags, writeSettings);
// Delete the child packages data
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -14316,18 +14523,21 @@ public class PackageManagerService extends IPackageManager.Stub {
childPs = mSettings.peekPackageLPr(pkg.childPackages.get(i).packageName);
}
if (childPs != null) {
+ PackageRemovedInfo childOutInfo = (outInfo != null
+ && outInfo.removedChildPackages != null)
+ ? outInfo.removedChildPackages.get(childPs.name) : null;
final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0
&& (replacingPackage != null
&& !replacingPackage.hasChildPackage(childPs.name))
? flags & ~DELETE_KEEP_DATA : flags;
- removePackageDataLI(childPs, allUserHandles, perUserInstalled, outInfo,
+ removePackageDataLI(childPs, allUserHandles, childOutInfo,
deleteFlags, writeSettings);
}
}
// Delete application code and resources only for parent packages
if (ps.pkg.parentPackage == null) {
- if (deleteCodeAndResources && (outInfo != null)) {
+ if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
@@ -14398,8 +14608,8 @@ public class PackageManagerService extends IPackageManager.Stub {
* This method handles package deletion in general
*/
private boolean deletePackageLI(String packageName, UserHandle user,
- boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo, boolean writeSettings,
+ boolean deleteCodeAndResources, int[] allUserHandles, int flags,
+ PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
@@ -14476,19 +14686,83 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ // If we are deleting a composite package for all users, keep track
+ // of result for each child.
+ if (ps.childPackageNames != null && outInfo != null) {
+ synchronized (mPackages) {
+ final int childCount = ps.childPackageNames.size();
+ outInfo.removedChildPackages = new ArrayMap<>(childCount);
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = ps.childPackageNames.get(i);
+ PackageRemovedInfo childInfo = new PackageRemovedInfo();
+ childInfo.removedPackage = childPackageName;
+ outInfo.removedChildPackages.put(childPackageName, childInfo);
+ PackageSetting childPs = mSettings.peekPackageLPr(childPackageName);
+ if (childPs != null) {
+ childInfo.origUsers = childPs.queryInstalledUsers(allUserHandles, true);
+ }
+ }
+ }
+ }
+
boolean ret = false;
if (isSystemApp(ps)) {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
- // When an updated system application is deleted we delete the existing resources as well and
- // fall back to existing code in system partition
- ret = deleteSystemPackageLI(ps.pkg, ps, allUserHandles, perUserInstalled,
- flags, outInfo, writeSettings, replacingPackage);
+ // When an updated system application is deleted we delete the existing resources
+ // as well and fall back to existing code in system partition
+ ret = deleteSystemPackageLI(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
// Kill application pre-emptively especially for apps on sd.
killApplication(packageName, ps.appId, "uninstall pkg");
ret = deleteInstalledPackageLI(ps.pkg, deleteCodeAndResources, flags, allUserHandles,
- perUserInstalled, outInfo, writeSettings, replacingPackage);
+ outInfo, writeSettings, replacingPackage);
+ }
+
+ // Take a note whether we deleted the package for all users
+ if (outInfo != null) {
+ outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
+ if (outInfo.removedChildPackages != null) {
+ synchronized (mPackages) {
+ final int childCount = outInfo.removedChildPackages.size();
+ for (int i = 0; i < childCount; i++) {
+ PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i);
+ if (childInfo != null) {
+ childInfo.removedForAllUsers = mPackages.get(
+ childInfo.removedPackage) == null;
+ }
+ }
+ }
+ }
+ // If we uninstalled an update to a system app there may be some
+ // child packages that appeared as they are declared in the system
+ // app but were not declared in the update.
+ if (isSystemApp(ps)) {
+ synchronized (mPackages) {
+ PackageSetting updatedPs = mSettings.peekPackageLPr(ps.name);
+ final int childCount = (updatedPs.childPackageNames != null)
+ ? updatedPs.childPackageNames.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = updatedPs.childPackageNames.get(i);
+ if (outInfo.removedChildPackages == null
+ || outInfo.removedChildPackages.indexOfKey(childPackageName) < 0) {
+ PackageSetting childPs = mSettings.peekPackageLPr(childPackageName);
+ if (childPs == null) {
+ continue;
+ }
+ PackageInstalledInfo installRes = new PackageInstalledInfo();
+ installRes.name = childPackageName;
+ installRes.newUsers = childPs.queryInstalledUsers(allUserHandles, true);
+ installRes.pkg = mPackages.get(childPackageName);
+ installRes.uid = childPs.pkg.applicationInfo.uid;
+ if (outInfo.appearedChildPackages == null) {
+ outInfo.appearedChildPackages = new ArrayMap<>();
+ }
+ outInfo.appearedChildPackages.put(childPackageName, installRes);
+ }
+ }
+ }
+ }
}
return ret;
@@ -17255,7 +17529,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// Delete package internally
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, null, false, null, null,
+ boolean res = deletePackageLI(pkgName, null, false, null,
PackageManager.DELETE_KEEP_DATA, outInfo, false, null);
if (res) {
pkgList.add(pkgName);
@@ -17401,7 +17675,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
final ApplicationInfo info = ps.pkg.applicationInfo;
final PackageRemovedInfo outInfo = new PackageRemovedInfo();
- if (deletePackageLI(ps.name, null, false, null, null,
+ if (deletePackageLI(ps.name, null, false, null,
PackageManager.DELETE_KEEP_DATA, outInfo, false, null)) {
unloaded.add(info);
} else {
@@ -17423,8 +17697,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
* recycled.
*/
private void reconcileUsers(String volumeUuid) {
+ // TODO: also reconcile DE directories
final File[] files = FileUtils
- .listFilesOrEmpty(Environment.getDataUserDirectory(volumeUuid));
+ .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
for (File file : files) {
if (!file.isDirectory()) continue;
@@ -17553,8 +17828,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ Integer.toHexString(flags));
- final File ceDir = Environment.getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
- final File deDir = Environment.getDataUserDeviceEncryptedDirectory(volumeUuid, userId);
+ final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
+ final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
boolean restoreconNeeded = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
new file mode 100644
index 000000000000..a3ac514ccd22
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -0,0 +1,153 @@
+/*
+ * 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.pm;
+
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+/**
+ * Class containing helper methods for the PackageManagerService.
+ *
+ * {@hide}
+ */
+public class PackageManagerServiceUtils {
+ private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+
+ private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
+ List<ResolveInfo> ris = null;
+ try {
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId);
+ } catch (RemoteException e) {
+ }
+ ArraySet<String> pkgNames = new ArraySet<String>();
+ if (ris != null) {
+ for (ResolveInfo ri : ris) {
+ pkgNames.add(ri.activityInfo.packageName);
+ }
+ }
+ return pkgNames;
+ }
+
+ private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
+ long dexOptLRUThresholdInMills) {
+ // Filter out packages that aren't recently used.
+ int total = pkgs.size();
+ int skipped = 0;
+ long now = System.currentTimeMillis();
+ for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
+ PackageParser.Package pkg = i.next();
+ long then = pkg.mLastPackageUsageTimeInMills;
+ if (then + dexOptLRUThresholdInMills < now) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
+ ((then == 0) ? "never" : new Date(then)));
+ }
+ i.remove();
+ skipped++;
+ }
+ }
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipped dexopt " + skipped + " of " + total);
+ }
+ }
+
+ // Sort apps by importance for dexopt ordering. Important apps are given
+ // more priority in case the device runs out of space.
+ public static List<PackageParser.Package> getPackagesForDexopt(
+ Collection<PackageParser.Package> packages,
+ PackageManagerService packageManagerService) {
+ ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
+ LinkedList<PackageParser.Package> result = new LinkedList<>();
+
+ // Give priority to core apps.
+ for (PackageParser.Package pkg : remainingPkgs) {
+ if (pkg.coreApp) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
+ }
+ result.add(pkg);
+ }
+ }
+ remainingPkgs.removeAll(result);
+
+ // Give priority to system apps that listen for pre boot complete.
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
+ for (PackageParser.Package pkg : remainingPkgs) {
+ if (pkgNames.contains(pkg.packageName)) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
+ pkg.packageName);
+ }
+ result.add(pkg);
+ }
+ }
+ remainingPkgs.removeAll(result);
+
+ // Filter out packages that aren't recently used, add all remaining apps.
+ // TODO: add a property to control this?
+ if (packageManagerService.isHistoricalPackageUsageAvailable()) {
+ filterRecentlyUsedApps(remainingPkgs, SEVEN_DAYS_IN_MILLISECONDS);
+ }
+ result.addAll(remainingPkgs);
+
+ // Now go ahead and also add the libraries required for these packages.
+ // TODO: Think about interleaving things.
+ Set<PackageParser.Package> dependencies = new HashSet<>();
+ for (PackageParser.Package p : result) {
+ dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
+ }
+ if (!dependencies.isEmpty()) {
+ // We might have packages already in `result` that are dependencies
+ // of other packages. Make sure we don't add those to the list twice.
+ dependencies.removeAll(result);
+ }
+ result.addAll(dependencies);
+
+ if (DEBUG_DEXOPT) {
+ StringBuilder sb = new StringBuilder();
+ for (PackageParser.Package pkg : result) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(pkg.packageName);
+ }
+ Log.i(TAG, "Packages to be dexopted: " + sb.toString());
+ }
+
+ return result;
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c1a5c5a24978..187237168109 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -540,8 +540,6 @@ final class Settings {
// is okay to muck with.
PackageSetting newp = new PackageSetting(p);
replacePackageLPw(name, newp);
- } else {
- mPackages.remove(name);
}
return true;
}
@@ -576,9 +574,10 @@ final class Settings {
}
PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
- String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags,
- String parentPackageName, List<String> childPackageNames) {
+ String legacyNativeLibraryPathString, String primaryCpuAbiString,
+ String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, int
+ pkgFlags, int pkgPrivateFlags, String parentPackageName,
+ List<String> childPackageNames) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.appId == uid) {
@@ -679,6 +678,9 @@ final class Settings {
if (p != null) {
p.primaryCpuAbiString = primaryCpuAbiString;
p.secondaryCpuAbiString = secondaryCpuAbiString;
+ if (childPackageNames != null) {
+ p.childPackageNames = new ArrayList<>(childPackageNames);
+ }
if (!p.codePath.equals(codePath)) {
// Check to see if its a disabled system app
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3cc7b10c6f24..76d6b2862534 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1789,6 +1789,10 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
+ return createUserInternalUnchecked(name, flags, parentId);
+ }
+
+ private UserInfo createUserInternalUnchecked(String name, int flags, int parentId) {
if (ActivityManager.isLowRamDeviceStatic()) {
return null;
}
@@ -1930,13 +1934,18 @@ public class UserManagerService extends IUserManager.Stub {
if (user == null) {
return null;
}
- setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
- // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
- // the putIntForUser() will fail.
- android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
- android.provider.Settings.Secure.LOCATION_MODE,
- android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
- setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
+ // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
+ // the putIntForUser() will fail.
+ android.provider.Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ android.provider.Settings.Secure.LOCATION_MODE,
+ android.provider.Settings.Secure.LOCATION_MODE_OFF, user.id);
+ setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user.id);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
return user;
}
@@ -2975,6 +2984,17 @@ public class UserManagerService extends IUserManager.Stub {
am.switchUser(UserHandle.USER_SYSTEM);
}
}
+
+ @Override
+ public UserInfo createUserEvenWhenDisallowed(String name, int flags) {
+ UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL);
+ // Keep this in sync with UserManager.createUser
+ if (user != null && !user.isAdmin()) {
+ setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
+ setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, true, user.id);
+ }
+ return user;
+ }
}
/* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f57f75f04ba6..4b355de62e87 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -155,7 +155,7 @@ public class UserRestrictionsUtils {
*/
public static boolean isValidRestriction(@NonNull String restriction) {
if (!USER_RESTRICTIONS.contains(restriction)) {
- Slog.wtf(TAG, "Unknown restriction: " + restriction);
+ Slog.e(TAG, "Unknown restriction: " + restriction);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c046ba610b3a..3a70e675acdb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,8 @@ package com.android.server.policy;
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.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -32,6 +34,7 @@ import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
@@ -135,6 +138,7 @@ import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.File;
import java.io.FileReader;
@@ -280,6 +284,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
+ StatusBarManagerInternal mStatusBarManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -488,6 +493,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mResettingSystemUiFlags = 0;
// Bits that we are currently always keeping cleared.
int mForceClearedSystemUiFlags = 0;
+ int mLastFullscreenStackSysUiFlags;
+ int mLastDockedStackSysUiFlags;
+ final Rect mNonDockedStackBounds = new Rect();
+ final Rect mDockedStackBounds = new Rect();
+ final Rect mLastNonDockedStackBounds = new Rect();
+ final Rect mLastDockedStackBounds = new Rect();
+
// What we last reported to system UI about whether the compatibility
// menu needs to be displayed.
boolean mLastFocusNeedsMenu = false;
@@ -508,6 +520,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mTopFullscreenOpaqueWindowState;
WindowState mTopFullscreenOpaqueOrDimmingWindowState;
+ WindowState mTopDockedOpaqueWindowState;
+ WindowState mTopDockedOpaqueOrDimmingWindowState;
HashSet<IApplicationToken> mAppsToBeHidden = new HashSet<IApplicationToken>();
HashSet<IApplicationToken> mAppsThatDismissKeyguard = new HashSet<IApplicationToken>();
boolean mTopIsFullscreen;
@@ -844,6 +858,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ StatusBarManagerInternal getStatusBarManagerInternal() {
+ synchronized (mServiceAquireLock) {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ return mStatusBarManagerInternal;
+ }
+ }
+
/*
* We always let the sensor be switched on by default except when
* the user has explicitly disabled sensor based rotation or when the
@@ -4373,6 +4397,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
+ mUnrestrictedScreenHeight;
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
+ pf.left = df.left = of.left = mRestrictedScreenLeft;
+ pf.top = df.top = of.top = mRestrictedScreenTop;
+ pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
+ pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop
+ + mRestrictedScreenHeight;
+ if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ cf.left = mDockLeft;
+ cf.top = mDockTop;
+ cf.right = mDockRight;
+ cf.bottom = mDockBottom;
+ } else {
+ cf.left = mContentLeft;
+ cf.top = mContentTop;
+ cf.right = mContentRight;
+ cf.bottom = mContentBottom;
+ }
} else {
pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
@@ -4552,6 +4593,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mTopFullscreenOpaqueOrDimmingWindowState = null;
+ mTopDockedOpaqueWindowState = null;
+ mTopDockedOpaqueOrDimmingWindowState = null;
mAppsToBeHidden.clear();
mAppsThatDismissKeyguard.clear();
mForceStatusBar = false;
@@ -4597,7 +4640,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
&& attrs.type < FIRST_SYSTEM_WINDOW;
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
-
+ final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null &&
win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
@@ -4646,9 +4689,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
mAppsToBeHidden.add(appToken);
}
- if (attrs.x == 0 && attrs.y == 0
- && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
- && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+ if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -4692,11 +4733,37 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWinShowWhenLocked = win;
}
}
- if (mTopFullscreenOpaqueOrDimmingWindowState == null
- && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()
- && win.isDimming()) {
+
+ // Keep track of the window if it's dimming but not necessarily fullscreen.
+ final boolean reallyVisible = win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw();
+ if (mTopFullscreenOpaqueOrDimmingWindowState == null && reallyVisible
+ && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
+
+ // We need to keep track of the top "fullscreen" opaque window for the docked stack
+ // separately, because both the "real fullscreen" opaque window and the one for the docked
+ // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ if (mTopDockedOpaqueWindowState == null && reallyVisible && appWindow && attached == null
+ && isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueWindowState = win;
+ if (mTopDockedOpaqueOrDimmingWindowState == null) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ // Also keep track of any windows that are dimming but not necessarily fullscreen in the
+ // docked stack.
+ if (mTopDockedOpaqueOrDimmingWindowState == null && reallyVisible && win.isDimming()
+ && stackId == DOCKED_STACK_ID) {
+ mTopDockedOpaqueOrDimmingWindowState = win;
+ }
+ }
+
+ private boolean isFullscreen(WindowManager.LayoutParams attrs) {
+ return attrs.x == 0 && attrs.y == 0
+ && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+ && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
}
/** {@inheritDoc} */
@@ -6838,42 +6905,52 @@ public class PhoneWindowManager implements WindowManagerPolicy {
tmpVisibility |= StatusBarManager.DISABLE_RECENT;
}
- tmpVisibility = updateLightStatusBarLw(tmpVisibility);
+ final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+ final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
+ mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
+ mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
+ final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
+ final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
- if (diff == 0 && mLastFocusNeedsMenu == needsMenu
- && mFocusedApp == win.getAppToken()) {
+ if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+ && mFocusedApp == win.getAppToken()
+ && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
+ && mLastDockedStackBounds.equals(mDockedStackBounds)) {
return 0;
}
mLastSystemUiFlags = visibility;
+ mLastFullscreenStackSysUiFlags = fullscreenVisibility;
+ mLastDockedStackSysUiFlags = dockedVisibility;
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = win.getAppToken();
+ final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
+ final Rect dockedStackBounds = new Rect(mDockedStackBounds);
mHandler.post(new Runnable() {
@Override
public void run() {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- statusbar.setSystemUiVisibility(visibility, 0xffffffff, win.toString());
- statusbar.topAppWindowChanged(needsMenu);
- }
- } catch (RemoteException e) {
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
+ dockedVisibility, 0xffffffff, fullscreenStackBounds,
+ dockedStackBounds, win.toString());
+ statusbar.topAppWindowChanged(needsMenu);
}
}
});
return diff;
}
- private int updateLightStatusBarLw(int vis) {
+ private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
- : mTopFullscreenOpaqueOrDimmingWindowState;
+ : opaqueOrDimming;
if (statusColorWin != null) {
- if (statusColorWin == mTopFullscreenOpaqueWindowState) {
+ if (statusColorWin == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 25d646d2bb3b..cbbcdae91488 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.statusbar;
+import android.graphics.Rect;
import android.os.Bundle;
import com.android.server.notification.NotificationDelegate;
@@ -29,4 +30,7 @@ public interface StatusBarManagerInternal {
void showAssistDisclosure();
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
+ void topAppWindowChanged(boolean menuVisible);
+ void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 90340d5589a5..6eab8d4e1f19 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -20,6 +20,7 @@ import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -70,6 +71,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
private Object mLock = new Object();
// encompasses lights-out mode and other flags defined on View
private int mSystemUiVisibility = 0;
+ private int mFullscreenStackSysUiVisibility;
+ private int mDockedStackSysUiVisibility;
+ private final Rect mFullscreenStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
private boolean mMenuVisible = false;
private int mImeWindowVis = 0;
private int mImeBackDisposition;
@@ -186,6 +191,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
}
}
+
+ @Override
+ public void topAppWindowChanged(boolean menuVisible) {
+ StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+ }
+
+ @Override
+ public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+ dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+ }
};
// ================================================================================
@@ -390,8 +408,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
* response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
* to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
*/
- @Override
- public void topAppWindowChanged(final boolean menuVisible) {
+ private void topAppWindowChanged(final boolean menuVisible) {
enforceStatusBar();
if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
@@ -399,15 +416,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
synchronized(mLock) {
mMenuVisible = menuVisible;
mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.topAppWindowChanged(menuVisible);
- } catch (RemoteException ex) {
- }
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.topAppWindowChanged(menuVisible);
+ } catch (RemoteException ex) {
}
}
- });
+ }
+ });
}
}
@@ -443,13 +460,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
@Override
public void setSystemUiVisibility(int vis, int mask, String cause) {
+ setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+ }
+
+ private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
+ Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis, mask);
+ updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+ fullscreenBounds, dockedBounds);
disableLocked(
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
@@ -458,14 +481,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
}
- private void updateUiVisibilityLocked(final int vis, final int mask) {
- if (mSystemUiVisibility != vis) {
+ private void updateUiVisibilityLocked(final int vis,
+ final int fullscreenStackVis, final int dockedStackVis, final int mask,
+ final Rect fullscreenBounds, final Rect dockedBounds) {
+ if (mSystemUiVisibility != vis
+ || mFullscreenStackSysUiVisibility != fullscreenStackVis
+ || mDockedStackSysUiVisibility != dockedStackVis
+ || !mFullscreenStackBounds.equals(fullscreenBounds)
+ || !mDockedStackBounds.equals(dockedBounds)) {
mSystemUiVisibility = vis;
+ mFullscreenStackSysUiVisibility = fullscreenStackVis;
+ mDockedStackSysUiVisibility = dockedStackVis;
+ mFullscreenStackBounds.set(fullscreenBounds);
+ mDockedStackBounds.set(dockedBounds);
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {
try {
- mBar.setSystemUiVisibility(vis, mask);
+ mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
+ mask, fullscreenBounds, dockedBounds);
} catch (RemoteException ex) {
}
}
@@ -617,7 +651,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
- List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
+ List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
+ Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -636,7 +671,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
switches[4] = mImeBackDisposition;
switches[5] = mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
+ switches[7] = mFullscreenStackSysUiVisibility;
+ switches[8] = mDockedStackSysUiVisibility;
binders.add(mImeToken);
+ fullscreenStackBounds.set(mFullscreenStackBounds);
+ dockedStackBounds.set(mDockedStackBounds);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index c3a6f5d2f637..f3b120f944fd 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -29,8 +29,9 @@ import android.content.pm.Signature;
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Secure;
+import android.provider.Settings.Global;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
@@ -90,7 +91,7 @@ public class WebViewUpdateService extends SystemService {
// change provider when the new version of the package is being installed).
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
&& intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
- synchronized(this) {
+ synchronized(WebViewUpdateService.this) {
if (mCurrentWebViewPackage == null) return;
String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
@@ -141,7 +142,7 @@ public class WebViewUpdateService extends SystemService {
// only kills dependents of packages that are being removed.
try {
ActivityManagerNative.getDefault().killPackageDependents(
- oldProviderName, getContext().getUserId());
+ oldProviderName, UserHandle.USER_ALL);
} catch (RemoteException e) {
}
}
@@ -209,7 +210,7 @@ public class WebViewUpdateService extends SystemService {
try {
if (oldPackage != null) {
ActivityManagerNative.getDefault().killPackageDependents(
- oldPackage.packageName, getContext().getUserId());
+ oldPackage.packageName, UserHandle.USER_ALL);
}
} catch (RemoteException e) {
}
@@ -267,13 +268,13 @@ public class WebViewUpdateService extends SystemService {
}
private static String getUserChosenWebViewProvider() {
- return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER);
+ return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER);
}
private void updateUserSetting(String newProviderName) {
- Settings.Secure.putString(getContext().getContentResolver(),
- Settings.Secure.WEBVIEW_PROVIDER,
+ Settings.Global.putString(getContext().getContentResolver(),
+ Settings.Global.WEBVIEW_PROVIDER,
newProviderName == null ? "" : newProviderName);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index d1c088175a17..9a3aaa5066ef 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -24,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -33,6 +34,10 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.IUserManager;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -73,6 +78,8 @@ class DragState {
IBinder mLocalWin;
int mPid;
int mUid;
+ int mSourceUserId;
+ boolean mCrossProfileCopyAllowed;
ClipData mData;
ClipDescription mDataDescription;
int mTouchSource;
@@ -221,6 +228,18 @@ class DragState {
mNotifiedWindows.clear();
mDragInProgress = true;
+ mSourceUserId = UserHandle.getUserId(mUid);
+
+ final IUserManager userManager =
+ (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
+ try {
+ mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
+ mCrossProfileCopyAllowed = false;
+ }
+
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
@@ -234,7 +253,7 @@ class DragState {
}
}
- /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+ /* helper - send a ACTION_DRAG_STARTED event, if the
* designated window is potentially a drop recipient. There are race situations
* around DRAG_ENDED broadcast, so we make sure that once we've declared that
* the drag has ended, we never send out another DRAG_STARTED for this drag action.
@@ -244,19 +263,7 @@ class DragState {
*/
private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
ClipDescription desc) {
- // Don't actually send the event if the drag is supposed to be pinned
- // to the originating window but 'newWin' is not that window.
- if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
- final IBinder winBinder = newWin.mClient.asBinder();
- if (winBinder != mLocalWin) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "Not dispatching local DRAG_STARTED to " + newWin);
- }
- return;
- }
- }
-
- if (mDragInProgress && newWin.isPotentialDragTarget()) {
+ if (mDragInProgress && isValidDropTarget(newWin)) {
DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
touchX, touchY, null, desc, null, null, false);
try {
@@ -274,17 +281,33 @@ class DragState {
}
}
- /* helper - construct and send a DRAG_STARTED event only if the window has not
+ private boolean isValidDropTarget(WindowState targetWin) {
+ if (targetWin == null) {
+ return false;
+ }
+ if (!targetWin.isPotentialDragTarget()) {
+ return false;
+ }
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
+ // Drag is limited to the current window.
+ if (mLocalWin != targetWin.mClient.asBinder()) {
+ return false;
+ }
+ }
+
+ return mCrossProfileCopyAllowed ||
+ mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
+ }
+
+ /* helper - send a ACTION_DRAG_STARTED event only if the window has not
* previously been notified, i.e. it became visible after the drag operation
* was begun. This is a rare case.
*/
void sendDragStartedIfNeededLw(WindowState newWin) {
if (mDragInProgress) {
// If we have sent the drag-started, we needn't do so again
- for (WindowState ws : mNotifiedWindows) {
- if (ws == newWin) {
- return;
- }
+ if (isWindowNotified(newWin)) {
+ return;
}
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
@@ -293,6 +316,15 @@ class DragState {
}
}
+ private boolean isWindowNotified(WindowState newWin) {
+ for (WindowState ws : mNotifiedWindows) {
+ if (ws == newWin) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void broadcastDragEndedLw() {
final int myPid = Process.myPid();
@@ -346,7 +378,9 @@ class DragState {
private void cleanUpDragLw() {
broadcastDragEndedLw();
- restorePointerIconLw();
+ if (isFromSource(InputDevice.SOURCE_MOUSE)) {
+ mService.restorePointerIconLocked(mDisplay, mCurrentX, mCurrentY);
+ }
// stop intercepting input
unregister();
@@ -384,19 +418,18 @@ class DragState {
void notifyLocationLw(float x, float y) {
// Tell the affected window
- WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ WindowState touchedWin = mService.getTouchableWinAtPointLocked(mDisplay, x, y);
if (touchedWin == null) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "No touched win at x=" + x + " y=" + y);
return;
}
- if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
- final IBinder touchedBinder = touchedWin.mClient.asBinder();
- if (touchedBinder != mLocalWin) {
- // This drag is pinned only to the originating window, but the drag
- // point is outside that window. Pretend it's over empty space.
- touchedWin = null;
- }
+
+ if (!isWindowNotified(touchedWin)) {
+ // The drag point is over a window which was not notified about a drag start.
+ // Pretend it's over empty space.
+ touchedWin = null;
}
+
try {
final int myPid = Process.myPid();
@@ -430,10 +463,6 @@ class DragState {
mTargetWindow = touchedWin;
}
- WindowState getDropTargetWinLw(float x, float y) {
- return getTouchedWinAtPointLw(x, y);
- }
-
// Tell the drop target about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
@@ -445,7 +474,7 @@ class DragState {
mCurrentX = x;
mCurrentY = y;
- if (touchedWin == null) {
+ if (!isWindowNotified(touchedWin)) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
mDragResult = false;
@@ -455,6 +484,9 @@ class DragState {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "sending DROP to " + touchedWin);
}
+ if (mSourceUserId != UserHandle.getUserId(touchedWin.getOwningUid())){
+ mData.fixUris(mSourceUserId);
+ }
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
@@ -478,77 +510,17 @@ class DragState {
return false;
}
- // Find the visible, touch-deliverable window under the given point
- private WindowState getTouchedWinAtPointLw(float xf, float yf) {
- WindowState touchedWin = null;
- final int x = (int) xf;
- final int y = (int) yf;
-
- final WindowList windows = mService.getWindowListLocked(mDisplay);
- if (windows == null) {
- return null;
- }
- final int N = windows.size();
- for (int i = N - 1; i >= 0; i--) {
- WindowState child = windows.get(i);
- final int flags = child.mAttrs.flags;
- if (!child.isVisibleLw()) {
- // not visible == don't tell about drags
- continue;
- }
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- // not touchable == don't tell about drags
- continue;
- }
-
- child.getVisibleBounds(mTmpRect);
- if (!mTmpRect.contains(x, y)) {
- // outside of this window's activity stack == don't tell about drags
- continue;
- }
-
- child.getTouchableRegion(mTmpRegion);
-
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (mTmpRegion.contains(x, y) || touchFlags == 0) {
- // Found it
- touchedWin = child;
- break;
- }
- }
-
- return touchedWin;
- }
-
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
ClipDescription description, ClipData data,
IDropPermissions dropPermissions,
boolean result) {
- final float winX = translateToWindowX(win, x);
- final float winY = translateToWindowY(win, y);
+ final float winX = win.translateToWindowX(x);
+ final float winY = win.translateToWindowY(y);
return DragEvent.obtain(action, winX, winY, localState, description, data,
dropPermissions, result);
}
- private static float translateToWindowX(WindowState win, float x) {
- float winX = x - win.mFrame.left;
- if (win.mEnforceSizeCompat) {
- winX *= win.mGlobalScale;
- }
- return winX;
- }
-
- private static float translateToWindowY(WindowState win, float y) {
- float winY = y - win.mFrame.top;
- if (win.mEnforceSizeCompat) {
- winY *= win.mGlobalScale;
- }
- return winY;
- }
-
boolean stepAnimationLocked(long currentTimeMs) {
if (mAnimation == null) {
return false;
@@ -604,21 +576,4 @@ class DragState {
InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRAB);
}
}
-
- private void restorePointerIconLw() {
- if (isFromSource(InputDevice.SOURCE_MOUSE)) {
- WindowState touchWin = getTouchedWinAtPointLw(mCurrentX, mCurrentY);
- if (touchWin != null) {
- try {
- touchWin.mClient.updatePointerIcon(
- translateToWindowX(touchWin, mCurrentX),
- translateToWindowY(touchWin, mCurrentY));
- return;
- } catch (RemoteException e) {
- Slog.w(TAG_WM, "unable to restore pointer icon");
- }
- }
- InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_DEFAULT);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a8d974f729a6..25de75a55926 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -528,6 +528,16 @@ final class Session extends IWindowSession.Stub
}
}
+ @Override
+ public void updatePointerIcon(IWindow window) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.updatePointerIcon(window);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0979cd32a1e2..e229c5e3fc35 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -37,8 +37,8 @@ public class WindowManagerDebugConfig {
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
- static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
- static final boolean DEBUG_ANIM = false;
+ static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || true;
+ static final boolean DEBUG_ANIM = true;
static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
@@ -50,7 +50,7 @@ public class WindowManagerDebugConfig {
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
- static final boolean DEBUG_APP_TRANSITIONS = false;
+ static final boolean DEBUG_APP_TRANSITIONS = true;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d1ffaa07ed13..c8f5dda1fb3a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -41,6 +41,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -81,6 +82,7 @@ import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.PointerIcon;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.IDockedStackListener;
@@ -466,6 +468,7 @@ public class WindowManagerService extends IWindowManager.Stub
final float[] mTmpFloats = new float[9];
final Rect mTmpRect = new Rect();
final Rect mTmpRect2 = new Rect();
+ final Region mTmpRegion = new Region();
boolean mDisplayReady;
boolean mSafeMode;
@@ -758,7 +761,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private boolean completeDropLw(float x, float y) {
- WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+ WindowState dropTargetWin = getTouchableWinAtPointLocked(mDragState.mDisplay, x, y);
DropPermissionsHandler dropPermissions = null;
if (dropTargetWin != null &&
@@ -769,7 +772,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDragState.mUid,
dropTargetWin.getOwningPackage(),
mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
- UserHandle.getUserId(mDragState.mUid),
+ mDragState.mSourceUserId,
UserHandle.getUserId(dropTargetWin.getOwningUid()));
}
@@ -1192,7 +1195,7 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
}
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+ windows.size());
windows.add(i + 1, win);
@@ -1224,7 +1227,7 @@ public class WindowManagerService extends IWindowManager.Stub
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+ windows.size());
windows.add(newIdx + 1, win);
@@ -1262,7 +1265,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
i++;
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"Free window: Adding window " + win + " at " + i + " of " + windows.size());
windows.add(i, win);
mWindowsChanged = true;
@@ -1333,7 +1336,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) {
- if (DEBUG_FOCUS_LIGHT) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
+ if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindowToListInOrderLocked: win=" + win +
" Callers=" + Debug.getCallers(4));
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
@@ -4236,7 +4239,6 @@ public class WindowManagerService extends IWindowManager.Stub
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
- wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
@@ -4246,6 +4248,8 @@ public class WindowManagerService extends IWindowManager.Stub
// if made visible again.
wtoken.appDied = false;
wtoken.removeAllWindows();
+ } else if (visible) {
+ wtoken.mAppStopped = false;
}
// If we are preparing an app transition, then delay changing
@@ -4863,6 +4867,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void getStackBounds(int stackId, Rect bounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
@@ -10168,6 +10173,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (displayId == Display.DEFAULT_DISPLAY) {
displayContent.mTapDetector = new TaskTapPointerEventListener(this, displayContent);
registerPointerEventListener(displayContent.mTapDetector);
+ registerPointerEventListener(mMousePositionTracker);
}
return displayContent;
@@ -10260,6 +10266,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.close();
if (displayId == Display.DEFAULT_DISPLAY) {
unregisterPointerEventListener(displayContent.mTapDetector);
+ unregisterPointerEventListener(mMousePositionTracker);
}
}
mAnimator.removeDisplayLocked(displayId);
@@ -10459,6 +10466,128 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /**
+ * Find the visible, touch-deliverable window under the given point
+ */
+ WindowState getTouchableWinAtPointLocked(Display display, float xf, float yf) {
+ WindowState touchedWin = null;
+ final int x = (int) xf;
+ final int y = (int) yf;
+
+ final WindowList windows = getWindowListLocked(display);
+ if (windows == null) {
+ return null;
+ }
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ WindowState child = windows.get(i);
+ final int flags = child.mAttrs.flags;
+ if (!child.isVisibleLw()) {
+ continue;
+ }
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ continue;
+ }
+
+ child.getTouchableRegion(mTmpRegion);
+
+ final int touchFlags = flags &
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ if (mTmpRegion.contains(x, y) || touchFlags == 0) {
+ touchedWin = child;
+ break;
+ }
+ }
+
+ return touchedWin;
+ }
+
+ private MousePositionTracker mMousePositionTracker = new MousePositionTracker();
+
+ private static class MousePositionTracker implements PointerEventListener {
+ private boolean mLatestEventWasMouse;
+ private float mLatestMouseX;
+ private float mLatestMouseY;
+
+ void updatePosition(float x, float y) {
+ synchronized (this) {
+ mLatestEventWasMouse = true;
+ mLatestMouseX = x;
+ mLatestMouseY = y;
+ }
+ }
+
+ @Override
+ public void onPointerEvent(MotionEvent motionEvent) {
+ if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ updatePosition(motionEvent.getRawX(), motionEvent.getRawY());
+ } else {
+ synchronized (this) {
+ mLatestEventWasMouse = false;
+ }
+ }
+ }
+ };
+
+ void updatePointerIcon(IWindow client) {
+ float mouseX, mouseY;
+
+ synchronized(mMousePositionTracker) {
+ if (!mMousePositionTracker.mLatestEventWasMouse) {
+ return;
+ }
+ mouseX = mMousePositionTracker.mLatestMouseX;
+ mouseY = mMousePositionTracker.mLatestMouseY;
+ }
+
+ synchronized (mWindowMap) {
+ if (mDragState != null) {
+ // Drag cursor overrides the app cursor.
+ return;
+ }
+ WindowState callingWin = windowForClientLocked(null, client, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ final DisplayContent displayContent = callingWin.getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+ Display display = displayContent.getDisplay();
+ WindowState windowUnderPointer = getTouchableWinAtPointLocked(display, mouseX, mouseY);
+ if (windowUnderPointer != callingWin) {
+ return;
+ }
+ try {
+ windowUnderPointer.mClient.updatePointerIcon(
+ windowUnderPointer.translateToWindowX(mouseX),
+ windowUnderPointer.translateToWindowY(mouseY));
+ } catch (RemoteException e) {
+ Slog.w(TAG_WM, "unable to update pointer icon");
+ }
+ }
+ }
+
+ void restorePointerIconLocked(Display display, float latestX, float latestY) {
+ // Mouse position tracker has not been getting updates while dragging, update it now.
+ mMousePositionTracker.updatePosition(latestX, latestY);
+
+ WindowState windowUnderPointer = getTouchableWinAtPointLocked(display, latestX, latestY);
+ if (windowUnderPointer != null) {
+ try {
+ windowUnderPointer.mClient.updatePointerIcon(
+ windowUnderPointer.translateToWindowX(latestX),
+ windowUnderPointer.translateToWindowY(latestY));
+ } catch (RemoteException e) {
+ Slog.w(TAG_WM, "unable to restore pointer icon");
+ }
+ } else {
+ InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_DEFAULT);
+ }
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2096ea065d0d..37c8a7e4f032 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2088,7 +2088,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mTmpRect;
}
- private int getStackId() {
+ @Override
+ public int getStackId() {
final TaskStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
@@ -2482,4 +2483,20 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mReplacingWindow = null;
mAnimateReplacingWindow = false;
}
+
+ float translateToWindowX(float x) {
+ float winX = x - mFrame.left;
+ if (mEnforceSizeCompat) {
+ winX *= mGlobalScale;
+ }
+ return winX;
+ }
+
+ float translateToWindowY(float y) {
+ float winY = y - mFrame.top;
+ if (mEnforceSizeCompat) {
+ winY *= mGlobalScale;
+ }
+ return winY;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 02012961f5be..f8f8363c6412 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1347,7 +1347,7 @@ class WindowStateAnimator {
}
} else {
if (DEBUG_ANIM && isAnimating()) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+ //Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
displayed = true;
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index b72cf4dc94d0..656c2141ff2e 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -330,7 +330,7 @@ static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr,
jsize len = env->GetArrayLength(body);
message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH);
ScopedByteArrayRO bodyPtr(env, body);
- std::memcpy(message.body, bodyPtr.get(), len);
+ std::memcpy(message.body, bodyPtr.get(), message.length);
HdmiCecController* controller =
reinterpret_cast<HdmiCecController*>(controllerPtr);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e75775fa9237..cdd551936511 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,13 +68,14 @@ static const GpsMeasurementInterface* sGpsMeasurementInterface = NULL;
static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
-#define MAX_SATELLITE_COUNT 512
-#define MAX_GPS_SATELLITE_COUNT 512
+#define GPS_MAX_SATELLITE_COUNT 32
+#define GNSS_MAX_SATELLITE_COUNT 64
-#define PRN_SHIFT_WIDTH 3
+#define SVID_SHIFT_WIDTH 7
+#define CONSTELLATION_TYPE_SHIFT_WIDTH 3
// temporary storage for GPS callbacks
-static GnssSvInfo sGnssSvList[MAX_SATELLITE_COUNT];
+static GnssSvInfo sGnssSvList[GNSS_MAX_SATELLITE_COUNT];
static size_t sGnssSvListSize;
static const char* sNmeaString;
static int sNmeaStringLength;
@@ -113,56 +114,75 @@ static void sv_status_callback(GpsSvStatus* sv_status)
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
size_t status_size = sv_status->size;
- // Some drive doesn't set the size field correctly. Assume GpsSvStatus_v1 if
- // it doesn't provide a valid size.
+ // Some drives doesn't set the size field correctly. Assume GpsSvStatus_v1
+ // if it doesn't provide a valid size.
if (status_size == 0) {
- status_size = sizeof(GpsSvStatus_v1);
- }
- if (status_size == sizeof(GpsSvStatus)) {
- sGnssSvListSize = sv_status->gnss_sv_list_size;
- // Cramp the list size
- if (sGnssSvListSize > MAX_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_SATELLITE_COUNT;
- }
- // Copy GNSS SV info into sGnssSvList, if any.
- if (sGnssSvListSize > 0 && sv_status->gnss_sv_list) {
- memcpy(sGnssSvList, sv_status->gnss_sv_list, sizeof(GnssSvInfo) * sGnssSvListSize);
- }
- } else if (status_size == sizeof(GpsSvStatus_v1)) {
- sGnssSvListSize = sv_status->num_svs;
- // Cramp the list size
- if (sGnssSvListSize > MAX_GPS_SATELLITE_COUNT) {
- sGnssSvListSize = MAX_GPS_SATELLITE_COUNT;
- }
- uint32_t ephemeris_mask = sv_status->ephemeris_mask;
- uint32_t almanac_mask = sv_status->almanac_mask;
- uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
- for (size_t i = 0; i < sGnssSvListSize; i++) {
- GnssSvInfo& info = sGnssSvList[i];
+ ALOGW("Invalid size of GpsSvStatus found: %zd.", status_size);
+ }
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size. Legacy GpsSvStatus has only 32 elements in sv_list.
+ if (sGnssSvListSize > GPS_MAX_SATELLITE_COUNT) {
+ ALOGW("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GPS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GPS_MAX_SATELLITE_COUNT;
+ }
+ uint32_t ephemeris_mask = sv_status->ephemeris_mask;
+ uint32_t almanac_mask = sv_status->almanac_mask;
+ uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
+ for (size_t i = 0; i < sGnssSvListSize; i++) {
+ GnssSvInfo& info = sGnssSvList[i];
+ info.svid = sv_status->sv_list[i].prn;
+ // TODO: implement the correct logic to derive the constellation type
+ // based on PRN ranges.
+ if (info.svid >=1 && info.svid <= 32) {
info.constellation = GNSS_CONSTELLATION_GPS;
- info.prn = sv_status->sv_list[i].prn;
- info.snr = sv_status->sv_list[i].snr;
- info.elevation = sv_status->sv_list[i].elevation;
- info.azimuth = sv_status->sv_list[i].azimuth;
- info.flags = GNSS_SV_FLAGS_NONE;
- if (info.prn > 0 && info.prn <= 32) {
- int32_t this_prn_mask = (1 << (info.prn - 1));
- if ((ephemeris_mask & this_prn_mask) != 0) {
+ } else {
+ info.constellation = GNSS_CONSTELLATION_UNKNOWN;
+ }
+ info.snr = sv_status->sv_list[i].snr;
+ info.elevation = sv_status->sv_list[i].elevation;
+ info.azimuth = sv_status->sv_list[i].azimuth;
+ info.flags = GNSS_SV_FLAGS_NONE;
+ if (info.svid > 0 && info.svid <= 32) {
+ int32_t this_svid_mask = (1 << (info.svid - 1));
+ if ((ephemeris_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA;
- }
- if ((almanac_mask & this_prn_mask) != 0) {
+ }
+ if ((almanac_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA;
- }
- if ((used_in_fix_mask & this_prn_mask) != 0) {
+ }
+ if ((used_in_fix_mask & this_svid_mask) != 0) {
info.flags |= GNSS_SV_FLAGS_USED_IN_FIX;
- }
}
}
- } else {
- sGnssSvListSize = 0;
- ALOGE("Invalid size of GpsSvStatus found: %zd.", status_size);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void gnss_sv_status_callback(GnssSvStatus* sv_status) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ size_t status_size = sv_status->size;
+ // Check the size, and reject the object that has invalid size.
+ if (status_size != sizeof(GnssSvStatus)) {
+ ALOGE("Invalid size of GnssSvStatus found: %zd.", status_size);
return;
}
+ sGnssSvListSize = sv_status->num_svs;
+ // Clamp the list size
+ if (sGnssSvListSize > GNSS_MAX_SATELLITE_COUNT) {
+ ALOGD("Too many satellites %zd. Clamps to %d.",
+ sGnssSvListSize,
+ GNSS_MAX_SATELLITE_COUNT);
+ sGnssSvListSize = GNSS_MAX_SATELLITE_COUNT;
+ }
+ // Copy GNSS SV info into sGnssSvList, if any.
+ if (sGnssSvListSize > 0) {
+ memcpy(sGnssSvList,
+ sv_status->gnss_sv_list,
+ sizeof(GnssSvInfo) * sGnssSvListSize);
+ }
env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
@@ -228,6 +248,7 @@ GpsCallbacks sGpsCallbacks = {
create_thread_callback,
request_utc_time_callback,
set_system_info_callback,
+ gnss_sv_status_callback,
};
static void xtra_download_request_callback()
@@ -677,31 +698,30 @@ static void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /*
}
static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
- jintArray prnWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
- jfloatArray azumArray, jintArray constellationTypeArray)
+ jintArray svidWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
+ jfloatArray azumArray)
{
// this should only be called from within a call to reportSvStatus
- jint* prnWithFlags = env->GetIntArrayElements(prnWithFlagArray, 0);
+ jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
- jint* constellationTypes = env->GetIntArrayElements(constellationTypeArray, 0);
// GNSS SV info.
for (size_t i = 0; i < sGnssSvListSize; ++i) {
const GnssSvInfo& info = sGnssSvList[i];
- constellationTypes[i] = info.constellation;
- prnWithFlags[i] = (info.prn << PRN_SHIFT_WIDTH) | info.flags;
+ svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+ (info.constellation << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+ info.flags;
snrs[i] = info.snr;
elev[i] = info.elevation;
azim[i] = info.azimuth;
}
- env->ReleaseIntArrayElements(prnWithFlagArray, prnWithFlags, 0);
+ env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
env->ReleaseFloatArrayElements(snrArray, snrs, 0);
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azumArray, azim, 0);
- env->ReleaseIntArrayElements(constellationTypeArray, constellationTypes, 0);
return (jint) sGnssSvListSize;
}
@@ -968,370 +988,367 @@ static jboolean android_location_GnssLocationProvider_resume_geofence(JNIEnv* /*
return JNI_FALSE;
}
-static jobject translate_gps_clock(JNIEnv* env, void* data, size_t size) {
- const char* doubleSignature = "(D)V";
- const char* longSignature = "(J)V";
-
- GpsClock* clock = reinterpret_cast<GpsClock*>(data);
-
- jclass gpsClockClass = env->FindClass("android/location/GnssClock");
- jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
-
- jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor);
- GpsClockFlags flags = clock->flags;
-
- if (flags & GPS_CLOCK_HAS_LEAP_SECOND) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setLeapSecond", "(S)V");
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->leap_second);
- }
+template<class T>
+class JavaMethodHelper {
+ public:
+ // Helper function to call setter on a Java object.
+ static void callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value);
+
+ private:
+ static const char *const signature_;
+};
- jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
- env->CallVoidMethod(gpsClockObject, typeSetterMethod, clock->type);
+template<class T>
+void JavaMethodHelper<T>::callJavaMethod(
+ JNIEnv* env,
+ jclass clazz,
+ jobject object,
+ const char* method_name,
+ T value) {
+ jmethodID method = env->GetMethodID(clazz, method_name, signature_);
+ env->CallVoidMethod(object, method, value);
+}
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_ns);
+class JavaObject {
+ public:
+ JavaObject(JNIEnv* env, const char* class_name);
+ virtual ~JavaObject();
+
+ template<class T>
+ void callSetter(const char* method_name, T value);
+ template<class T>
+ void callSetter(const char* method_name, T* value, size_t size);
+ jobject get();
+
+ private:
+ JNIEnv* env_;
+ jclass clazz_;
+ jobject object_;
+};
- if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
- }
+JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) {
+ clazz_ = env_->FindClass(class_name);
+ jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V");
+ object_ = env_->NewObject(clazz_, ctor);
+}
- if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
- }
+JavaObject::~JavaObject() {
+ env_->DeleteLocalRef(clazz_);
+}
- if (flags & GPS_CLOCK_HAS_BIAS) {
- jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_ns);
- }
+template<class T>
+void JavaObject::callSetter(const char* method_name, T value) {
+ JavaMethodHelper<T>::callJavaMethod(
+ env_, clazz_, object_, method_name, value);
+}
- if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
- }
+template<>
+void JavaObject::callSetter(
+ const char* method_name, uint8_t* value, size_t size) {
+ jbyteArray array = env_->NewByteArray(size);
+ env_->SetByteArrayRegion(array, 0, size, (jbyte*) value);
+ jmethodID method = env_->GetMethodID(
+ clazz_,
+ method_name,
+ "([B)V");
+ env_->CallVoidMethod(object_, method, array);
+}
- if (flags & GPS_CLOCK_HAS_DRIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_nsps);
- }
+jobject JavaObject::get() {
+ return object_;
+}
- if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
- env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
- }
+// Define Java method signatures for all known types.
+
+template<>
+const char *const JavaMethodHelper<uint8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int8_t>::signature_ = "(B)V";
+template<>
+const char *const JavaMethodHelper<int16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<uint16_t>::signature_ = "(S)V";
+template<>
+const char *const JavaMethodHelper<int>::signature_ = "(I)V";
+template<>
+const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
+template<>
+const char *const JavaMethodHelper<float>::signature_ = "(F)V";
+template<>
+const char *const JavaMethodHelper<double>::signature_ = "(D)V";
+template<>
+const char *const JavaMethodHelper<bool>::signature_ = "(Z)V";
+
+#define SET(setter, value) object.callSetter("set" # setter, (value))
+#define SET_IF(flag, setter, value) \
+ if (flags & (flag)) object.callSetter("set" # setter, (value))
+
+static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
+ GpsClockFlags flags = clock->flags;
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
+
+ /*
if (flags & GPS_CLOCK_TYPE_LOCAL_HW_TIME) {
- if (size == sizeof(GpsClock)) {
+ if (size == sizeof(GnssClock)) {
jmethodID setterMethod =
env->GetMethodID(gpsClockClass,
"setTimeOfLastHwClockDiscontinuityInNs",
longSignature);
env->CallVoidMethod(gpsClockObject,
setterMethod,
- clock->time_of_last_hw_clock_discontinuity_ns);
+ reinterpret_cast<GnssClock*>(clock)->time_of_last_hw_clock_discontinuity_ns);
}
}
+ */
- env->DeleteLocalRef(gpsClockClass);
- return gpsClockObject;
+ return object.get();
}
-static jobject translate_gps_measurement(JNIEnv* env, void* data, size_t size) {
- const char* byteSignature = "(B)V";
- const char* shortSignature = "(S)V";
- const char* intSignature = "(I)V";
- const char* longSignature = "(J)V";
- const char* floatSignature = "(F)V";
- const char* doubleSignature = "(D)V";
+static jobject translate_gnss_clock(JNIEnv* env, GnssClock* clock) {
+ JavaObject object(env, "android/location/GnssClock");
+ GpsClockFlags flags = clock->flags;
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
- jmethodID gnssMeasurementCtor = env->GetMethodID(gnssMeasurementClass, "<init>", "()V");
- GpsMeasurement* measurement = reinterpret_cast<GpsMeasurement*>(data);
+ SET_IF(GPS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
+ SET(Type, clock->type);
+ SET(TimeInNs, clock->time_ns);
+ SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
+ TimeUncertaintyInNs,
+ clock->time_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasInNs, clock->full_bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS, BiasInNs, clock->bias_ns);
+ SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
+ BiasUncertaintyInNs,
+ clock->bias_uncertainty_ns);
+ SET_IF(GPS_CLOCK_HAS_DRIFT, DriftInNsPerSec, clock->drift_nsps);
+ SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+ DriftUncertaintyInNsPerSec,
+ clock->drift_uncertainty_nsps);
+
+ SET_IF(GPS_CLOCK_TYPE_LOCAL_HW_TIME,
+ TimeOfLastHwClockDiscontinuityInNs,
+ clock->time_of_last_hw_clock_discontinuity_ns);
+
+ return object.get();
+}
- jobject gnssMeasurementObject = env->NewObject(gnssMeasurementClass, gnssMeasurementCtor);
+static jobject translate_gps_measurement(JNIEnv* env,
+ GpsMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
GpsMeasurementFlags flags = measurement->flags;
- jmethodID prnSetterMethod = env->GetMethodID(gnssMeasurementClass, "setPrn", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, prnSetterMethod, measurement->prn);
+ SET(Svid, static_cast<int16_t>(measurement->prn));
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+
+ return object.get();
+}
- jmethodID timeOffsetSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeOffsetInNs", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- timeOffsetSetterMethod,
- measurement->time_offset_ns);
+static jobject translate_gnss_measurement(JNIEnv* env,
+ GnssMeasurement* measurement) {
+ JavaObject object(env, "android/location/GnssMeasurement");
+ GpsMeasurementFlags flags = measurement->flags;
- jmethodID stateSetterMethod = env->GetMethodID(gnssMeasurementClass, "setState", shortSignature);
- env->CallVoidMethod(gnssMeasurementObject, stateSetterMethod, measurement->state);
+ SET(Svid, measurement->svid);
+ SET(TimeOffsetInNs, measurement->time_offset_ns);
+ SET(State, measurement->state);
+ SET(ReceivedGpsTowInNs, measurement->received_gps_tow_ns);
+ SET(ReceivedGpsTowUncertaintyInNs,
+ measurement->received_gps_tow_uncertainty_ns);
+ SET(Cn0InDbHz, measurement->c_n0_dbhz);
+ SET(PseudorangeRateInMetersPerSec, measurement->pseudorange_rate_mps);
+ SET(PseudorangeRateUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_uncertainty_mps);
+ SET(AccumulatedDeltaRangeState, measurement->accumulated_delta_range_state);
+ SET(AccumulatedDeltaRangeInMeters, measurement->accumulated_delta_range_m);
+ SET(AccumulatedDeltaRangeUncertaintyInMeters,
+ measurement->accumulated_delta_range_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE,
+ PseudorangeInMeters,
+ measurement->pseudorange_m);
+ SET_IF(GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY,
+ PseudorangeUncertaintyInMeters,
+ measurement->pseudorange_uncertainty_m);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE,
+ CodePhaseInChips,
+ measurement->code_phase_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY,
+ CodePhaseUncertaintyInChips,
+ measurement->code_phase_uncertainty_chips);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY,
+ CarrierFrequencyInHz,
+ measurement->carrier_frequency_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_CYCLES,
+ CarrierCycles,
+ measurement->carrier_cycles);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE,
+ CarrierPhase,
+ measurement->carrier_phase);
+ SET_IF(GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
+ CarrierPhaseUncertainty,
+ measurement->carrier_phase_uncertainty);
+ SET(LossOfLock, measurement->loss_of_lock);
+ SET_IF(GPS_MEASUREMENT_HAS_BIT_NUMBER, BitNumber, measurement->bit_number);
+ SET_IF(GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT,
+ TimeFromLastBitInMs,
+ measurement->time_from_last_bit_ms);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT,
+ DopplerShiftInHz,
+ measurement->doppler_shift_hz);
+ SET_IF(GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY,
+ DopplerShiftUncertaintyInHz,
+ measurement->doppler_shift_uncertainty_hz);
+ SET(MultipathIndicator, measurement->multipath_indicator);
+ SET_IF(GPS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION,
+ ElevationInDeg,
+ measurement->elevation_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY,
+ ElevationUncertaintyInDeg,
+ measurement->elevation_uncertainty_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH,
+ AzimuthInDeg,
+ measurement->azimuth_deg);
+ SET_IF(GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY,
+ AzimuthUncertaintyInDeg,
+ measurement->azimuth_uncertainty_deg);
+ SET(UsedInFix,
+ (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+
+ SET(PseudorangeRateCarrierInMetersPerSec,
+ measurement->pseudorange_rate_carrier_mps);
+ SET(PseudorangeRateCarrierUncertaintyInMetersPerSec,
+ measurement->pseudorange_rate_carrier_uncertainty_mps);
+
+ return object.get();
+}
- jmethodID receivedGpsTowSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setReceivedGpsTowInNs", longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowSetterMethod,
- measurement->received_gps_tow_ns);
+static jobjectArray translate_gps_measurements(JNIEnv* env,
+ GpsMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
+ return NULL;
+ }
- jmethodID receivedGpsTowUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setReceivedGpsTowUncertaintyInNs",
- longSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- receivedGpsTowUncertaintySetterMethod,
- measurement->received_gps_tow_uncertainty_ns);
-
- jmethodID cn0SetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCn0InDbHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
-
- jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateSetterMethod,
- measurement->pseudorange_rate_mps);
-
- jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeRateUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- pseudorangeRateUncertaintySetterMethod,
- measurement->pseudorange_rate_uncertainty_mps);
-
- jmethodID accumulatedDeltaRangeStateSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeStateSetterMethod,
- measurement->accumulated_delta_range_state);
-
- jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAccumulatedDeltaRangeInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeSetterMethod,
- measurement->accumulated_delta_range_m);
-
- jmethodID accumulatedDeltaRangeUncertaintySetterMethod = env->GetMethodID(
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
+ jobjectArray gnssMeasurementArray = env->NewObjectArray(
+ count,
gnssMeasurementClass,
- "setAccumulatedDeltaRangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- accumulatedDeltaRangeUncertaintySetterMethod,
- measurement->accumulated_delta_range_uncertainty_m);
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setPseudorangeInMeters", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->pseudorange_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setPseudorangeUncertaintyInMeters",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_uncertainty_m);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCodePhaseInChips", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->code_phase_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCodePhaseUncertaintyInChips",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->code_phase_uncertainty_chips);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_frequency_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_CYCLES) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierCycles", longSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_cycles);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setCarrierPhase", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->carrier_phase);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setCarrierPhaseUncertainty",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->carrier_phase_uncertainty);
- }
-
- jmethodID lossOfLockSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setLossOfLock", byteSignature);
- env->CallVoidMethod(gnssMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
-
- if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setBitNumber", intSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->bit_number);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->time_from_last_bit_ms);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setDopplerShiftInHz", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->doppler_shift_hz);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setDopplerShiftUncertaintyInHz",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->doppler_shift_uncertainty_hz);
- }
-
- jmethodID multipathIndicatorSetterMethod =
- env->GetMethodID(gnssMeasurementClass, "setMultipathIndicator", byteSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- multipathIndicatorSetterMethod,
- measurement->multipath_indicator);
-
- if (flags & GPS_MEASUREMENT_HAS_SNR) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setSnrInDb", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->snr_db);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->elevation_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->elevation_uncertainty_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass, "setAzimuthInDeg", doubleSignature);
- env->CallVoidMethod(gnssMeasurementObject, setterMethod, measurement->azimuth_deg);
- }
-
- if (flags & GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY) {
- jmethodID setterMethod = env->GetMethodID(
- gnssMeasurementClass,
- "setAzimuthUncertaintyInDeg",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->azimuth_uncertainty_deg);
- }
-
- jmethodID usedInFixSetterMethod = env->GetMethodID(gnssMeasurementClass, "setUsedInFix", "(Z)V");
- env->CallVoidMethod(
- gnssMeasurementObject,
- usedInFixSetterMethod,
- (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
-
- if (size == sizeof(GpsMeasurement)) {
- jmethodID setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_mps);
-
- setterMethod =
- env->GetMethodID(gnssMeasurementClass,
- "setPseudorangeRateCarrierUncertaintyInMetersPerSec",
- doubleSignature);
- env->CallVoidMethod(
- gnssMeasurementObject,
- setterMethod,
- measurement->pseudorange_rate_carrier_uncertainty_mps);
+ NULL /* initialElement */);
+
+ for (uint16_t i = 0; i < count; ++i) {
+ jobject gnssMeasurement = translate_gps_measurement(
+ env,
+ &measurements[i]);
+ env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
+ env->DeleteLocalRef(gnssMeasurement);
}
env->DeleteLocalRef(gnssMeasurementClass);
- return gnssMeasurementObject;
+ return gnssMeasurementArray;
}
-/**
- * <T> can only be GpsData or GpsData_v1. Must rewrite this function if more
- * types are introduced in the future releases.
- */
-template<class T>
-static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
- T* gps_data = reinterpret_cast<T*>(data);
- size_t measurementCount = gps_data->measurement_count;
- if (measurementCount == 0) {
+static jobjectArray translate_gnss_measurements(JNIEnv* env,
+ GnssMeasurement* measurements,
+ size_t count) {
+ if (count == 0) {
return NULL;
}
- jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
+ jclass gnssMeasurementClass = env->FindClass(
+ "android/location/GnssMeasurement");
jobjectArray gnssMeasurementArray = env->NewObjectArray(
- measurementCount,
+ count,
gnssMeasurementClass,
NULL /* initialElement */);
- for (uint16_t i = 0; i < measurementCount; ++i) {
- jobject gnssMeasurement = translate_gps_measurement(
+ for (uint16_t i = 0; i < count; ++i) {
+ jobject gnssMeasurement = translate_gnss_measurement(
env,
- &(gps_data->measurements[i]),
- sizeof(gps_data->measurements[0]));
+ &measurements[i]);
env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
env->DeleteLocalRef(gnssMeasurement);
}
@@ -1340,50 +1357,81 @@ static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
return gnssMeasurementArray;
}
+static void set_measurement_data(JNIEnv *env,
+ jobject clock,
+ jobjectArray measurementArray) {
+ jclass gnssMeasurementsEventClass = env->FindClass(
+ "android/location/GnssMeasurementsEvent");
+ jmethodID gnssMeasurementsEventCtor = env->GetMethodID(
+ gnssMeasurementsEventClass,
+ "<init>",
+ "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+
+ jobject gnssMeasurementsEvent = env->NewObject(
+ gnssMeasurementsEventClass,
+ gnssMeasurementsEventCtor,
+ clock,
+ measurementArray);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportMeasurementData,
+ gnssMeasurementsEvent);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(gnssMeasurementsEventClass);
+ env->DeleteLocalRef(gnssMeasurementsEvent);
+}
+
static void measurement_callback(GpsData* data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
if (data == NULL) {
ALOGE("Invalid data provided to gps_measurement_callback");
return;
}
- if (data->size != sizeof(GpsData) && data->size != sizeof(GpsData_v1)) {
- ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size);
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
return;
}
- jobject gpsClock;
+ jobject clock;
jobjectArray measurementArray;
- if (data->size == sizeof(GpsData)) {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock));
- measurementArray = translate_gps_measurements<GpsData>(env, data);
- } else {
- gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock_v1));
- measurementArray = translate_gps_measurements<GpsData_v1>(env, data);
- }
- jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
- jmethodID gnssMeasurementsEventCtor = env->GetMethodID(
- gnssMeasurementsEventClass,
- "<init>",
- "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+ clock = translate_gps_clock(env, &data->clock);
+ measurementArray = translate_gps_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
- jobject gnssMeasurementsEvent = env->NewObject(
- gnssMeasurementsEventClass,
- gnssMeasurementsEventCtor,
- gpsClock,
- measurementArray);
+ env->DeleteLocalRef(clock);
+ env->DeleteLocalRef(measurementArray);
+}
- env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
+static void gnss_measurement_callback(GnssData* data) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (data == NULL) {
+ ALOGE("Invalid data provided to gps_measurement_callback");
+ return;
+ }
+ if (data->size != sizeof(GpsData)) {
+ ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+ "size=%zd",
+ data->size);
+ return;
+ }
- env->DeleteLocalRef(gpsClock);
+ jobject clock;
+ jobjectArray measurementArray;
+ clock = translate_gnss_clock(env, &data->clock);
+ measurementArray = translate_gnss_measurements(
+ env, data->measurements, data->measurement_count);
+ set_measurement_data(env, clock, measurementArray);
+
+ env->DeleteLocalRef(clock);
env->DeleteLocalRef(measurementArray);
- env->DeleteLocalRef(gnssMeasurementsEventClass);
- env->DeleteLocalRef(gnssMeasurementsEvent);
}
GpsMeasurementCallbacks sGpsMeasurementCallbacks = {
sizeof(GpsMeasurementCallbacks),
measurement_callback,
+ gnss_measurement_callback,
};
static jboolean android_location_GnssLocationProvider_is_measurement_supported(
@@ -1431,69 +1479,86 @@ static jobject translate_gps_navigation_message(JNIEnv* env, GpsNavigationMessag
ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
return NULL;
}
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, static_cast<int16_t>(message->prn));
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- jclass navigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
- jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V");
- jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor);
-
- jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type);
-
- jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V");
- env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn);
-
- jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id);
-
- jmethodID setSubmessageIdMethod =
- env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V");
- env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id);
-
- jbyteArray dataArray = env->NewByteArray(dataLength);
- env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data);
- jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V");
- env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray);
+static jobject translate_gnss_navigation_message(
+ JNIEnv* env, GnssNavigationMessage* message) {
+ size_t dataLength = message->data_length;
+ uint8_t* data = message->data;
+ if (dataLength == 0 || data == NULL) {
+ ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data, dataLength);
+ return NULL;
+ }
+ JavaObject object(env, "android/location/GnssNavigationMessage");
+ SET(Type, message->type);
+ SET(Svid, message->svid);
+ SET(MessageId, message->message_id);
+ SET(SubmessageId, message->submessage_id);
+ object.callSetter("setData", data, dataLength);
+ return object.get();
+}
- env->DeleteLocalRef(navigationMessageClass);
- env->DeleteLocalRef(dataArray);
- return navigationMessageObject;
+static void set_navigation_message(jobject navigationMessage) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jclass navigationMessageEventClass =
+ env->FindClass("android/location/GnssNavigationMessageEvent");
+ jmethodID navigationMessageEventCtor = env->GetMethodID(
+ navigationMessageEventClass,
+ "<init>",
+ "(Landroid/location/GnssNavigationMessage;)V");
+ jobject navigationMessageEvent = env->NewObject(
+ navigationMessageEventClass,
+ navigationMessageEventCtor,
+ navigationMessage);
+ env->CallVoidMethod(mCallbacksObj,
+ method_reportNavigationMessages,
+ navigationMessageEvent);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(navigationMessageEventClass);
+ env->DeleteLocalRef(navigationMessageEvent);
}
static void navigation_message_callback(GpsNavigationMessage* message) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
if (message == NULL) {
ALOGE("Invalid Navigation Message provided to callback");
return;
}
-
- if (message->size == sizeof(GpsNavigationMessage)) {
- jobject navigationMessage = translate_gps_navigation_message(env, message);
-
- jclass navigationMessageEventClass =
- env->FindClass("android/location/GnssNavigationMessageEvent");
- jmethodID navigationMessageEventCtor = env->GetMethodID(
- navigationMessageEventClass,
- "<init>",
- "(Landroid/location/GnssNavigationMessage;)V");
- jobject navigationMessageEvent = env->NewObject(
- navigationMessageEventClass,
- navigationMessageEventCtor,
- navigationMessage);
-
- env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
- env->DeleteLocalRef(navigationMessage);
- env->DeleteLocalRef(navigationMessageEventClass);
- env->DeleteLocalRef(navigationMessageEvent);
- } else {
+ if (message->size != sizeof(GpsNavigationMessage)) {
ALOGE("Invalid GpsNavigationMessage size found: %zd", message->size);
+ return;
+ }
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gps_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
+}
+
+static void gnss_navigation_message_callback(GnssNavigationMessage* message) {
+ if (message == NULL) {
+ ALOGE("Invalid Navigation Message provided to callback");
+ return;
+ }
+ if (message->size != sizeof(GnssNavigationMessage)) {
+ ALOGE("Invalid GnssNavigationMessage size found: %zd", message->size);
+ return;
}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject navigationMessage = translate_gnss_navigation_message(env, message);
+ set_navigation_message(navigationMessage);
+ env->DeleteLocalRef(navigationMessage);
}
GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = {
sizeof(GpsNavigationMessageCallbacks),
navigation_message_callback,
+ gnss_navigation_message_callback,
};
static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
@@ -1567,7 +1632,7 @@ static const JNINativeMethod sMethods[] = {
"(I)V",
(void*)android_location_GnssLocationProvider_delete_aiding_data},
{"native_read_sv_status",
- "([I[F[F[F[I)I",
+ "([I[F[F[F)I",
(void*)android_location_GnssLocationProvider_read_sv_status},
{"native_read_nmea", "([BI)I", (void*)android_location_GnssLocationProvider_read_nmea},
{"native_inject_time", "(JJI)V", (void*)android_location_GnssLocationProvider_inject_time},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eacf11f47415..916b66d04a54 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -116,6 +116,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -274,6 +275,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final int PROFILE_KEYGUARD_FEATURES =
PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_AFFECT_PROFILE;
+ private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -281,6 +284,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final UserManagerInternal mUserManagerInternal;
private final LockPatternUtils mLockPatternUtils;
+ /**
+ * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
+ * is requested for user u.
+ */
+ private final Set<Pair<String, Integer>> mPackagesToRemove =
+ new ArraySet<Pair<String, Integer>>();
+
final LocalService mLocalService;
// Stores and loads state on device and profile owners.
@@ -1239,7 +1249,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (packageName == null || packageName.equals(adminPackage)) {
if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
|| mIPackageManager.getReceiverInfo(
- aa.info.getComponent(), 0, userHandle) == null) {
+ aa.info.getComponent(),
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+ userHandle) == null) {
removed = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
@@ -2015,35 +2027,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin != null) {
getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
-
sendAdminCommandLocked(admin,
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (DevicePolicyManagerService.this) {
- int userHandle = admin.getUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- boolean doProxyCleanup = admin.info.usesPolicy(
- DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
- policy.mAdminList.remove(admin);
- policy.mAdminMap.remove(adminReceiver);
- validatePasswordOwnerLocked(policy);
- if (doProxyCleanup) {
- resetGlobalProxyLocked(getUserData(userHandle));
- }
- saveSettingsLocked(userHandle);
- updateMaximumTimeToLockLocked(userHandle);
- policy.mRemovingAdmins.remove(adminReceiver);
- }
- // The removed admin might have disabled camera, so update user
- // restrictions.
- pushUserRestrictions(userHandle);
+ removeAdminArtifacts(adminReceiver, userHandle);
+ removePackageIfRequired(adminReceiver.getPackageName(), userHandle);
}
});
}
}
+
public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
boolean throwForMissiongPermission) {
if (!mHasFeature) {
@@ -3528,11 +3524,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ if (!isCallerWithSystemUid()) {
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
+ }
DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent));
@@ -4492,7 +4491,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);
}
}
@@ -4522,23 +4522,50 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (mInjector.securityLogIsLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1);
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+ /*method strength*/ 1);
+ }
+ }
+
+ @Override
+ public void reportFailedFingerprintAttempt(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ if (mInjector.securityLogIsLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+ /*method strength*/ 0);
+ }
+ }
+
+ @Override
+ public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ if (mInjector.securityLogIsLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
+ /*method strength*/ 0);
}
}
@Override
- public void reportKeyguardDismissed() {
+ public void reportKeyguardDismissed(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
}
}
@Override
- public void reportKeyguardSecured() {
+ public void reportKeyguardSecured(int userHandle) {
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
if (mInjector.securityLogIsLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
}
@@ -6769,6 +6796,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
+ admin + " are not in the same package");
}
+ // Only allow the system user to use this method
+ if (!mInjector.binderGetCallingUserHandle().isSystem()) {
+ throw new SecurityException("createAndManageUser was called from non-system user");
+ }
// Create user.
UserHandle user = null;
synchronized (this) {
@@ -6780,7 +6811,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if ((flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) {
userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
}
- UserInfo userInfo = mUserManager.createUser(name, userInfoFlags);
+ UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
+ userInfoFlags);
if (userInfo != null) {
user = userInfo.getUserHandle();
}
@@ -7110,7 +7142,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- 0, // no flags
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
parentUserId);
if (VERBOSE_LOG) {
@@ -8009,7 +8041,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean isPackageInstalledForUser(String packageName, int userHandle) {
try {
- PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+ PackageInfo pi = mInjector.getIPackageManager().getPackageInfo(packageName, 0,
+ userHandle);
return (pi != null) && (pi.applicationInfo.flags != 0);
} catch (RemoteException re) {
throw new RuntimeException("Package manager has died", re);
@@ -8465,4 +8498,137 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
}
+
+ private void enforceCanManageDeviceAdmin() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+ null);
+ }
+
+ @Override
+ public boolean isUninstallInQueue(final String packageName) {
+ enforceCanManageDeviceAdmin();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ return mPackagesToRemove.contains(packageUserPair);
+ }
+ }
+
+ @Override
+ public void uninstallPackageWithActiveAdmins(final String packageName) {
+ enforceCanManageDeviceAdmin();
+ Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+
+ final ComponentName profileOwner = getProfileOwner(userId);
+ if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
+ throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
+ }
+
+ final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false);
+ if (getDeviceOwnerUserId() == userId && deviceOwner != null
+ && packageName.equals(deviceOwner.getPackageName())) {
+ throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
+ }
+
+ final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ mPackagesToRemove.add(packageUserPair);
+ }
+
+ // All active admins on the user.
+ final List<ComponentName> allActiveAdmins = getActiveAdmins(userId);
+
+ // Active admins in the target package.
+ final List<ComponentName> packageActiveAdmins = new ArrayList<>();
+ if (allActiveAdmins != null) {
+ for (ComponentName activeAdmin : allActiveAdmins) {
+ if (packageName.equals(activeAdmin.getPackageName())) {
+ packageActiveAdmins.add(activeAdmin);
+ removeActiveAdmin(activeAdmin, userId);
+ }
+ }
+ }
+ if (packageActiveAdmins.size() == 0) {
+ startUninstallIntent(packageName, userId);
+ } else {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (ComponentName activeAdmin : packageActiveAdmins) {
+ removeAdminArtifacts(activeAdmin, userId);
+ }
+ startUninstallIntent(packageName, userId);
+ }
+ }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+ }
+ }
+
+ private void removePackageIfRequired(final String packageName, final int userId) {
+ if (!packageHasActiveAdmins(packageName, userId)) {
+ // Will not do anything if uninstall was not requested or was already started.
+ startUninstallIntent(packageName, userId);
+ }
+ }
+
+ private void startUninstallIntent(final String packageName, final int userId) {
+ final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+ synchronized (this) {
+ if (!mPackagesToRemove.contains(packageUserPair)) {
+ // Do nothing if uninstall was not requested or was already started.
+ return;
+ }
+ mPackagesToRemove.remove(packageUserPair);
+ }
+ try {
+ if (mInjector.getIPackageManager().getPackageInfo(packageName, 0, userId) == null) {
+ // Package does not exist. Nothing to do.
+ return;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Failure talking to PackageManager while getting package info");
+ }
+
+ try { // force stop the package before uninstalling
+ mInjector.getIActivityManager().forceStopPackage(packageName, userId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+ }
+ final Uri packageURI = Uri.parse("package:" + packageName);
+ final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+ uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId));
+ }
+
+ /**
+ * Removes the admin from the policy. Ideally called after the admin's
+ * {@link DeviceAdminReceiver#onDisabled(Context, Intent)} has been successfully completed.
+ *
+ * @param adminReceiver The admin to remove
+ * @param userHandle The user for which this admin has to be removed.
+ */
+ private void removeAdminArtifacts(final ComponentName adminReceiver, final int userHandle) {
+ synchronized (this) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (admin == null) {
+ return;
+ }
+ final DevicePolicyData policy = getUserData(userHandle);
+ final boolean doProxyCleanup = admin.info.usesPolicy(
+ DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+ policy.mAdminList.remove(admin);
+ policy.mAdminMap.remove(adminReceiver);
+ validatePasswordOwnerLocked(policy);
+ if (doProxyCleanup) {
+ resetGlobalProxyLocked(policy);
+ }
+ saveSettingsLocked(userHandle);
+ updateMaximumTimeToLockLocked(userHandle);
+ policy.mRemovingAdmins.remove(adminReceiver);
+ }
+ // The removed admin might have disabled camera, so update user
+ // restrictions.
+ pushUserRestrictions(userHandle);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 880f810107a0..68fd0f681aa5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -716,11 +716,11 @@ class Owners {
}
File getLegacyConfigFileWithTestOverride() {
- return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML_LEGACY);
+ return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
}
File getDeviceOwnerFileWithTestOverride() {
- return new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML);
+ return new File(Environment.getDataSystemDirectory(), DEVICE_OWNER_XML);
}
File getProfileOwnerFileWithTestOverride(int userId) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 2329b4251fc7..ceabfceccca6 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -27,7 +27,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.DhcpResults;
import android.net.BaseDhcpStateMachine;
-import android.net.DhcpStateMachine;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -103,12 +102,35 @@ public class DhcpClient extends BaseDhcpStateMachine {
// t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
+ private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+
+ /* Commands from controller to start/stop DHCP */
+ public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
+ public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
+ public static final int CMD_RENEW_DHCP = PUBLIC_BASE + 3;
+
+ /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+ public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 4;
+ /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+ * success/failure */
+ public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 5;
+ /* Notification from DHCP state machine before quitting */
+ public static final int CMD_ON_QUIT = PUBLIC_BASE + 6;
+
+ /* Command from controller to indicate DHCP discovery/renewal can continue
+ * after pre DHCP action is complete */
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7;
+
+ /* Message.arg1 arguments to CMD_POST_DHCP notification */
+ public static final int DHCP_SUCCESS = 1;
+ public static final int DHCP_FAILURE = 2;
+
// Messages.
- private static final int BASE = Protocol.BASE_DHCP + 100;
- private static final int CMD_KICK = BASE + 1;
- private static final int CMD_RECEIVED_PACKET = BASE + 2;
- private static final int CMD_TIMEOUT = BASE + 3;
- private static final int CMD_ONESHOT_TIMEOUT = BASE + 4;
+ private static final int PRIVATE_BASE = Protocol.BASE_DHCP + 100;
+ private static final int CMD_KICK = PRIVATE_BASE + 1;
+ private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
+ private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
+ private static final int CMD_ONESHOT_TIMEOUT = PRIVATE_BASE + 4;
// DHCP parameters that we request.
private static final byte[] REQUESTED_PARAMS = new byte[] {
@@ -211,7 +233,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
// Used to time out PacketRetransmittingStates.
mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
// Used to schedule DHCP renews.
- mRenewAlarm = makeWakeupMessage("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+ mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
// Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
// TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
// remove this.
@@ -400,13 +422,12 @@ public class DhcpClient extends BaseDhcpStateMachine {
}
private void notifySuccess() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
+ mController.sendMessage(
+ CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
private void notifyFailure() {
- mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
- DhcpStateMachine.DHCP_FAILURE, 0, null);
+ mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
}
private void clearDhcpState() {
@@ -428,7 +449,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
protected void onQuitting() {
Log.d(TAG, "onQuitting");
- mController.sendMessage(DhcpStateMachine.CMD_ON_QUIT);
+ mController.sendMessage(CMD_ON_QUIT);
}
private void maybeLog(String msg) {
@@ -442,17 +463,17 @@ public class DhcpClient extends BaseDhcpStateMachine {
private String messageName(int what) {
switch (what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
return "CMD_START_DHCP";
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
return "CMD_STOP_DHCP";
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
return "CMD_RENEW_DHCP";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case CMD_PRE_DHCP_ACTION:
return "CMD_PRE_DHCP_ACTION";
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
return "CMD_PRE_DHCP_ACTION_COMPLETE";
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+ case CMD_POST_DHCP_ACTION:
return "CMD_POST_DHCP_ACTION";
case CMD_KICK:
return "CMD_KICK";
@@ -495,14 +516,14 @@ public class DhcpClient extends BaseDhcpStateMachine {
@Override
public void enter() {
super.enter();
- mController.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION);
+ mController.sendMessage(CMD_PRE_DHCP_ACTION);
}
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
transitionTo(mOtherState);
return HANDLED;
default:
@@ -532,7 +553,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_START_DHCP:
+ case CMD_START_DHCP:
scheduleOneshotTimeout();
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeStartState);
@@ -588,7 +609,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_STOP_DHCP:
+ case CMD_STOP_DHCP:
transitionTo(mStoppedState);
return HANDLED;
case CMD_ONESHOT_TIMEOUT:
@@ -810,7 +831,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
- case DhcpStateMachine.CMD_RENEW_DHCP:
+ case CMD_RENEW_DHCP:
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeRenewalState);
} else {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 06b6ee75da7a..a388a26d9e91 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -61,7 +61,6 @@ import java.util.Objects;
* @hide
*/
public class IpManager extends StateMachine {
- private static final String TAG = IpManager.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -92,13 +91,18 @@ public class IpManager extends StateMachine {
*/
// Implementations must call IpManager#completedPreDhcpAction().
+ // TODO: Remove this requirement, perhaps via some
+ // registerForPreDhcpAction()-style mechanism.
public void onPreDhcpAction() {}
public void onPostDhcpAction() {}
- // TODO: Kill with fire once DHCP and static configuration are moved
- // out of WifiStateMachine.
- public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
- public void onIPv4ProvisioningFailure() {}
+ // This is purely advisory and not an indication of provisioning
+ // success or failure. This is only here for callers that want to
+ // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+ // DHCPv4 or static IPv4 configuration failure or success can be
+ // determined by whether or not the passed-in DhcpResults object is
+ // null or not.
+ public void onNewDhcpResults(DhcpResults dhcpResults) {}
public void onProvisioningSuccess(LinkProperties newLp) {}
public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,8 +126,10 @@ public class IpManager extends StateMachine {
private final Object mLock = new Object();
private final State mStoppedState = new StoppedState();
+ private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
+ private final String mTag;
private final Context mContext;
private final String mInterfaceName;
@VisibleForTesting
@@ -150,11 +156,11 @@ public class IpManager extends StateMachine {
public IpManager(Context context, String ifName, Callback callback)
throws IllegalArgumentException {
- super(TAG + "." + ifName);
+ super(IpManager.class.getSimpleName() + "." + ifName);
+ mTag = getName();
mContext = context;
mInterfaceName = ifName;
-
mCallback = callback;
mNwService = INetworkManagementService.Stub.asInterface(
@@ -171,7 +177,7 @@ public class IpManager extends StateMachine {
try {
mNwService.registerObserver(mNetlinkTracker);
} catch (RemoteException e) {
- Log.e(TAG, "Couldn't register NetlinkTracker: " + e.toString());
+ Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
}
resetLinkProperties();
@@ -179,6 +185,8 @@ public class IpManager extends StateMachine {
// Super simple StateMachine.
addState(mStoppedState);
addState(mStartedState);
+ addState(mStoppingState);
+
setInitialState(mStoppedState);
setLogRecSize(MAX_LOG_RECORDS);
super.start();
@@ -192,7 +200,9 @@ public class IpManager extends StateMachine {
*/
@VisibleForTesting
protected IpManager(String ifName, Callback callback) {
- super(TAG + ".test-" + ifName);
+ super(IpManager.class.getSimpleName() + ".test-" + ifName);
+ mTag = getName();
+
mInterfaceName = ifName;
mCallback = callback;
@@ -203,13 +213,11 @@ public class IpManager extends StateMachine {
public void startProvisioning(StaticIpConfiguration staticIpConfig) {
getInterfaceIndex();
-
sendMessage(CMD_START, staticIpConfig);
}
public void startProvisioning() {
getInterfaceIndex();
-
sendMessage(CMD_START);
}
@@ -236,12 +244,48 @@ public class IpManager extends StateMachine {
* Internals.
*/
+ @Override
+ protected String getWhatToString(int what) {
+ // TODO: Investigate switching to reflection.
+ switch (what) {
+ case CMD_STOP:
+ return "CMD_STOP";
+ case CMD_START:
+ return "CMD_START";
+ case CMD_CONFIRM:
+ return "CMD_CONFIRM";
+ case EVENT_PRE_DHCP_ACTION_COMPLETE:
+ return "EVENT_PRE_DHCP_ACTION_COMPLETE";
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ return "DhcpClient.CMD_PRE_DHCP_ACTION";
+ case DhcpClient.CMD_POST_DHCP_ACTION:
+ return "DhcpClient.CMD_POST_DHCP_ACTION";
+ case DhcpClient.CMD_ON_QUIT:
+ return "DhcpClient.CMD_ON_QUIT";
+ }
+ return "UNKNOWN:" + Integer.toString(what);
+ }
+
+ @Override
+ protected String getLogRecString(Message msg) {
+ final String logLine = String.format(
+ "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
+ mInterfaceName, mInterfaceIndex,
+ msg.arg1, msg.arg2, Objects.toString(msg.obj));
+ if (VDBG) {
+ Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
+ }
+ return logLine;
+ }
+
private void getInterfaceIndex() {
try {
mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
} catch (SocketException | NullPointerException e) {
// TODO: throw new IllegalStateException.
- Log.e(TAG, "ALERT: Failed to get interface index: ", e);
+ Log.e(mTag, "ALERT: Failed to get interface index: ", e);
}
}
@@ -260,16 +304,93 @@ public class IpManager extends StateMachine {
}
}
+ // For now: use WifiStateMachine's historical notion of provisioned.
+ private static boolean isProvisioned(LinkProperties lp) {
+ // For historical reasons, we should connect even if all we have is
+ // an IPv4 address and nothing else.
+ return lp.isProvisioned() || lp.hasIPv4Address();
+ }
+
+ // TODO: Investigate folding all this into the existing static function
+ // LinkProperties.compareProvisioning() or some other single function that
+ // takes two LinkProperties objects and returns a ProvisioningChange
+ // object that is a correct and complete assessment of what changed, taking
+ // account of the asymmetries described in the comments in this function.
+ // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+ private static ProvisioningChange compareProvisioning(
+ LinkProperties oldLp, LinkProperties newLp) {
+ ProvisioningChange delta;
+
+ final boolean wasProvisioned = isProvisioned(oldLp);
+ final boolean isProvisioned = isProvisioned(newLp);
+
+ if (!wasProvisioned && isProvisioned) {
+ delta = ProvisioningChange.GAINED_PROVISIONING;
+ } else if (wasProvisioned && isProvisioned) {
+ delta = ProvisioningChange.STILL_PROVISIONED;
+ } else if (!wasProvisioned && !isProvisioned) {
+ delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+ } else {
+ // (wasProvisioned && !isProvisioned)
+ //
+ // Note that this is true even if we lose a configuration element
+ // (e.g., a default gateway) that would not be required to advance
+ // into provisioned state. This is intended: if we have a default
+ // router and we lose it, that's a sure sign of a problem, but if
+ // we connect to a network with no IPv4 DNS servers, we consider
+ // that to be a network without DNS servers and connect anyway.
+ //
+ // See the comment below.
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ // Additionally:
+ //
+ // Partial configurations (e.g., only an IPv4 address with no DNS
+ // servers and no default route) are accepted as long as DHCPv4
+ // succeeds. On such a network, isProvisioned() will always return
+ // false, because the configuration is not complete, but we want to
+ // connect anyway. It might be a disconnected network such as a
+ // Chromecast or a wireless printer, for example.
+ //
+ // Because on such a network isProvisioned() will always return false,
+ // delta will never be LOST_PROVISIONING. So check for loss of
+ // provisioning here too.
+ if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+ (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ return delta;
+ }
+
+ private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+ switch (delta) {
+ case GAINED_PROVISIONING:
+ if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+ mCallback.onProvisioningSuccess(newLp);
+ break;
+
+ case LOST_PROVISIONING:
+ if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+ mCallback.onProvisioningFailure(newLp);
+ break;
+
+ default:
+ if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+ mCallback.onLinkPropertiesChange(newLp);
+ break;
+ }
+ }
+
private ProvisioningChange setLinkProperties(LinkProperties newLp) {
if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.updateLinkProperties(newLp);
}
- // TODO: Figure out whether and how to incorporate static configuration
- // into the notion of provisioning.
ProvisioningChange delta;
synchronized (mLock) {
- delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
+ delta = compareProvisioning(mLinkProperties, newLp);
mLinkProperties = new LinkProperties(newLp);
}
@@ -277,7 +398,7 @@ public class IpManager extends StateMachine {
switch (delta) {
case GAINED_PROVISIONING:
case LOST_PROVISIONING:
- Log.d(TAG, "provisioning: " + delta);
+ Log.d(mTag, "provisioning: " + delta);
break;
}
}
@@ -333,7 +454,7 @@ public class IpManager extends StateMachine {
}
if (VDBG) {
- Log.d(TAG, "newLp{" + newLp + "}");
+ Log.d(mTag, "newLp{" + newLp + "}");
}
return newLp;
@@ -345,21 +466,51 @@ public class IpManager extends StateMachine {
ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
} catch (RemoteException e) {
- Log.e(TAG, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
+ Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
}
}
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
- setLinkProperties(assembleLinkProperties());
- mCallback.onIPv4ProvisioningSuccess(dhcpResults);
+ final LinkProperties newLp = assembleLinkProperties();
+ final ProvisioningChange delta = setLinkProperties(newLp);
+
+ if (VDBG) {
+ Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+ }
+ mCallback.onNewDhcpResults(dhcpResults);
+
+ dispatchCallback(delta, newLp);
}
private void handleIPv4Failure() {
+ // TODO: Figure out to de-dup this and the same code in DhcpClient.
clearIPv4Address();
mDhcpResults = null;
- setLinkProperties(assembleLinkProperties());
- mCallback.onIPv4ProvisioningFailure();
+ final LinkProperties newLp = assembleLinkProperties();
+ ProvisioningChange delta = setLinkProperties(newLp);
+ // If we've gotten here and we're still not provisioned treat that as
+ // a total loss of provisioning.
+ //
+ // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+ // there was no usable IPv6 obtained before the DHCPv4 timeout.
+ //
+ // Regardless: GAME OVER.
+ //
+ // TODO: Make the DHCP client not time out and just continue in
+ // exponential backoff. Callers such as Wi-Fi which need a timeout
+ // should implement it themselves.
+ if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
+ delta = ProvisioningChange.LOST_PROVISIONING;
+ }
+
+ if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+ mCallback.onNewDhcpResults(null);
+
+ dispatchCallback(delta, newLp);
+ if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ transitionTo(mStoppingState);
+ }
}
class StoppedState extends State {
@@ -369,7 +520,7 @@ public class IpManager extends StateMachine {
mNwService.disableIpv6(mInterfaceName);
mNwService.clearInterfaceAddresses(mInterfaceName);
} catch (Exception e) {
- Log.e(TAG, "Failed to clear addresses or disable IPv6" + e);
+ Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
}
resetLinkProperties();
@@ -390,14 +541,9 @@ public class IpManager extends StateMachine {
setLinkProperties(assembleLinkProperties());
break;
- case DhcpStateMachine.CMD_ON_QUIT:
- // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
- // Shutting down DHCPv4 progresses simultaneously with
- // transitioning to StoppedState, so we can receive this
- // message after we've already transitioned here.
- //
- // TODO: Figure out if this is actually useful and if not
- // expunge it.
+ case DhcpClient.CMD_ON_QUIT:
+ // Everything is already stopped.
+ Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
break;
default:
@@ -407,6 +553,30 @@ public class IpManager extends StateMachine {
}
}
+ class StoppingState extends State {
+ @Override
+ public void enter() {
+ if (mDhcpStateMachine == null) {
+ // There's no DHCPv4 for which to wait; proceed to stopped.
+ transitionTo(mStoppedState);
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case DhcpClient.CMD_ON_QUIT:
+ mDhcpStateMachine = null;
+ transitionTo(mStoppedState);
+ break;
+
+ default:
+ deferMessage(msg);
+ }
+ return HANDLED;
+ }
+ }
+
class StartedState extends State {
@Override
public void enter() {
@@ -416,9 +586,9 @@ public class IpManager extends StateMachine {
mNwService.enableIpv6(mInterfaceName);
// TODO: Perhaps clearIPv4Address() as well.
} catch (RemoteException re) {
- Log.e(TAG, "Unable to change interface settings: " + re);
+ Log.e(mTag, "Unable to change interface settings: " + re);
} catch (IllegalStateException ie) {
- Log.e(TAG, "Unable to change interface settings: " + ie);
+ Log.e(mTag, "Unable to change interface settings: " + ie);
}
mIpReachabilityMonitor = new IpReachabilityMonitor(
@@ -439,13 +609,15 @@ public class IpManager extends StateMachine {
if (applyStaticIpConfig()) {
handleIPv4Success(new DhcpResults(mStaticIpConfig));
} else {
- handleIPv4Failure();
+ if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+ mCallback.onProvisioningFailure(getLinkProperties());
+ transitionTo(mStoppingState);
}
} else {
// Start DHCPv4.
makeDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_START_DHCP);
}
}
@@ -455,9 +627,8 @@ public class IpManager extends StateMachine {
mIpReachabilityMonitor = null;
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpStateMachine.doQuit();
- mDhcpStateMachine = null;
}
resetLinkProperties();
@@ -471,7 +642,7 @@ public class IpManager extends StateMachine {
break;
case CMD_START:
- Log.e(TAG, "ALERT: START received in StartedState. Please fix caller.");
+ Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
break;
case CMD_CONFIRM:
@@ -489,8 +660,7 @@ public class IpManager extends StateMachine {
// calls completedPreDhcpAction() after provisioning with
// a static IP configuration.
if (mDhcpStateMachine != null) {
- mDhcpStateMachine.sendMessage(
- DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+ mDhcpStateMachine.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
}
break;
@@ -500,57 +670,43 @@ public class IpManager extends StateMachine {
break;
}
final ProvisioningChange delta = setLinkProperties(newLp);
-
- // NOTE: The only receiver of these callbacks currently
- // treats all three of them identically, namely it calls
- // IpManager#getLinkProperties() and makes its own determination.
- switch (delta) {
- case GAINED_PROVISIONING:
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case LOST_PROVISIONING:
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- // TODO: Only notify on STILL_PROVISIONED?
- mCallback.onLinkPropertiesChange(newLp);
- break;
+ dispatchCallback(delta, newLp);
+ if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ transitionTo(mStoppedState);
}
break;
}
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
mCallback.onPreDhcpAction();
break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION: {
+ case DhcpClient.CMD_POST_DHCP_ACTION: {
// Note that onPostDhcpAction() is likely to be
// asynchronous, and thus there is no guarantee that we
// will be able to observe any of its effects here.
+ if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); }
mCallback.onPostDhcpAction();
final DhcpResults dhcpResults = (DhcpResults) msg.obj;
switch (msg.arg1) {
- case DhcpStateMachine.DHCP_SUCCESS:
+ case DhcpClient.DHCP_SUCCESS:
handleIPv4Success(dhcpResults);
break;
- case DhcpStateMachine.DHCP_FAILURE:
+ case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
break;
default:
- Log.e(TAG, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
+ Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
}
break;
}
- case DhcpStateMachine.CMD_ON_QUIT:
- // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
- // Regardless, we ignore it.
- //
- // TODO: Figure out if this is actually useful and if not
- // expunge it.
+ case DhcpClient.CMD_ON_QUIT:
+ // DHCPv4 quit early for some reason.
+ Log.e(mTag, "Unexpected CMD_ON_QUIT.");
+ mDhcpStateMachine = null;
break;
default:
@@ -565,9 +721,9 @@ public class IpManager extends StateMachine {
ifcg.setInterfaceUp();
try {
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) Log.d(TAG, "Static IP configuration succeeded");
+ if (DBG) Log.d(mTag, "Static IP configuration succeeded");
} catch (IllegalStateException | RemoteException e) {
- Log.e(TAG, "Static IP configuration failed: ", e);
+ Log.e(mTag, "Static IP configuration failed: ", e);
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 64f60d93628b..467ecd77970e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -22,9 +22,12 @@ import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
+import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
@@ -995,6 +998,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testApplicationRestrictionsManagingApp() throws Exception {
setAsProfileOwner(admin1);
+ final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
final int appRestrictionsManagerAppId = 20987;
final int appRestrictionsManagerUid = UserHandle.getUid(
@@ -1004,6 +1008,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(DpmMockContext.CALLER_USER_HANDLE));
mContext.binder.callingUid = appRestrictionsManagerUid;
+ final PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+ doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+ eq(appRestrictionsManagerPackage),
+ anyInt(),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+
// appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
// delegated that permission yet.
assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
@@ -1028,6 +1040,16 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+ // Check the API does not allow setting a non-existent package
+ try {
+ dpm.setApplicationRestrictionsManagingPackage(admin1,
+ nonExistAppRestrictionsManagerPackage);
+ fail("Non-existent app set as app restriction manager.");
+ } catch (IllegalArgumentException expected) {
+ MoreAsserts.assertContainsRegex(
+ "is not installed on the current user", expected.getMessage());
+ }
+
// Let appRestrictionsManagerPackage manage app restrictions
dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
assertEquals(appRestrictionsManagerPackage,
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 73cc4a5f6138..3f32dbe0fdbe 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -16,6 +16,8 @@
package android.telecom;
+import android.os.AsyncTask;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.IllegalFormatException;
@@ -38,8 +40,26 @@ final public class Log {
public static final boolean WARN = isLoggable(android.util.Log.WARN);
public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
+ private static MessageDigest sMessageDigest;
+
private Log() {}
+ public static void initMd5Sum() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... args) {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ md = null;
+ }
+ sMessageDigest = md;
+ return null;
+ }
+ }.execute();
+ }
+
public static boolean isLoggable(int level) {
return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
}
@@ -137,15 +157,14 @@ final public class Log {
}
private static String secureHash(byte[] input) {
- MessageDigest messageDigest;
- try {
- messageDigest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return null;
+ if (sMessageDigest != null) {
+ sMessageDigest.reset();
+ sMessageDigest.update(input);
+ byte[] result = sMessageDigest.digest();
+ return encodeHex(result);
+ } else {
+ return "Uninitialized SHA1";
}
- messageDigest.update(input);
- byte[] result = messageDigest.digest();
- return encodeHex(result);
}
private static String encodeHex(byte[] bytes) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f1cbb9ab73d3..9f478df2063e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -498,6 +498,7 @@ public class TelecomManager {
mContext = context;
}
mTelecomServiceOverride = telecomServiceImpl;
+ android.telecom.Log.initMd5Sum();
}
/**
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 23a69d10aa7a..69259d078baf 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -17,6 +17,7 @@
package com.android.ims.internal;
import com.android.ims.ImsReasonInfo;
+
/**
* A listener type for receiving notifications about the changes to
* the IMS connection(registration).
@@ -26,15 +27,36 @@ import com.android.ims.ImsReasonInfo;
interface IImsRegistrationListener {
/**
* Notifies the application when the device is connected to the IMS network.
+ *
+ * @deprecated see {@link registrationConnectedWithRadioTech}
*/
void registrationConnected();
/**
* Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @deprecated see {@link registrationProgressingWithRadioTech}
*/
void registrationProgressing();
/**
+ * Notifies the application when the device is connected to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationConnectedWithRadioTech(int imsRadioTech);
+
+ /**
+ * Notifies the application when the device is trying to connect the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are {@code
+ * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}.
+ */
+ void registrationProgressingWithRadioTech(int imsRadioTech);
+
+
+ /**
* Notifies the application when the device is disconnected from the IMS network.
*/
void registrationDisconnected(in ImsReasonInfo imsReasonInfo);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 429839f6f6f9..0d6c70b9a4c6 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,7 +92,33 @@ public interface RILConstants {
int NO_SMS_TO_ACK = 48; /* ACK received when there is no SMS to ack */
int NETWORK_ERR = 49; /* Received error from network */
int REQUEST_RATE_LIMITED = 50; /* Operation denied due to overly-frequent requests */
-
+ // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
+ // reveal particular replacement for Generic failure
+ int OEM_ERROR_1 = 501;
+ int OEM_ERROR_2 = 502;
+ int OEM_ERROR_3 = 503;
+ int OEM_ERROR_4 = 504;
+ int OEM_ERROR_5 = 505;
+ int OEM_ERROR_6 = 506;
+ int OEM_ERROR_7 = 507;
+ int OEM_ERROR_8 = 508;
+ int OEM_ERROR_9 = 509;
+ int OEM_ERROR_10 = 510;
+ int OEM_ERROR_11 = 511;
+ int OEM_ERROR_12 = 512;
+ int OEM_ERROR_13 = 513;
+ int OEM_ERROR_14 = 514;
+ int OEM_ERROR_15 = 515;
+ int OEM_ERROR_16 = 516;
+ int OEM_ERROR_17 = 517;
+ int OEM_ERROR_18 = 518;
+ int OEM_ERROR_19 = 519;
+ int OEM_ERROR_20 = 520;
+ int OEM_ERROR_21 = 521;
+ int OEM_ERROR_22 = 522;
+ int OEM_ERROR_23 = 523;
+ int OEM_ERROR_24 = 524;
+ int OEM_ERROR_25 = 525;
/* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index 57a5b551fe21..ea7648708e75 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -27,7 +27,6 @@
android:layout_marginTop="25dp"
android:saveEnabled="false"
android:textSize="18sp"
- android:textColor="#ffffffff"
android:text="@string/waste_away"
/>
@@ -38,7 +37,6 @@
android:layout_marginTop="25dp"
android:saveEnabled="false"
android:textSize="18sp"
- android:textColor="#ffffffff"
android:text="@string/wake_away"
/>
@@ -52,7 +50,6 @@
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:textSize="12sp"
- android:textColor="#ffffffff"
/>
</ScrollView>
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4d9ba6c95d9e..18a194326de7 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1883,8 +1883,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil
//printf("Comment of %s: %s\n", String8(e).string(),
// String8(cmt).string());
syms->appendComment(String8(e), String16(cmt), srcPos);
- } else {
- //printf("No comment for %s\n", String8(e).string());
}
syms->makeSymbolPublic(String8(e), srcPos);
} else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
@@ -2535,10 +2533,6 @@ static status_t writeSymbolClass(
fprintf(fp,
"%s/** %s\n",
getIndentSpace(indent), cmt.string());
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
@@ -2581,10 +2575,6 @@ static status_t writeSymbolClass(
"%s */\n",
getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
- } else if (sym.isPublic && !includePrivate) {
- sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
- assets->getPackage().string(), className.string(),
- String8(sym.name).string());
}
ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 88b6270fad60..cb82ac37a3b2 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -38,6 +38,7 @@ sources := \
io/ZipArchive.cpp \
link/AutoVersioner.cpp \
link/ManifestFixer.cpp \
+ link/ProductFilter.cpp \
link/PrivateAttributeMover.cpp \
link/ReferenceLinker.cpp \
link/TableMerger.cpp \
@@ -83,6 +84,7 @@ testSources := \
link/AutoVersioner_test.cpp \
link/ManifestFixer_test.cpp \
link/PrivateAttributeMover_test.cpp \
+ link/ProductFilter_test.cpp \
link/ReferenceLinker_test.cpp \
link/TableMerger_test.cpp \
link/XmlReferenceLinker_test.cpp \
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index b4e75f9be3a9..4bea12973692 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -144,8 +144,8 @@ void Debug::printTable(ResourceTable* table) {
PrintVisitor visitor;
for (const auto& value : entry->values) {
- std::cout << " (" << value.config << ") ";
- value.value->accept(&visitor);
+ std::cout << " (" << value->config << ") ";
+ value->value->accept(&visitor);
std::cout << std::endl;
}
}
@@ -176,7 +176,7 @@ void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyl
if (result) {
ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
- if (Style* style = valueCast<Style>(value.value.get())) {
+ if (Style* style = valueCast<Style>(value->value.get())) {
if (style->parent && style->parent.value().name) {
parents.insert(style->parent.value().name.value());
stylesToVisit.push(style->parent.value().name.value());
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b37d366a7887..b100e84f8a46 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -19,7 +19,6 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "util/Comparators.h"
#include "util/ImmutableMap.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
@@ -71,6 +70,7 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) {
struct ParsedResource {
ResourceName name;
ConfigDescription config;
+ std::string product;
Source source;
ResourceId id;
Maybe<SymbolState> symbolState;
@@ -79,35 +79,6 @@ struct ParsedResource {
std::list<ParsedResource> childResources;
};
-bool ResourceParser::shouldStripResource(const ResourceNameRef& name,
- const Maybe<std::u16string>& product) const {
- if (product) {
- for (const std::u16string& productToMatch : mOptions.products) {
- if (product.value() == productToMatch) {
- // We specified a product, and it is in the list, so don't strip.
- return false;
- }
- }
- }
-
- // Nothing matched, try 'default'. Default only matches if we didn't already use another
- // product variant.
- if (!product || product.value() == u"default") {
- if (Maybe<ResourceTable::SearchResult> result = mTable->findResource(name)) {
- const ResourceEntry* entry = result.value().entry;
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), mConfig,
- cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == mConfig && !iter->value->isWeak()) {
- // We have a value for this config already, and it is not weak,
- // so filter out this default.
- return true;
- }
- }
- return false;
- }
- return true;
-}
-
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
if (res->symbolState) {
@@ -125,7 +96,8 @@ static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed
res->value->setComment(std::move(res->comment));
res->value->setSource(std::move(res->source));
- if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
+ if (!table->addResource(res->name, res->id, res->config, res->product,
+ std::move(res->value), diag)) {
return false;
}
}
@@ -295,9 +267,8 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
parsedResource.comment = std::move(comment);
// Extract the product name if it exists.
- Maybe<std::u16string> product;
if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
- product = maybeProduct.value().toString();
+ parsedResource.product = util::utf16ToUtf8(maybeProduct.value());
}
// Parse the resource regardless of product.
@@ -306,12 +277,7 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
continue;
}
- // We successfully parsed the resource. Check if we should include it or strip it.
- if (shouldStripResource(parsedResource.name, product)) {
- // Record that we stripped out this resource name.
- // We will check that at least one variant of this resource was included.
- strippedResources.insert(parsedResource.name);
- } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
+ if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
error = true;
}
}
@@ -524,7 +490,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const
// name.package can be empty here, as it will assume the package name of the table.
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(mSource.withLine(beginXmlLine));
- mTable->addResource(name, {}, std::move(id), mDiag);
+ mTable->addResource(name, {}, {}, std::move(id), mDiag);
};
// Process the raw value.
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 51cbbe19384e..ee5b33788312 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -34,13 +34,6 @@ struct ParsedResource;
struct ResourceParserOptions {
/**
- * Optional product names by which to filter resources.
- * This is like a preprocessor definition in that we strip out resources
- * that don't match before we compile them.
- */
- std::vector<std::u16string> products;
-
- /**
* Whether the default setting for this parser is to allow translation.
*/
bool translatable = true;
@@ -106,9 +99,6 @@ private:
bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool shouldStripResource(const ResourceNameRef& name,
- const Maybe<std::u16string>& product) const;
-
IDiagnostics* mDiag;
ResourceTable* mTable;
Source mSource;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index cf0fcd11b903..3450de9078bb 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -48,24 +48,13 @@ struct ResourceParserTest : public ::testing::Test {
}
::testing::AssertionResult testParse(const StringPiece& str) {
- return testParse(str, ConfigDescription{}, {});
+ return testParse(str, ConfigDescription{});
}
::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
- return testParse(str, config, {});
- }
-
- ::testing::AssertionResult testParse(const StringPiece& str,
- std::initializer_list<std::u16string> products) {
- return testParse(str, {}, std::move(products));
- }
-
- ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
- std::initializer_list<std::u16string> products) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
- parserOptions.products = products;
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
parserOptions);
xml::XmlPullParser xmlParser(input);
@@ -546,7 +535,7 @@ TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
ASSERT_NE(nullptr, id);
}
-TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
+TEST_F(ResourceParserTest, KeepAllProducts) {
std::string input = R"EOF(
<string name="foo" product="phone">hi</string>
<string name="foo" product="no-sdcard">ho</string>
@@ -555,33 +544,26 @@ TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
<string name="bit" product="phablet">hoot</string>
<string name="bot" product="default">yes</string>
)EOF";
- ASSERT_TRUE(testParse(input, { std::u16string(u"no-sdcard"), std::u16string(u"phablet") }));
-
- String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
- ASSERT_NE(nullptr, fooStr);
- EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);
-
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bit"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bot"));
-}
-
-TEST_F(ResourceParserTest, FilterProductsThatBothMatchInOrder) {
- std::string input = R"EOF(
- <string name="foo" product="phone">phone</string>
- <string name="foo" product="default">default</string>
- )EOF";
- ASSERT_TRUE(testParse(input, { std::u16string(u"phone") }));
-
- String* foo = test::getValue<String>(&mTable, u"@string/foo");
- ASSERT_NE(nullptr, foo);
- EXPECT_EQ(std::u16string(u"phone"), *foo->value);
-}
+ ASSERT_TRUE(testParse(input));
-TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
- std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
- ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
+ ConfigDescription::defaultConfig(),
+ "phone"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
+ ConfigDescription::defaultConfig(),
+ "no-sdcard"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bar",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/baz",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bit",
+ ConfigDescription::defaultConfig(),
+ "phablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bot",
+ ConfigDescription::defaultConfig(),
+ "default"));
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8a3d047f6e8d..3e73be4dbc54 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -19,8 +19,6 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
-#include "util/Comparators.h"
#include "util/Util.h"
#include <algorithm>
@@ -124,6 +122,73 @@ ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) {
return entries.emplace(iter, new ResourceEntry(name))->get();
}
+ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
+ return findValue(config, StringPiece());
+}
+
+struct ConfigKey {
+ const ConfigDescription* config;
+ const StringPiece& product;
+};
+
+bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
+}
+
+ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
+ const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{ &config, product }, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ return nullptr;
+}
+
+ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{ &config, product }, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ ResourceConfigValue* newValue = values.insert(
+ iter, util::make_unique<ResourceConfigValue>(config, product))->get();
+ return newValue;
+}
+
+std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
+ std::vector<ResourceConfigValue*> results;
+
+ auto iter = values.begin();
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ ++iter;
+ break;
+ }
+ }
+
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ }
+ }
+ return results;
+}
+
/**
* The default handler for collisions. A return value of -1 means keep the
* existing value, 0 means fail, and +1 means take the incoming value.
@@ -188,56 +253,69 @@ int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
static constexpr const char16_t* kValidNameChars = u"._-";
static constexpr const char16_t* kValidNameMangledChars = u"._-$";
-bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag) {
- return addResourceImpl(name, {}, config, std::move(value), kValidNameChars,
+bool ResourceTable::addResource(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
resolveValueCollision, diag);
}
-bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+bool ResourceTable::addResource(const ResourceNameRef& name,
+ const ResourceId resId,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, resId, config, std::move(value), kValidNameChars,
+ return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
resolveValueCollision, diag);
}
-bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+bool ResourceTable::addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
IDiagnostics* diag) {
return addFileReference(name, config, source, path, resolveValueCollision, diag);
}
-bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+bool ResourceTable::addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
std::function<int(Value*,Value*)> conflictResolver,
IDiagnostics* diag) {
std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
stringPool.makeRef(path));
fileRef->setSource(source);
- return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars,
- conflictResolver, diag);
+ return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
+ kValidNameChars, conflictResolver, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, ResourceId{}, config, std::move(value), kValidNameMangledChars,
- resolveValueCollision, diag);
+ return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
+ kValidNameMangledChars, resolveValueCollision, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
const ResourceId id,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars,
+ return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
resolveValueCollision, diag);
}
bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
const ResourceId resId,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
const char16_t* validChars,
std::function<int(Value*,Value*)> conflictResolver,
@@ -298,21 +376,21 @@ bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
return false;
}
- const auto endIter = entry->values.end();
- auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
- if (iter == endIter || iter->config != config) {
- // This resource did not exist before, add it.
- entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
+ ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
+ if (!configValue->value) {
+ // Resource does not exist, add it now.
+ configValue->value = std::move(value);
+
} else {
- int collisionResult = conflictResolver(iter->value.get(), value.get());
+ int collisionResult = conflictResolver(configValue->value.get(), value.get());
if (collisionResult > 0) {
// Take the incoming value.
- iter->value = std::move(value);
+ configValue->value = std::move(value);
} else if (collisionResult == 0) {
diag->error(DiagMessage(value->getSource())
<< "duplicate value for resource '" << name << "' "
<< "with config '" << config << "'");
- diag->error(DiagMessage(iter->value->getSource())
+ diag->error(DiagMessage(configValue->value->getSource())
<< "resource previously defined here");
return false;
}
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 6b7b07ea3a93..8ffff1f70710 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -24,9 +24,12 @@
#include "Source.h"
#include "StringPool.h"
+#include <android-base/macros.h>
+#include <map>
#include <memory>
#include <string>
#include <tuple>
+#include <unordered_map>
#include <vector>
namespace aapt {
@@ -46,19 +49,36 @@ struct Symbol {
std::u16string comment;
};
-/**
- * Represents a value defined for a given configuration.
- */
-struct ResourceConfigValue {
- ConfigDescription config;
+class ResourceConfigValue {
+public:
+ /**
+ * The configuration for which this value is defined.
+ */
+ const ConfigDescription config;
+
+ /**
+ * The product for which this value is defined.
+ */
+ const std::string product;
+
+ /**
+ * The actual Value.
+ */
std::unique_ptr<Value> value;
+
+ ResourceConfigValue(const ConfigDescription& config, const StringPiece& product) :
+ config(config), product(product.toString()) { }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};
/**
* Represents a resource entry, which may have
* varying values for each defined configuration.
*/
-struct ResourceEntry {
+class ResourceEntry {
+public:
/**
* The name of the resource. Immutable, as
* this determines the order of this resource
@@ -72,24 +92,33 @@ struct ResourceEntry {
Maybe<uint16_t> id;
/**
- * Whether this resource is public (and must maintain the same
- * entry ID across builds).
+ * Whether this resource is public (and must maintain the same entry ID across builds).
*/
Symbol symbolStatus;
/**
* The resource's values for each configuration.
*/
- std::vector<ResourceConfigValue> values;
+ std::vector<std::unique_ptr<ResourceConfigValue>> values;
ResourceEntry(const StringPiece16& name) : name(name.toString()) { }
+
+ ResourceConfigValue* findValue(const ConfigDescription& config);
+ ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product);
+ ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product);
+ std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};
/**
* Represents a resource type, which holds entries defined
* for this type.
*/
-struct ResourceTableType {
+class ResourceTableType {
+public:
/**
* The logical type of resource (string, drawable, layout, etc.).
*/
@@ -114,8 +143,10 @@ struct ResourceTableType {
explicit ResourceTableType(const ResourceType type) : type(type) { }
ResourceEntry* findEntry(const StringPiece16& name);
-
ResourceEntry* findOrCreateEntry(const StringPiece16& name);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
};
enum class PackageType {
@@ -125,16 +156,20 @@ enum class PackageType {
Dynamic
};
-struct ResourceTablePackage {
+class ResourceTablePackage {
+public:
PackageType type = PackageType::App;
Maybe<uint8_t> id;
std::u16string name;
std::vector<std::unique_ptr<ResourceTableType>> types;
+ ResourceTablePackage() = default;
ResourceTableType* findType(ResourceType type);
-
ResourceTableType* findOrCreateType(const ResourceType type);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
/**
@@ -144,8 +179,6 @@ struct ResourceTablePackage {
class ResourceTable {
public:
ResourceTable() = default;
- ResourceTable(const ResourceTable&) = delete;
- ResourceTable& operator=(const ResourceTable&) = delete;
/**
* When a collision of resources occurs, this method decides which value to keep.
@@ -155,38 +188,59 @@ public:
*/
static int resolveValueCollision(Value* existing, Value* incoming);
- bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag);
+ bool addResource(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+ bool addResource(const ResourceNameRef& name,
+ const ResourceId resId,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+ bool addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
- std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag);
+ bool addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
+ std::function<int(Value*,Value*)> conflictResolver,
+ IDiagnostics* diag);
/**
* Same as addResource, but doesn't verify the validity of the name. This is used
* when loading resources from an existing binary resource table that may have mangled
* names.
*/
- bool addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag);
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool addResourceAllowMangled(const ResourceNameRef& name, const ResourceId id,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ResourceId id,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag);
- bool setSymbolState(const ResourceNameRef& name, const ResourceId resId,
- const Symbol& symbol, IDiagnostics* diag);
+ bool setSymbolState(const ResourceNameRef& name,
+ const ResourceId resId,
+ const Symbol& symbol,
+ IDiagnostics* diag);
- bool setSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId resId,
- const Symbol& symbol, IDiagnostics* diag);
+ bool setSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId resId,
+ const Symbol& symbol,
+ IDiagnostics* diag);
struct SearchResult {
ResourceTablePackage* package;
@@ -229,13 +283,19 @@ private:
bool addResourceImpl(const ResourceNameRef& name,
ResourceId resId,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
const char16_t* validChars,
std::function<int(Value*,Value*)> conflictResolver,
IDiagnostics* diag);
- bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId,
- const Symbol& symbol, const char16_t* validChars, IDiagnostics* diag);
+ bool setSymbolStateImpl(const ResourceNameRef& name,
+ ResourceId resId,
+ const Symbol& symbol,
+ const char16_t* validChars,
+ IDiagnostics* diag);
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 42508fe154b8..180bd11275df 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -43,13 +43,13 @@ TEST_F(ResourceTableTest, FailToAddResourceWithBadName) {
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
- ConfigDescription{},
+ ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
&mDiagnostics));
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
- ConfigDescription{},
+ ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
&mDiagnostics));
}
@@ -59,6 +59,7 @@ TEST_F(ResourceTableTest, AddOneResource) {
EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
ConfigDescription{},
+ "",
test::ValueBuilder<Id>()
.setSource("test/path/file.xml", 23u).build(),
&mDiagnostics));
@@ -76,24 +77,28 @@ TEST_F(ResourceTableTest, AddMultipleResources) {
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:attr/layout_width"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:attr/id"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
languageConfig,
+ "",
test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
.setSource("test/path/file.xml", 20u)
.build(),
@@ -110,18 +115,49 @@ TEST_F(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- util::make_unique<Attribute>(true), &mDiagnostics));
+ "", util::make_unique<Attribute>(true), &mDiagnostics));
Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_TRUE(attr->isWeak());
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- util::make_unique<Attribute>(false), &mDiagnostics));
+ "", util::make_unique<Attribute>(false), &mDiagnostics));
attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_FALSE(attr->isWeak());
}
+TEST_F(ResourceTableTest, ProductVaryingValues) {
+ ResourceTable table;
+
+ EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
+ test::parseConfigOrDie("land"),
+ "tablet",
+ util::make_unique<Id>(),
+ &mDiagnostics));
+ EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
+ test::parseConfigOrDie("land"),
+ "phone",
+ util::make_unique<Id>(),
+ &mDiagnostics));
+
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
+ test::parseConfigOrDie("land"),
+ "tablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
+ test::parseConfigOrDie("land"),
+ "phone"));
+
+ Maybe<ResourceTable::SearchResult> sr = table.findResource(
+ test::parseNameOrDie(u"@android:string/foo"));
+ AAPT_ASSERT_TRUE(sr);
+ std::vector<ResourceConfigValue*> values = sr.value().entry->findAllValues(
+ test::parseConfigOrDie("land"));
+ ASSERT_EQ(2u, values.size());
+ EXPECT_EQ(std::string("phone"), values[0]->product);
+ EXPECT_EQ(std::string("tablet"), values[1]->product);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 549303939351..ea2aa55764c1 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -146,7 +146,7 @@ inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor*
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& configValue : entry->values) {
- configValue.value->accept(visitor);
+ configValue->value->accept(visitor);
}
}
}
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 1eefb821768e..0dd8e1859661 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -107,7 +107,6 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
struct CompileOptions {
std::string outputPath;
Maybe<std::string> resDir;
- std::vector<std::u16string> products;
bool pseudolocalize = false;
bool legacyMode = false;
bool verbose = false;
@@ -198,7 +197,6 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
xml::XmlPullParser xmlParser(fin);
ResourceParserOptions parserOptions;
- parserOptions.products = options.products;
parserOptions.errorOnPositionalArguments = !options.legacyMode;
// If the filename includes donottranslate, then the default translatable is false.
@@ -457,11 +455,8 @@ public:
int compile(const std::vector<StringPiece>& args) {
CompileOptions options;
- Maybe<std::string> productList;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
- .optionalFlag("--product", "Comma separated list of product types to compile",
- &productList)
.optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
.optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
"(en-XA and ar-XB)", &options.pseudolocalize)
@@ -472,12 +467,6 @@ int compile(const std::vector<StringPiece>& args) {
return 1;
}
- if (productList) {
- for (StringPiece part : util::tokenize<char>(productList.value(), ',')) {
- options.products.push_back(util::utf8ToUtf16(part));
- }
- }
-
CompileContext context;
std::unique_ptr<IArchiveWriter> archiveWriter;
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 2963d135cbca..be26b528b184 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -19,7 +19,6 @@
#include "ValueVisitor.h"
#include "compile/PseudolocaleGenerator.h"
#include "compile/Pseudolocalizer.h"
-#include "util/Comparators.h"
namespace aapt {
@@ -208,10 +207,12 @@ ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
return modified;
}
-void pseudolocalizeIfNeeded(std::vector<ResourceConfigValue>* configValues,
- Pseudolocalizer::Method method, StringPool* pool, Value* value) {
+void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
+ ResourceConfigValue* originalValue,
+ StringPool* pool,
+ ResourceEntry* entry) {
Visitor visitor(pool, method);
- value->accept(&visitor);
+ originalValue->value->accept(&visitor);
std::unique_ptr<Value> localizedValue;
if (visitor.mValue) {
@@ -220,16 +221,18 @@ void pseudolocalizeIfNeeded(std::vector<ResourceConfigValue>* configValues,
localizedValue = std::move(visitor.mItem);
}
- if (localizedValue) {
- ConfigDescription pseudolocalizedConfig = modifyConfigForPseudoLocale(ConfigDescription{},
- method);
- auto iter = std::lower_bound(configValues->begin(), configValues->end(),
- pseudolocalizedConfig, cmp::lessThanConfig);
- if (iter == configValues->end() || iter->config != pseudolocalizedConfig) {
- // The pseudolocalized config doesn't exist, add it.
- configValues->insert(iter, ResourceConfigValue{ pseudolocalizedConfig,
- std::move(localizedValue) });
- }
+ if (!localizedValue) {
+ return;
+ }
+
+ ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
+ originalValue->config, method);
+
+ ResourceConfigValue* newConfigValue = entry->findOrCreateValue(
+ configWithAccent, originalValue->product);
+ if (!newConfigValue->value) {
+ // Only use auto-generated pseudo-localization if none is defined.
+ newConfigValue->value = std::move(localizedValue);
}
}
@@ -239,18 +242,13 @@ bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table)
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- ConfigDescription{}, cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == ConfigDescription{}) {
- // Only pseudolocalize the default configuration.
-
- // The iterator will be invalidated, so grab a pointer to the value.
- Value* originalValue = iter->value.get();
-
- pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kAccent,
- &table->stringPool, originalValue);
- pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kBidi,
- &table->stringPool, originalValue);
+ std::vector<ResourceConfigValue*> values = entry->findAllValues(
+ ConfigDescription::defaultConfig());
+ for (ResourceConfigValue* value : values) {
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
+ &table->stringPool, entry.get());
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
+ &table->stringPool, entry.get());
}
}
}
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 71ab3dbf52ca..da81046b2e42 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -402,10 +402,10 @@ private:
const size_t configCount = entry->values.size();
for (size_t i = 0; i < configCount; i++) {
- const ConfigDescription& config = entry->values[i].config;
+ const ConfigDescription& config = entry->values[i]->config;
for (size_t j = i + 1; j < configCount; j++) {
configMasks[entry->id.value()] |= util::hostToDevice32(
- config.diff(entry->values[j].config));
+ config.diff(entry->values[j]->config));
}
}
}
@@ -445,8 +445,8 @@ private:
// Group values by configuration.
for (auto& configValue : entry->values) {
- configToEntryListMap[configValue.config].push_back(FlatEntry{
- entry, configValue.value.get(), keyIndex });
+ configToEntryListMap[configValue->config].push_back(FlatEntry{
+ entry, configValue->value.get(), keyIndex });
}
}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 7280f3a968a0..6e340a2a2742 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -23,7 +23,6 @@
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinitionWriter.h"
#include "java/JavaClassGenerator.h"
-#include "util/Comparators.h"
#include "util/StringPiece.h"
#include <algorithm>
@@ -258,12 +257,12 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef
}
for (const auto& configValue : entry->values) {
- processor.appendComment(configValue.value->getComment());
+ processor.appendComment(configValue->value->getComment());
}
// If this is an Attribute, append the format Javadoc.
if (!entry->values.empty()) {
- if (Attribute* attr = valueCast<Attribute>(entry->values.front().value.get())) {
+ if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
// We list out the available values for the given attribute.
addAttributeFormatDoc(&processor, attr);
}
@@ -272,7 +271,7 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef
if (type->type == ResourceType::kStyleable) {
assert(!entry->values.empty());
const Styleable* styleable = static_cast<const Styleable*>(
- entry->values.front().value.get());
+ entry->values.front()->value.get());
writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
unmangledName, styleable);
} else {
@@ -311,11 +310,10 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
if (type->type == ResourceType::kAttr) {
// Also include private attributes in this same class.
- auto iter = std::lower_bound(package->types.begin(), package->types.end(),
- ResourceType::kAttrPrivate, cmp::lessThanType);
- if (iter != package->types.end() && (*iter)->type == ResourceType::kAttrPrivate) {
+ ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
+ if (privType) {
result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), iter->get());
+ package.get(), privType);
if (!result) {
return false;
}
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index c7e603ea3774..459c330cfbdc 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -18,9 +18,7 @@
#include "ResourceTable.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-
#include "link/Linkers.h"
-#include "util/Comparators.h"
#include <algorithm>
#include <cassert>
@@ -31,7 +29,12 @@ bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDes
const int sdkVersionToGenerate) {
assert(sdkVersionToGenerate > config.sdkVersion);
const auto endIter = entry->values.end();
- auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
+ auto iter = entry->values.begin();
+ for (; iter != endIter; ++iter) {
+ if ((*iter)->config == config) {
+ break;
+ }
+ }
// The source config came from this list, so it should be here.
assert(iter != entry->values.end());
@@ -45,10 +48,10 @@ bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDes
// are no higher sdk level versions of this resource.
ConfigDescription tempConfig(config);
for (; iter != endIter; ++iter) {
- tempConfig.sdkVersion = iter->config.sdkVersion;
- if (tempConfig == iter->config) {
+ tempConfig.sdkVersion = (*iter)->config.sdkVersion;
+ if (tempConfig == (*iter)->config) {
// The two configs are the same, check the sdk version.
- return sdkVersionToGenerate < iter->config.sdkVersion;
+ return sdkVersionToGenerate < (*iter)->config.sdkVersion;
}
}
@@ -65,14 +68,14 @@ bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
for (auto& entry : type->entries) {
for (size_t i = 0; i < entry->values.size(); i++) {
- ResourceConfigValue& configValue = entry->values[i];
- if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ ResourceConfigValue* configValue = entry->values[i].get();
+ if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
// If this configuration is only used on L-MR1 then we don't need
// to do anything since we use private attributes since that version.
continue;
}
- if (Style* style = valueCast<Style>(configValue.value.get())) {
+ if (Style* style = valueCast<Style>(configValue->value.get())) {
Maybe<size_t> minSdkStripped;
std::vector<Style::Entry> stripped;
@@ -82,7 +85,7 @@ bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
// Find the SDK level that is higher than the configuration allows.
const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
- if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) {
+ if (sdkLevel > std::max<size_t>(configValue->config.sdkVersion, 1)) {
// Record that we are about to strip this.
stripped.emplace_back(std::move(*iter));
@@ -105,10 +108,10 @@ bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
// there is no other defined resource for the version we want to
// generate.
if (shouldGenerateVersionedResource(entry.get(),
- configValue.config,
+ configValue->config,
minSdkStripped.value())) {
// Let's create a new Style for this versioned resource.
- ConfigDescription newConfig(configValue.config);
+ ConfigDescription newConfig(configValue->config);
newConfig.sdkVersion = minSdkStripped.value();
std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
@@ -121,14 +124,8 @@ bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
std::make_move_iterator(stripped.end()));
// Insert the new Resource into the correct place.
- auto iter = std::lower_bound(entry->values.begin(),
- entry->values.end(),
- newConfig,
- cmp::lessThanConfig);
-
- entry->values.insert(
- iter,
- ResourceConfigValue{ newConfig, std::move(newStyle) });
+ entry->findOrCreateValue(newConfig, {})->value =
+ std::move(newStyle);
}
}
}
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 29bcc93518cf..9b3a87c4eed0 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -15,9 +15,7 @@
*/
#include "ConfigDescription.h"
-
#include "link/Linkers.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -31,9 +29,9 @@ TEST(AutoVersionerTest, GenerateVersionedResources) {
const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ landConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpLandConfig });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
@@ -45,9 +43,9 @@ TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
const ConfigDescription v21Config = test::parseConfigOrDie("v21");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpV13Config });
- entry.values.push_back(ResourceConfigValue{ v21Config });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpV13Config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8e321798787a..3437ac0d9a7f 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -31,6 +31,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ProductFilter.h"
#include "link/ReferenceLinker.h"
#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
@@ -70,6 +71,7 @@ struct LinkOptions {
Maybe<std::u16string> privateSymbols;
ManifestFixerOptions manifestFixerOptions;
IConfigFilter* configFilter = nullptr;
+ std::unordered_set<std::string> products;
};
struct LinkContext : public IAaptContext {
@@ -292,16 +294,16 @@ public:
for (const auto& configValue : entry->values) {
// Special case the occurrence of an ID that is being generated for the
// 'android' package. This is due to legacy reasons.
- if (valueCast<Id>(configValue.value.get()) &&
+ if (valueCast<Id>(configValue->value.get()) &&
package->name == u"android") {
mContext->getDiagnostics()->warn(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "generated id '" << resName
<< "' for external package '" << package->name
<< "'");
} else {
mContext->getDiagnostics()->error(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "defined resource '" << resName
<< "' for external package '" << package->name
<< "'");
@@ -512,7 +514,10 @@ public:
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
+ bool result = mFinalTable.addResourceAllowMangled(resName,
+ ConfigDescription::defaultConfig(),
+ std::string(),
+ std::move(id),
mContext->getDiagnostics());
if (!result) {
return false;
@@ -681,6 +686,12 @@ public:
mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
return 1;
}
+
+ ProductFilter productFilter(mOptions.products);
+ if (!productFilter.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
+ return 1;
+ }
}
proguard::KeepSet proguardKeepSet;
@@ -931,6 +942,7 @@ int link(const std::vector<StringPiece>& args) {
Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
Maybe<std::string> configs;
+ Maybe<std::string> productList;
bool legacyXFlag = false;
bool requireLocalization = false;
Flags flags = Flags()
@@ -954,6 +966,8 @@ int link(const std::vector<StringPiece>& args) {
&requireLocalization)
.optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
"is all configurations", &configs)
+ .optionalFlag("--product", "Comma separated list of product names to keep",
+ &productList)
.optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
"by -o",
&options.outputToDirectory)
@@ -1039,6 +1053,14 @@ int link(const std::vector<StringPiece>& args) {
}
}
+ if (productList) {
+ for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.toString());
+ }
+ }
+ }
+
AxisConfigFilter filter;
if (configs) {
for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 4d3a483c6b82..ec532aba465f 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -26,7 +26,7 @@
namespace aapt {
class ResourceTable;
-struct ResourceEntry;
+class ResourceEntry;
struct ConfigDescription;
/**
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
new file mode 100644
index 000000000000..8784e891b293
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#include "link/ProductFilter.h"
+
+namespace aapt {
+
+ProductFilter::ResourceConfigValueIter
+ProductFilter::selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag) {
+ ResourceConfigValueIter defaultProductIter = end;
+ ResourceConfigValueIter selectedProductIter = end;
+
+ for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
+ ResourceConfigValue* configValue = iter->get();
+ if (mProducts.find(configValue->product) != mProducts.end()) {
+ if (selectedProductIter != end) {
+ // We have two possible values for this product!
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "selection of product '" << configValue->product
+ << "' for resource " << name << " is ambiguous");
+
+ ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
+ diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
+ << "product '" << previouslySelectedConfigValue->product
+ << "' is also a candidate");
+ return end;
+ }
+
+ // Select this product.
+ selectedProductIter = iter;
+ }
+
+ if (configValue->product.empty() || configValue->product == "default") {
+ if (defaultProductIter != end) {
+ // We have two possible default values.
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "multiple default products defined for resource " << name);
+
+ ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
+ diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
+ << "default product also defined here");
+ return end;
+ }
+
+ // Mark the default.
+ defaultProductIter = iter;
+ }
+ }
+
+ if (defaultProductIter == end) {
+ diag->error(DiagMessage() << "no default product defined for resource " << name);
+ return end;
+ }
+
+ if (selectedProductIter == end) {
+ selectedProductIter = defaultProductIter;
+ }
+ return selectedProductIter;
+}
+
+bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
+ bool error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
+
+ ResourceConfigValueIter iter = entry->values.begin();
+ ResourceConfigValueIter startRangeIter = iter;
+ while (iter != entry->values.end()) {
+ ++iter;
+ if (iter == entry->values.end() ||
+ (*iter)->config != (*startRangeIter)->config) {
+
+ // End of the array, or we saw a different config,
+ // so this must be the end of a range of products.
+ // Select the product to keep from the set of products defined.
+ ResourceNameRef name(pkg->name, type->type, entry->name);
+ auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
+ context->getDiagnostics());
+ if (valueToKeep == iter) {
+ // An error occurred, we could not pick a product.
+ error = true;
+ } else {
+ // We selected a product to keep. Move it to the new array.
+ newValues.push_back(std::move(*valueToKeep));
+ }
+
+ // Start the next range of products.
+ startRangeIter = iter;
+ }
+ }
+
+ // Now move the new values in to place.
+ entry->values = std::move(newValues);
+ }
+ }
+ }
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
new file mode 100644
index 000000000000..d2edd38289dc
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LINK_PRODUCTFILTER_H
+#define AAPT_LINK_PRODUCTFILTER_H
+
+#include "ResourceTable.h"
+#include "process/IResourceTableConsumer.h"
+
+#include <android-base/macros.h>
+#include <unordered_set>
+
+namespace aapt {
+
+class ProductFilter {
+public:
+ using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
+
+ ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag);
+
+ bool consume(IAaptContext* context, ResourceTable* table);
+
+private:
+ std::unordered_set<std::string> mProducts;
+
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINK_PRODUCTFILTER_H */
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
new file mode 100644
index 000000000000..f4f756ae4519
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include "link/ProductFilter.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ProductFilterTest, SelectTwoProducts) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ const ConfigDescription land = test::parseConfigOrDie("land");
+ const ConfigDescription port = test::parseConfigOrDie("port");
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ land, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ land, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({ "tablet" });
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, "tablet"));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, "tablet"));
+}
+
+TEST(ProductFilterTest, SelectDefaultProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ "tablet"));
+}
+
+TEST(ProductFilterTest, FailOnAmbiguousProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "no-sdcard",
+ test::ValueBuilder<Id>()
+ .setSource(Source("no-sdcard.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({ "tablet", "no-sdcard" });
+ ASSERT_FALSE(filter.consume(context.get(), &table));
+}
+
+TEST(ProductFilterTest, FailOnMultipleDefaults) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source(".xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "default",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_FALSE(filter.consume(context.get(), &table));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 27435398c408..ef3fe4f58d41 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -316,7 +316,7 @@ bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
&table->stringPool, &declStack, &callSite);
for (auto& configValue : entry->values) {
- configValue.value->accept(&visitor);
+ configValue->value->accept(&visitor);
}
if (visitor.hasError()) {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index e01a00401133..2ecd5b018691 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -18,9 +18,7 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
#include "link/TableMerger.h"
-#include "util/Comparators.h"
#include "util/Util.h"
#include <cassert>
@@ -197,28 +195,28 @@ bool TableMerger::doMerge(const Source& src,
ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
- for (ResourceConfigValue& srcValue : srcEntry->values) {
- auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
- srcValue.config, cmp::lessThanConfig);
+ for (auto& srcValue : srcEntry->values) {
+ ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
+ srcValue->product);
const bool stripConfig = mOptions.filter ?
- !mOptions.filter->match(srcValue.config) : false;
+ !mOptions.filter->match(srcValue->config) : false;
- if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
+ if (dstValue) {
const int collisionResult = ResourceTable::resolveValueCollision(
- iter->value.get(), srcValue.value.get());
+ dstValue->value.get(), srcValue->value.get());
if (collisionResult == 0 && !overlay) {
// Error!
ResourceNameRef resourceName(srcPackage->name,
srcType->type,
srcEntry->name);
- mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
+ mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
<< "resource '" << resourceName
<< "' has a conflicting value for "
<< "configuration ("
- << srcValue.config << ")");
- mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
+ << srcValue->config << ")");
+ mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
<< "originally defined here");
error = true;
continue;
@@ -227,16 +225,18 @@ bool TableMerger::doMerge(const Source& src,
continue;
}
- } else if (!stripConfig){
- // Insert a place holder value. We will fill it in below.
- iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
}
if (stripConfig) {
continue;
}
- if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
+ if (!dstValue) {
+ // Force create the entry if we didn't have it.
+ dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
+ }
+
+ if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
std::unique_ptr<FileReference> newFileRef;
if (manglePackage) {
newFileRef = cloneAndMangleFile(srcPackage->name, *f);
@@ -246,15 +246,15 @@ bool TableMerger::doMerge(const Source& src,
}
if (callback) {
- if (!callback(resName, iter->config, newFileRef.get(), f)) {
+ if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
error = true;
continue;
}
}
- iter->value = std::move(newFileRef);
+ dstValue->value = std::move(newFileRef);
} else {
- iter->value = std::unique_ptr<Value>(srcValue.value->clone(
+ dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
&mMasterTable->stringPool));
}
}
@@ -290,7 +290,8 @@ bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, b
ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
pkg->findOrCreateType(fileDesc.name.type)
->findOrCreateEntry(fileDesc.name.entry)
- ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
+ ->findOrCreateValue(fileDesc.config, {})
+ ->value = std::move(fileRef);
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
FileReference* newFile, FileReference* oldFile) -> bool {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 6ad2f9c10d22..b6030a2874a3 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -17,9 +17,7 @@
#include "ConfigDescription.h"
#include "Resource.h"
#include "ValueVisitor.h"
-
#include "process/SymbolTable.h"
-#include "util/Comparators.h"
#include "util/Util.h"
#include <androidfw/AssetManager.h>
@@ -55,12 +53,10 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
- auto iter = std::lower_bound(sr.entry->values.begin(), sr.entry->values.end(),
- kDefaultConfig, cmp::lessThanConfig);
-
- if (iter != sr.entry->values.end() && iter->config == kDefaultConfig) {
+ ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
+ if (configValue) {
// This resource has an Attribute.
- if (Attribute* attr = valueCast<Attribute>(iter->value.get())) {
+ if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
symbol->attribute = util::make_unique<Attribute>(*attr);
} else {
return {};
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 1310aa6774bb..9856a00d7f67 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -19,7 +19,6 @@
#include "ValueVisitor.h"
#include "proto/ProtoHelpers.h"
#include "proto/ProtoSerialize.h"
-#include "util/Comparators.h"
#include <androidfw/ResourceTypes.h>
@@ -134,21 +133,19 @@ public:
return {};
}
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- config, cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == config) {
+ ResourceConfigValue* configValue = entry->findOrCreateValue(config,
+ pbConfig.product());
+ if (configValue->value) {
// Duplicate config.
mDiag->error(DiagMessage(mSource) << "duplicate configuration");
return {};
}
- std::unique_ptr<Value> value = deserializeValueFromPb(pbConfigValue.value(),
- config,
- &table->stringPool);
- if (!value) {
+ configValue->value = deserializeValueFromPb(pbConfigValue.value(),
+ config, &table->stringPool);
+ if (!configValue->value) {
return {};
}
- entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
}
}
}
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 4a2176d20db9..b3d87d805c6d 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -111,7 +111,7 @@ public:
serializeReferenceToPb(entry.key, pbEntry->mutable_key());
pb::Item* pbItem = pbEntry->mutable_item();
- serializeItemCommonToPb(*entry.value, pbEntry);
+ serializeItemCommonToPb(entry.key, pbEntry);
PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem);
entry.value->accept(&subVisitor);
}
@@ -242,21 +242,24 @@ std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) {
for (auto& configValue : entry->values) {
pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
- serializeConfig(configValue.config, pbConfigValue->mutable_config());
+ serializeConfig(configValue->config, pbConfigValue->mutable_config());
+ if (!configValue->product.empty()) {
+ pbConfigValue->mutable_config()->set_product(configValue->product);
+ }
pb::Value* pbValue = pbConfigValue->mutable_value();
- serializeSourceToPb(configValue.value->getSource(), &sourcePool,
+ serializeSourceToPb(configValue->value->getSource(), &sourcePool,
pbValue->mutable_source());
- if (!configValue.value->getComment().empty()) {
- pbValue->set_comment(util::utf16ToUtf8(configValue.value->getComment()));
+ if (!configValue->value->getComment().empty()) {
+ pbValue->set_comment(util::utf16ToUtf8(configValue->value->getComment()));
}
- if (configValue.value->isWeak()) {
+ if (configValue->value->isWeak()) {
pbValue->set_weak(true);
}
PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
- configValue.value->accept(&visitor);
+ configValue->value->accept(&visitor);
}
}
}
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 1061b8f76f57..70a33f795f87 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -49,9 +49,19 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one"));
ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"),
- ConfigDescription{}, std::move(plural),
+ ConfigDescription{}, std::string(), std::move(plural),
context->getDiagnostics()));
+ // Make a resource with different products.
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"),
+ test::parseConfigOrDie("land"), std::string(),
+ test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"),
+ test::parseConfigOrDie("land"), std::string("tablet"),
+ test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
+ context->getDiagnostics()));
+
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
ASSERT_NE(nullptr, pbTable);
@@ -69,6 +79,17 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
AAPT_ASSERT_TRUE(result);
EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state);
EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+
+ // Find the product-dependent values
+ BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
+ newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(123u, prim->value.data);
+
+ prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
+ newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(321u, prim->value.data);
}
TEST(TableProtoSerializer, SerializeFileHeader) {
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 579a46ec230f..834caf8b9a49 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -104,8 +104,8 @@ public:
const ConfigDescription& config,
std::unique_ptr<Value> value) {
ResourceName resName = parseNameOrDie(name);
- bool result = mTable->addResourceAllowMangled(resName, id, config, std::move(value),
- &mDiagnostics);
+ bool result = mTable->addResourceAllowMangled(resName, id, config, std::string(),
+ std::move(value), &mDiagnostics);
assert(result);
return *this;
}
@@ -132,6 +132,14 @@ inline std::unique_ptr<Reference> buildReference(const StringPiece16& ref,
return reference;
}
+inline std::unique_ptr<BinaryPrimitive> buildPrimitive(uint8_t type, uint32_t data) {
+ android::Res_value value = {};
+ value.size = sizeof(value);
+ value.dataType = type;
+ value.data = data;
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
template <typename T>
class ValueBuilder {
private:
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 51e2dd44e521..348c32a04e88 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -52,6 +52,11 @@ struct DummyDiagnosticsImpl : public IDiagnostics {
void note(const DiagMessage& message) override {}
};
+inline IDiagnostics* getDiagnostics() {
+ static DummyDiagnosticsImpl diag;
+ return &diag;
+}
+
inline ResourceName parseNameOrDie(const StringPiece16& str) {
ResourceNameRef ref;
bool result = ResourceUtils::tryParseReference(str, &ref);
@@ -66,23 +71,25 @@ inline ConfigDescription parseConfigOrDie(const StringPiece& str) {
return config;
}
-template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName,
- const ConfigDescription& config) {
+template <typename T> T* getValueForConfigAndProduct(ResourceTable* table,
+ const StringPiece16& resName,
+ const ConfigDescription& config,
+ const StringPiece& product) {
Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName));
if (result) {
- ResourceEntry* entry = result.value().entry;
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), config,
- [](const ResourceConfigValue& a, const ConfigDescription& b)
- -> bool {
- return a.config < b;
- });
- if (iter != entry->values.end() && iter->config == config) {
- return valueCast<T>(iter->value.get());
+ ResourceConfigValue* configValue = result.value().entry->findValue(config, product);
+ if (configValue) {
+ return valueCast<T>(configValue->value.get());
}
}
return nullptr;
}
+template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName,
+ const ConfigDescription& config) {
+ return getValueForConfigAndProduct<T>(table, resName, config, {});
+}
+
template <typename T> T* getValue(ResourceTable* table, const StringPiece16& resName) {
return getValueForConfig<T>(table, resName, {});
}
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 341770360068..33b505ed2eb4 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -352,7 +352,7 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
return false;
}
- if (!mTable->addResourceAllowMangled(name, config, std::move(resourceValue),
+ if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
mContext->getDiagnostics())) {
return false;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index c7b24bcb352d..b6588b65b5ae 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -288,8 +288,10 @@ public class FontFamily_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
+ /*package*/ static boolean nAddFontWeightStyle(long nativeFamily,
+ final String path, final int index, final List<FontListParser.Axis> axes,
final int weight, final boolean isItalic) {
+ // 'index' and 'axes' are not supported by java.awt.Font
final FontFamily_Delegate delegate = getDelegate(nativeFamily);
if (delegate != null) {
if (sFontLocation == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index d0dd22f8faad..a10ac00fc356 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -388,21 +388,18 @@ public final class Path_Delegate {
@LayoutlibDelegate
/*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
float bottom, float[] radii, int dir) {
- // Java2D doesn't support different rounded corners in each corner, so just use the
- // first value.
- native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
-
- // there can be a case where this API is used but with similar values for all corners, so
- // in that case we don't warn.
- // we only care if 2 corners are different so just compare to the next one.
- for (int i = 0 ; i < 3 ; i++) {
- if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Different corner sizes are not supported in Path.addRoundRect.",
- null, null /*data*/);
- break;
- }
+
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ float[] cornerDimensions = new float[radii.length];
+ for (int i = 0; i < radii.length; i++) {
+ cornerDimensions[i] = 2 * radii[i];
}
+ pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
+ cornerDimensions), false);
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
new file mode 100644
index 000000000000..edd36e54aa77
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RectangularShape;
+import java.awt.geom.RoundRectangle2D;
+import java.util.EnumSet;
+import java.util.NoSuchElementException;
+
+/**
+ * Defines a rectangle with rounded corners, where the sizes of the corners
+ * are potentially different.
+ */
+public class RoundRectangle extends RectangularShape {
+ public double x;
+ public double y;
+ public double width;
+ public double height;
+ public double ulWidth;
+ public double ulHeight;
+ public double urWidth;
+ public double urHeight;
+ public double lrWidth;
+ public double lrHeight;
+ public double llWidth;
+ public double llHeight;
+
+ private enum Zone {
+ CLOSE_OUTSIDE,
+ CLOSE_INSIDE,
+ MIDDLE,
+ FAR_INSIDE,
+ FAR_OUTSIDE
+ }
+
+ private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
+ private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
+
+ /**
+ * @param cornerDimensions array of 8 floating-point number corresponding to the width and
+ * the height of each corner in the following order: upper-left, upper-right, lower-right,
+ * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
+ * is that the width and height of a corner correspond to the total width and height of the
+ * ellipse that corner is a quarter of.
+ */
+ public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
+ if (cornerDimensions.length != 8) {
+ throw new IllegalArgumentException("The array of corner dimensions must have eight " +
+ "elements");
+ }
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ float[] dimensions = cornerDimensions.clone();
+ // If a value is negative, the corresponding corner is squared
+ for (int i = 0; i < dimensions.length; i += 2) {
+ if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
+ dimensions[i] = 0;
+ dimensions[i + 1] = 0;
+ }
+ }
+
+ double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
+ double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
+ double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
+ double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
+
+ // Rescale the corner dimensions if they are bigger than the rectangle
+ double scale = Math.min(1.0, width / topCornerWidth);
+ scale = Math.min(scale, width / bottomCornerWidth);
+ scale = Math.min(scale, height / leftCornerHeight);
+ scale = Math.min(scale, height / rightCornerHeight);
+
+ this.ulWidth = dimensions[0] * scale;
+ this.ulHeight = dimensions[1] * scale;
+ this.urWidth = dimensions[2] * scale;
+ this.urHeight = dimensions[3] * scale;
+ this.lrWidth = dimensions[4] * scale;
+ this.lrHeight = dimensions[5] * scale;
+ this.llWidth = dimensions[6] * scale;
+ this.llHeight = dimensions[7] * scale;
+ }
+
+ @Override
+ public double getX() {
+ return x;
+ }
+
+ @Override
+ public double getY() {
+ return y;
+ }
+
+ @Override
+ public double getWidth() {
+ return width;
+ }
+
+ @Override
+ public double getHeight() {
+ return height;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (width <= 0d) || (height <= 0d);
+ }
+
+ @Override
+ public void setFrame(double x, double y, double w, double h) {
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ }
+
+ @Override
+ public Rectangle2D getBounds2D() {
+ return new Rectangle2D.Double(x, y, width, height);
+ }
+
+ @Override
+ public boolean contains(double x, double y) {
+ if (isEmpty()) {
+ return false;
+ }
+
+ double x0 = getX();
+ double y0 = getY();
+ double x1 = x0 + getWidth();
+ double y1 = y0 + getHeight();
+ // Check for trivial rejection - point is outside bounding rectangle
+ if (x < x0 || y < y0 || x >= x1 || y >= y1) {
+ return false;
+ }
+
+ double insideTopX0 = x0 + ulWidth / 2d;
+ double insideLeftY0 = y0 + ulHeight / 2d;
+ if (x < insideTopX0 && y < insideLeftY0) {
+ // In the upper-left corner
+ return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
+ }
+
+ double insideTopX1 = x1 - urWidth / 2d;
+ double insideRightY0 = y0 + urHeight / 2d;
+ if (x > insideTopX1 && y < insideRightY0) {
+ // In the upper-right corner
+ return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
+ }
+
+ double insideBottomX1 = x1 - lrWidth / 2d;
+ double insideRightY1 = y1 - lrHeight / 2d;
+ if (x > insideBottomX1 && y > insideRightY1) {
+ // In the lower-right corner
+ return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
+ lrHeight / 2d);
+ }
+
+ double insideBottomX0 = x0 + llWidth / 2d;
+ double insideLeftY1 = y1 - llHeight / 2d;
+ if (x < insideBottomX0 && y > insideLeftY1) {
+ // In the lower-left corner
+ return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
+ llHeight / 2d);
+ }
+
+ // In the central part of the rectangle
+ return true;
+ }
+
+ private boolean isInsideCorner(double x, double y, double width, double height) {
+ double squareDist = height * height * x * x + width * width * y * y;
+ return squareDist <= width * width * height * height;
+ }
+
+ private Zone classify(double coord, double side1, double arcSize1, double side2,
+ double arcSize2) {
+ if (coord < side1) {
+ return Zone.CLOSE_OUTSIDE;
+ } else if (coord < side1 + arcSize1) {
+ return Zone.CLOSE_INSIDE;
+ } else if (coord < side2 - arcSize2) {
+ return Zone.MIDDLE;
+ } else if (coord < side2) {
+ return Zone.FAR_INSIDE;
+ } else {
+ return Zone.FAR_OUTSIDE;
+ }
+ }
+
+ public boolean intersects(double x, double y, double w, double h) {
+ if (isEmpty() || w <= 0 || h <= 0) {
+ return false;
+ }
+ double x0 = getX();
+ double y0 = getY();
+ double x1 = x0 + getWidth();
+ double y1 = y0 + getHeight();
+ // Check for trivial rejection - bounding rectangles do not intersect
+ if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
+ return false;
+ }
+
+ double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
+ double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
+ double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
+ double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
+ Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+ Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+ Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+ Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+
+ // Trivially accept if any point is inside inner rectangle
+ if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
+ return true;
+ }
+ // Trivially accept if either edge spans inner rectangle
+ if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
+ far.contains(y1class))) {
+ return true;
+ }
+
+ // Since neither edge spans the center, then one of the corners
+ // must be in one of the rounded edges. We detect this case if
+ // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases
+ // must be true for each direction.
+ // We now find a "nearest point" to test for being inside a rounded
+ // corner.
+ if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
+ // Potentially in upper-left corner
+ x = x + w - x0 - ulWidth / 2d;
+ y = y + h - y0 - ulHeight / 2d;
+ return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
+ }
+ if (x1class == Zone.CLOSE_INSIDE) {
+ // Potentially in lower-left corner
+ x = x + w - x0 - llWidth / 2d;
+ y = y - y1 + llHeight / 2d;
+ return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
+ }
+ if (y1class == Zone.CLOSE_INSIDE) {
+ //Potentially in the upper-right corner
+ x = x - x1 + urWidth / 2d;
+ y = y + h - y0 - urHeight / 2d;
+ return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
+ }
+ // Potentially in the lower-right corner
+ x = x - x1 + lrWidth / 2d;
+ y = y - y1 + lrHeight / 2d;
+ return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
+ }
+
+ @Override
+ public boolean contains(double x, double y, double w, double h) {
+ if (isEmpty() || w <= 0 || h <= 0) {
+ return false;
+ }
+ return (contains(x, y) &&
+ contains(x + w, y) &&
+ contains(x, y + h) &&
+ contains(x + w, y + h));
+ }
+
+ @Override
+ public PathIterator getPathIterator(final AffineTransform at) {
+ return new PathIterator() {
+ int index;
+
+ // ArcIterator.btan(Math.PI/2)
+ public static final double CtrlVal = 0.5522847498307933;
+ private final double ncv = 1.0 - CtrlVal;
+
+ // Coordinates of control points for Bezier curves approximating the straight lines
+ // and corners of the rounded rectangle.
+ private final double[][] ctrlpts = {
+ {0.0, 0.0, 0.0, ulHeight},
+ {0.0, 0.0, 1.0, -llHeight},
+ {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
+ 1.0, 0.0},
+ {1.0, -lrWidth, 1.0, 0.0},
+ {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
+ -lrHeight},
+ {1.0, 0.0, 0.0, urHeight},
+ {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
+ 0.0, 0.0},
+ {0.0, ulWidth, 0.0, 0.0},
+ {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
+ ulHeight},
+ {}
+ };
+ private final int[] types = {
+ SEG_MOVETO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_LINETO, SEG_CUBICTO,
+ SEG_CLOSE,
+ };
+
+ @Override
+ public int getWindingRule() {
+ return WIND_NON_ZERO;
+ }
+
+ @Override
+ public boolean isDone() {
+ return index >= ctrlpts.length;
+ }
+
+ @Override
+ public void next() {
+ index++;
+ }
+
+ @Override
+ public int currentSegment(float[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("roundrect iterator out of bounds");
+ }
+ int nc = 0;
+ double ctrls[] = ctrlpts[index];
+ for (int i = 0; i < ctrls.length; i += 4) {
+ coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
+ coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
+ }
+ if (at != null) {
+ at.transform(coords, 0, coords, 0, nc / 2);
+ }
+ return types[index];
+ }
+
+ @Override
+ public int currentSegment(double[] coords) {
+ if (isDone()) {
+ throw new NoSuchElementException("roundrect iterator out of bounds");
+ }
+ int nc = 0;
+ double ctrls[] = ctrlpts[index];
+ for (int i = 0; i < ctrls.length; i += 4) {
+ coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
+ coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
+ }
+ if (at != null) {
+ at.transform(coords, 0, coords, 0, nc / 2);
+ }
+ return types[index];
+ }
+ };
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 7b8e29a03d86..fe05b0e91e83 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -236,4 +236,9 @@ public final class BridgeWindowSession implements IWindowSession {
public void prepareToReplaceChildren(IBinder appToken) {
// pass for now.
}
+
+ @Override
+ public void updatePointerIcon(IWindow window) {
+ // pass for now.
+ }
}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 9137d9d90439..7f1ae2471a8d 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -23,7 +23,7 @@ import java.util.concurrent.CountDownLatch;
@SystemApi
public class RttManager {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String TAG = "RttManager";
/** @deprecated It is Not supported anymore. */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2ad3c2e16e21..4921073cce6c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@ import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -892,6 +893,24 @@ public class WifiManager {
}
/**
+ * Sets whether or not the given network is metered from a network policy
+ * point of view. A network should be classified as metered when the user is
+ * sensitive to heavy data usage on that connection due to monetary costs,
+ * data limitations or battery/performance issues. A typical example would
+ * be a wifi connection where the user was being charged for usage.
+ * @param netId the integer that identifies the network configuration
+ * to the supplicant.
+ * @param isMetered True to mark the network as metered.
+ * @return {@code true} if the operation succeeded.
+ * @hide
+ */
+ @SystemApi
+ public boolean setMetered(int netId, boolean isMetered) {
+ // TODO(jjoslin): Implement
+ return false;
+ }
+
+ /**
* Remove the specified network from the list of configured networks.
* This may result in the asynchronous delivery of state change
* events.
@@ -1301,13 +1320,15 @@ public class WifiManager {
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get valid results.
+ * in order to get valid results. If there is a remote exception (e.g., either a communication
+ * problem with the system service or an exception within the framework) an empty list will be
+ * returned.
*/
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName());
} catch (RemoteException e) {
- return null;
+ return new ArrayList<ScanResult>();
}
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index 5c18bd7e0f07..9e6ed4ee9634 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -36,7 +36,7 @@ import android.util.Log;
*/
public class WifiNanEventListener {
private static final String TAG = "WifiNanEventListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index cb82268ec195..667c4b1de6d0 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -38,7 +38,7 @@ import android.util.Log;
*/
public class WifiNanManager {
private static final String TAG = "WifiNanManager";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
private IBinder mBinder;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index d0a94109d0d8..bc1787fee478 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -27,7 +27,7 @@ import android.util.Log;
*/
public class WifiNanSession {
private static final String TAG = "WifiNanSession";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index 092508766570..b9af7def6868 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -43,7 +43,7 @@ import android.util.Log;
*/
public class WifiNanSessionListener {
private static final String TAG = "WifiNanSessionListener";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**