summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java3
-rw-r--r--apex/statsd/framework/Android.bp23
-rw-r--r--api/current.txt83
-rwxr-xr-xapi/system-current.txt44
-rw-r--r--cmds/statsd/src/atoms.proto19
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java2
-rw-r--r--core/java/android/app/AppOpsManager.java16
-rw-r--r--core/java/android/app/AsyncNotedAppOp.java4
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl5
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/SystemServiceRegistry.java11
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java18
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java36
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java8
-rw-r--r--core/java/android/hardware/display/DeviceProductInfo.java224
-rw-r--r--core/java/android/os/IThermalService.aidl7
-rw-r--r--core/java/android/os/PowerManager.java160
-rw-r--r--core/java/android/os/UserManager.java18
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java19
-rw-r--r--core/java/android/provider/Settings.java38
-rw-r--r--core/java/android/service/notification/ConversationChannelWrapper.java28
-rw-r--r--core/java/android/view/CutoutSpecification.java22
-rw-r--r--core/java/android/view/DisplayInfo.java14
-rw-r--r--core/java/android/view/InputDevice.java16
-rw-r--r--core/java/android/view/Surface.java47
-rw-r--r--core/java/android/view/SurfaceControl.java17
-rw-r--r--core/java/android/view/SurfaceView.java34
-rw-r--r--core/java/android/view/View.java41
-rw-r--r--core/java/android/view/ViewGroup.java51
-rw-r--r--core/java/android/view/ViewParent.java14
-rw-r--r--core/java/android/view/ViewRootImpl.java30
-rw-r--r--core/java/android/view/WindowlessWindowManager.java9
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java205
-rw-r--r--core/java/android/widget/TextView.java43
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java22
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java7
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java5
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java40
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java5
-rw-r--r--core/java/com/android/internal/policy/DockedDividerUtils.java39
-rw-r--r--core/jni/android_view_Surface.cpp11
-rw-r--r--core/jni/android_view_SurfaceControl.cpp93
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp102
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/proto/android/service/graphicsstats.proto5
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto7
-rw-r--r--core/res/AndroidManifest.xml34
-rw-r--r--core/res/res/layout/chooser_grid.xml8
-rw-r--r--core/res/res/layout/resolver_list.xml12
-rw-r--r--core/res/res/layout/resolver_list_per_profile.xml1
-rw-r--r--core/res/res/layout/resolver_list_with_default.xml13
-rw-r--r--core/res/res/values/strings.xml13
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java22
-rw-r--r--core/tests/coretests/src/android/view/CutoutSpecificationTest.java106
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--libs/hwui/protos/graphicsstats.proto5
-rw-r--r--native/android/surface_control.cpp13
-rw-r--r--packages/OsuLogin/AndroidManifest.xml4
-rw-r--r--packages/OsuLogin/res/layout/osu_web_view.xml2
-rw-r--r--packages/OsuLogin/src/com/android/hotspot2/osulogin/OsuLoginActivity.java (renamed from packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java)8
-rw-r--r--packages/SystemUI/res/layout/qs_carrier.xml4
-rw-r--r--packages/SystemUI/res/layout/qs_carrier_group.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/TransactionPool.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java (renamed from packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java (renamed from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java (renamed from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java)57
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java451
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java441
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java310
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java159
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java322
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java278
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java260
-rw-r--r--services/Android.bp10
-rw-r--r--services/api/lint-baseline.txt35
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java20
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java20
-rw-r--r--services/core/java/com/android/server/SensorNotificationService.java10
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java59
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java19
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java19
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java67
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkRanker.java50
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java11
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java14
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java73
-rw-r--r--services/core/java/com/android/server/location/CountryDetectorBase.java8
-rw-r--r--services/core/java/com/android/server/location/LocationFudger.java7
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java21
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java40
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java15
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java27
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java13
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java247
-rw-r--r--services/core/java/com/android/server/twilight/TwilightService.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java77
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java126
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java182
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java12
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java2
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java9
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java32
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskTile.java34
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java94
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java2
-rw-r--r--services/people/java/com/android/server/people/SessionInfo.java6
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java26
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java12
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java5
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java89
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java109
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java16
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java366
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java11
-rw-r--r--tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt30
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java71
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java20
-rwxr-xr-xwifi/java/android/net/wifi/WifiOemMigrationHook.java266
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java11
192 files changed, 5550 insertions, 2557 deletions
diff --git a/Android.bp b/Android.bp
index b3c9e2a720f6..add492fb32c5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1327,7 +1327,6 @@ java_library {
libs: [
"framework-minus-apex",
"unsupportedappusage",
- "ike-stubs",
],
static_libs: [
"libphonenumber-platform",
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index d4ceabda8acc..7b3764a999f7 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -334,7 +334,8 @@ public class BlobStoreManagerService extends SystemService {
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
- if (leaseExpiryTimeMillis != 0 && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
+ if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
+ && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
throw new IllegalArgumentException(
"Lease expiry cannot be later than blobs expiry time");
}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index ab669d4ac45a..5533ed850321 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -48,17 +48,8 @@ filegroup {
java_defaults {
name: "framework-statsd-defaults",
-
- // TODO(b/146757305): Use "module_current" once it's ready.
- sdk_version: "core_current",
-
- libs: [
- "framework-annotations-lib",
-
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current" or "module_current"
- "android_module_lib_stubs_current",
- ],
+ sdk_version: "module_current",
+ libs: [ "framework-annotations-lib" ],
}
java_library {
@@ -72,16 +63,6 @@ java_library {
":framework-statsd-sources",
],
- aidl: {
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current" or "module_current"
- include_dirs: [
- // To refer:
- // android.app.PendintIntent
- "frameworks/base/core/java",
- ],
- },
-
permitted_packages: [
"android.app",
"android.os",
diff --git a/api/current.txt b/api/current.txt
index 8ed4b9f5d020..20933e4ad762 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9,6 +9,7 @@ package android {
ctor public Manifest.permission();
field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
+ field public static final String ACCESS_CALL_AUDIO = "android.permission.ACCESS_CALL_AUDIO";
field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
field public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -36633,6 +36634,7 @@ package android.os {
method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
+ method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
@@ -40585,13 +40587,13 @@ package android.provider {
field public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
- field public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
- field public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
+ field @Deprecated public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
field public static final String WIFI_ON = "wifi_on";
- field public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
- field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
- field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
- field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
+ field @Deprecated public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
field public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale";
}
@@ -46813,59 +46815,6 @@ package android.telephony {
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = "ims.wifi_off_deferring_time_int";
}
- public static final class CarrierConfigManager.Iwlan {
- field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
- field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
- field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
- field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
- field public static final int DH_GROUP_NONE = 0; // 0x0
- field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3
- field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12
- field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
- field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
- field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0
- field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
- field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
- field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int";
- field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
- field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array";
- field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_encryption_aes_ctr_key_size_int_array";
- field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
- field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
- field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array";
- field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int";
- field public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING = "iwlan.epdg_static_address_roaming_string";
- field public static final String KEY_EPDG_STATIC_ADDRESS_STRING = "iwlan.epdg_static_address_string";
- field public static final String KEY_IKE_FRAGMENTATION_ENABLED_BOOL = "iwlan.ike_fragmentation_enabled_bool";
- field public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT = "iwlan.ike_rekey_hard_timer_in_sec";
- field public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT = "iwlan.ike_rekey_soft_timer_sec_int";
- field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
- field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_aes_ctr_key_size_int_array";
- field public static final int KEY_LEN_AES_128 = 128; // 0x80
- field public static final int KEY_LEN_AES_192 = 192; // 0xc0
- field public static final int KEY_LEN_AES_256 = 256; // 0x100
- field public static final int KEY_LEN_UNUSED = 0; // 0x0
- field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
- field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
- field public static final String KEY_NATT_ENABLED_BOOL = "iwlan.natt_enabled_bool";
- field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
- field public static final String KEY_PREFIX = "iwlan.";
- field public static final String KEY_RETRANSMIT_TIMER_SEC_INT = "iwlan.retransmit_timer_sec_int";
- field public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_encryption_algorithms_int_array";
- field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
- field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
- field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
- field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
- field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
- }
-
public abstract class CellIdentity implements android.os.Parcelable {
method public int describeContents();
method @Nullable public CharSequence getOperatorAlphaLong();
@@ -53240,11 +53189,13 @@ package android.view {
method public android.graphics.Canvas lockHardwareCanvas();
method public void readFromParcel(android.os.Parcel);
method public void release();
- method public void setFrameRate(@FloatRange(from=0.0) float);
+ method public void setFrameRate(@FloatRange(from=0.0) float, int);
method @Deprecated public void unlockCanvas(android.graphics.Canvas);
method public void unlockCanvasAndPost(android.graphics.Canvas);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.Surface> CREATOR;
+ field public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0; // 0x0
+ field public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1; // 0x1
field public static final int ROTATION_0 = 0; // 0x0
field public static final int ROTATION_180 = 2; // 0x2
field public static final int ROTATION_270 = 3; // 0x3
@@ -53284,7 +53235,7 @@ package android.view {
method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
- method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
@@ -53892,6 +53843,7 @@ package android.view {
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public final void requestUnbufferedDispatch(android.view.MotionEvent);
+ method public final void requestUnbufferedDispatch(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
method public void resetPivot();
method public static int resolveSize(int, int);
@@ -55605,6 +55557,7 @@ package android.view.accessibility {
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
method public int getInputType();
@@ -55757,6 +55710,7 @@ package android.view.accessibility {
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
@@ -55844,6 +55798,12 @@ package android.view.accessibility {
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
}
+ public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
+ method @Nullable public android.util.Size getLayoutParams();
+ method public float getTextSizeInPx();
+ method public int getTextSizeUnit();
+ }
+
public static final class AccessibilityNodeInfo.RangeInfo {
ctor public AccessibilityNodeInfo.RangeInfo(int, float, float, float);
method public float getCurrent();
@@ -60826,6 +60786,7 @@ package android.widget {
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleLeft();
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleRight();
method @android.view.ViewDebug.ExportedProperty(category="text") public float getTextSize();
+ method public int getTextSizeUnit();
method public int getTotalPaddingBottom();
method public int getTotalPaddingEnd();
method public int getTotalPaddingLeft();
diff --git a/api/system-current.txt b/api/system-current.txt
index f974380f72c3..98c1f0d6367c 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -118,6 +118,7 @@ package android {
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+ field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
@@ -375,6 +376,7 @@ package android.app {
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
+ field public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio";
field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -887,7 +889,7 @@ package android.app.admin {
field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
- field public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -7536,6 +7538,7 @@ package android.net.wifi {
method public int getChannel();
method public int getMaxNumberOfClients();
method public int getShutdownTimeoutMillis();
+ method public boolean isAutoShutdownEnabled();
method public boolean isClientControlByUserEnabled();
method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
field public static final int BAND_2GHZ = 1; // 0x1
@@ -7549,14 +7552,15 @@ package android.net.wifi {
ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
method @NonNull public android.net.wifi.SoftApConfiguration build();
method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
}
@@ -7750,6 +7754,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
@@ -7897,6 +7902,7 @@ package android.net.wifi {
public final class WifiOemMigrationHook {
method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
+ method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
}
public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
@@ -7914,6 +7920,31 @@ package android.net.wifi {
method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
}
+ public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getP2pDeviceName();
+ method public boolean isP2pFactoryResetPending();
+ method public boolean isScanAlwaysAvailable();
+ method public boolean isScanThrottleEnabled();
+ method public boolean isSoftApTimeoutEnabled();
+ method public boolean isVerboseLoggingEnabled();
+ method public boolean isWakeUpEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR;
+ }
+
+ public static final class WifiOemMigrationHook.SettingsMigrationData.Builder {
+ ctor public WifiOemMigrationHook.SettingsMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
+ }
+
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -9720,18 +9751,11 @@ package android.provider {
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
- field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
field public static final String TETHER_SUPPORTED = "tether_supported";
field public static final String THEATER_MODE_ON = "theater_mode_on";
field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
field public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
- field public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
- field public static final String WIFI_P2P_PENDING_FACTORY_RESET = "wifi_p2p_pending_factory_reset";
- field public static final String WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_enabled";
- field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
- field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
- field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bf6afe7f2c0f..a4e8fdc10a3b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8231,14 +8231,6 @@ message DangerousPermissionStateSampled {
}
/**
- * HWUI renders pipeline type: GL (0) or Vulkan (1).
- */
-enum PipelineType {
- GL = 0;
- VULKAN = 1;
-}
-
-/**
* HWUI stats for a given app.
*/
message GraphicsStats {
@@ -8251,9 +8243,16 @@ message GraphicsStats {
// The start & end timestamps in UTC as
// milliseconds since January 1, 1970
// Compatible with java.util.Date#setTime()
- optional int64 stats_start = 3;
+ optional int64 start_millis = 3;
- optional int64 stats_end = 4;
+ optional int64 end_millis = 4;
+
+ // HWUI renders pipeline type: GL (1) or Vulkan (2).
+ enum PipelineType {
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
+ }
// HWUI renders pipeline type: GL or Vulkan.
optional PipelineType pipeline = 5;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 83d609cd5648..0a138cf81a4c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -960,7 +960,7 @@ public abstract class AccessibilityService extends Service {
return false;
}
List<GestureDescription.GestureStep> steps =
- MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
+ MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 16);
try {
synchronized (mLock) {
mGestureStatusCallbackSequence++;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9ed479840750..8f02f1555edf 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1035,10 +1035,12 @@ public class AppOpsManager {
public static final int OP_ACTIVATE_PLATFORM_VPN = 94;
/** @hide */
public static final int OP_LOADER_USAGE_STATS = 95;
+ /** @hide Access telephony call audio */
+ public static final int OP_ACCESS_CALL_AUDIO = 96;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 96;
+ public static final int _NUM_OP = 97;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1329,6 +1331,9 @@ public class AppOpsManager {
@SystemApi
public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
"android:manage_external_storage";
+ /** @hide Access telephony call audio */
+ @SystemApi
+ public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio";
/** @hide Communicate cross-profile within the same profile group. */
@SystemApi
@@ -1418,6 +1423,7 @@ public class AppOpsManager {
OP_MANAGE_EXTERNAL_STORAGE,
OP_INTERACT_ACROSS_PROFILES,
OP_LOADER_USAGE_STATS,
+ OP_ACCESS_CALL_AUDIO,
};
/**
@@ -1525,6 +1531,7 @@ public class AppOpsManager {
OP_INTERACT_ACROSS_PROFILES, //INTERACT_ACROSS_PROFILES
OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN
OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS
+ OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO
};
/**
@@ -1627,6 +1634,7 @@ public class AppOpsManager {
OPSTR_INTERACT_ACROSS_PROFILES,
OPSTR_ACTIVATE_PLATFORM_VPN,
OPSTR_LOADER_USAGE_STATS,
+ OPSTR_ACCESS_CALL_AUDIO,
};
/**
@@ -1730,6 +1738,7 @@ public class AppOpsManager {
"INTERACT_ACROSS_PROFILES",
"ACTIVATE_PLATFORM_VPN",
"LOADER_USAGE_STATS",
+ "ACCESS_CALL_AUDIO",
};
/**
@@ -1834,6 +1843,7 @@ public class AppOpsManager {
android.Manifest.permission.INTERACT_ACROSS_PROFILES,
null, // no permission for OP_ACTIVATE_PLATFORM_VPN
android.Manifest.permission.LOADER_USAGE_STATS,
+ Manifest.permission.ACCESS_CALL_AUDIO,
};
/**
@@ -1938,6 +1948,7 @@ public class AppOpsManager {
null, // INTERACT_ACROSS_PROFILES
null, // ACTIVATE_PLATFORM_VPN
null, // LOADER_USAGE_STATS
+ null, // ACCESS_CALL_AUDIO
};
/**
@@ -2041,6 +2052,7 @@ public class AppOpsManager {
false, // INTERACT_ACROSS_PROFILES
false, // ACTIVATE_PLATFORM_VPN
false, // LOADER_USAGE_STATS
+ false, // ACCESS_CALL_AUDIO
};
/**
@@ -2143,6 +2155,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
+ AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO
};
/**
@@ -2249,6 +2262,7 @@ public class AppOpsManager {
false, // INTERACT_ACROSS_PROFILES
false, // ACTIVATE_PLATFORM_VPN
false, // LOADER_USAGE_STATS
+ false, // ACCESS_CALL_AUDIO
};
/**
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 130e2ecdcc83..6b1afdad82df 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@ public final class AsyncNotedAppOp implements Parcelable {
};
@DataClass.Generated(
- time = 1580158740502L,
+ time = 1581728574427L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=95L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=96L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 180507cd7e9c..3c475c1a8083 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -315,11 +315,6 @@ interface IActivityTaskManager {
void positionTaskInStack(int taskId, int stackId, int position);
void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
- /**
- * Dismisses split-screen multi-window mode.
- * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
- */
- void dismissSplitScreenMode(boolean toTop);
/**
* Dismisses PiP
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 526c0b3f87ee..948546b18473 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@ interface INotificationManager
void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+ ParceledListSlice getConversations(boolean onlyImportant);
ParceledListSlice getConversationsForPackage(String pkg, int uid);
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 655dd9b41c34..f1559f79996e 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -148,6 +148,7 @@ import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
import android.os.ISystemUpdateManager;
+import android.os.IThermalService;
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
@@ -576,10 +577,12 @@ public final class SystemServiceRegistry {
new CachedServiceFetcher<PowerManager>() {
@Override
public PowerManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
- IPowerManager service = IPowerManager.Stub.asInterface(b);
- return new PowerManager(ctx.getOuterContext(),
- service, ctx.mMainThread.getHandler());
+ IBinder powerBinder = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
+ IPowerManager powerService = IPowerManager.Stub.asInterface(powerBinder);
+ IBinder thermalBinder = ServiceManager.getServiceOrThrow(Context.THERMAL_SERVICE);
+ IThermalService thermalService = IThermalService.Stub.asInterface(thermalBinder);
+ return new PowerManager(ctx.getOuterContext(), powerService, thermalService,
+ ctx.mMainThread.getHandler());
}});
registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d08dbc609332..139b1796d8fc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1416,6 +1416,7 @@ public class DevicePolicyManager {
* @see #setFactoryResetProtectionPolicy
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION)
@SystemApi
public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED =
"android.app.action.RESET_PROTECTION_POLICY_CHANGED";
@@ -11989,4 +11990,21 @@ public class DevicePolicyManager {
}
return 0;
}
+
+ /**
+ * Returns {@code true} when {@code userId} has a profile owner that is capable of resetting
+ * password in RUNNING_LOCKED state. For that it should have at least one direct boot aware
+ * component and have an active password reset token. Can only be called by the system.
+ * @hide
+ */
+ public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
+ if (mService != null) {
+ try {
+ return mService.canProfileOwnerResetPasswordWhenLocked(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 84332ca466ef..25c1e1205d2e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -478,4 +478,5 @@ interface IDevicePolicyManager {
long getManagedProfileMaximumTimeOff(in ComponentName admin);
void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
+ boolean canProfileOwnerResetPasswordWhenLocked(in int userId);
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5b60b85f4721..3e1a480b4f66 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -25,6 +25,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Handler;
@@ -1299,6 +1300,31 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
+ private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
+ "cache_key.bluetooth.get_bond_state";
+ private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
+ new PropertyInvalidatedCache<BluetoothDevice, Integer>(
+ 8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
+ @Override
+ protected Integer recompute(BluetoothDevice query) {
+ try {
+ return sService.getBondState(query);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+
+ /** @hide */
+ public void disableBluetoothGetBondStateCache() {
+ mBluetoothBondCache.disableLocal();
+ }
+
+ /** @hide */
+ public static void invalidateBluetoothGetBondStateCache() {
+ PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
+ }
+
/**
* Get the bond state of the remote device.
* <p>Possible values for the bond state are:
@@ -1316,9 +1342,13 @@ public final class BluetoothDevice implements Parcelable {
return BOND_NONE;
}
try {
- return service.getBondState(this);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
+ return mBluetoothBondCache.query(this);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof RemoteException) {
+ Log.e(TAG, "", e);
+ } else {
+ throw e;
+ }
}
return BOND_NONE;
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a69905eb3de4..3a934211c7e5 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.LocusId;
import android.content.pm.LauncherApps.ShortcutQuery;
@@ -92,4 +93,11 @@ public abstract class ShortcutServiceInternal {
public abstract void uncacheShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
+
+ /**
+ * Retrieves all of the direct share targets that match the given IntentFilter for the specified
+ * user.
+ */
+ public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+ @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
}
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
new file mode 100644
index 000000000000..6ad7faed16d8
--- /dev/null
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2020 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.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ * @hide
+ */
+public final class DeviceProductInfo implements Parcelable {
+ final private String mName;
+ final private String mManufacturerPnpId;
+ final private String mProductId;
+ final private Integer mModelYear;
+ final private ManufactureDate mManufactureDate;
+
+ public DeviceProductInfo(
+ String name,
+ String manufacturerPnpId,
+ String productCode,
+ Integer modelYear,
+ ManufactureDate manufactureDate) {
+ this.mName = name;
+ this.mManufacturerPnpId = manufacturerPnpId;
+ this.mProductId = productCode;
+ this.mModelYear = modelYear;
+ this.mManufactureDate = manufactureDate;
+ }
+
+ private DeviceProductInfo(Parcel in) {
+ mName = in.readString();
+ mManufacturerPnpId = in.readString();
+ mProductId = (String) in.readValue(null);
+ mModelYear = (Integer) in.readValue(null);
+ mManufactureDate = (ManufactureDate) in.readValue(null);
+ }
+
+ /**
+ * @return Display name.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * @return Manufacturer Plug and Play ID.
+ */
+ public String getManufacturerPnpId() {
+ return mManufacturerPnpId;
+ }
+
+ /**
+ * @return Manufacturer product ID.
+ */
+ public String getProductId() {
+ return mProductId;
+ }
+
+ /**
+ * @return Model year of the device. Typically exactly one of model year or
+ * manufacture date will be present.
+ */
+ public Integer getModelYear() {
+ return mModelYear;
+ }
+
+ /**
+ * @return Manufacture date. Typically exactly one of model year or manufacture
+ * date will be present.
+ */
+ public ManufactureDate getManufactureDate() {
+ return mManufactureDate;
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceProductInfo{"
+ + "name="
+ + mName
+ + ", manufacturerPnpId="
+ + mManufacturerPnpId
+ + ", productId="
+ + mProductId
+ + ", modelYear="
+ + mModelYear
+ + ", manufactureDate="
+ + mManufactureDate
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceProductInfo that = (DeviceProductInfo) o;
+ return Objects.equals(mName, that.mName)
+ && Objects.equals(mManufacturerPnpId, that.mManufacturerPnpId)
+ && Objects.equals(mProductId, that.mProductId)
+ && Objects.equals(mModelYear, that.mModelYear)
+ && Objects.equals(mManufactureDate, that.mManufactureDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate);
+ }
+
+ public static final Creator<DeviceProductInfo> CREATOR =
+ new Creator<DeviceProductInfo>() {
+ @Override
+ public DeviceProductInfo createFromParcel(Parcel in) {
+ return new DeviceProductInfo(in);
+ }
+
+ @Override
+ public DeviceProductInfo[] newArray(int size) {
+ return new DeviceProductInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mManufacturerPnpId);
+ dest.writeValue(mProductId);
+ dest.writeValue(mModelYear);
+ dest.writeValue(mManufactureDate);
+ }
+
+ /**
+ * Stores information about the date of manufacture.
+ *
+ * @hide
+ */
+ public static class ManufactureDate implements Parcelable {
+ final private Integer mWeek;
+ final private Integer mYear;
+
+ public ManufactureDate(Integer week, Integer year) {
+ mWeek = week;
+ mYear = year;
+ }
+
+ protected ManufactureDate(Parcel in) {
+ mWeek = (Integer) in.readValue(null);
+ mYear = (Integer) in.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeValue(mWeek);
+ dest.writeValue(mYear);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ManufactureDate> CREATOR =
+ new Creator<ManufactureDate>() {
+ @Override
+ public ManufactureDate createFromParcel(Parcel in) {
+ return new ManufactureDate(in);
+ }
+
+ @Override
+ public ManufactureDate[] newArray(int size) {
+ return new ManufactureDate[size];
+ }
+ };
+
+ public int getYear() {
+ return mYear;
+ }
+
+ public int getWeek() {
+ return mWeek;
+ }
+
+ @Override
+ public String toString() {
+ return "ManufactureDate{week=" + mWeek + ", year=" + mYear + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ManufactureDate that = (ManufactureDate) o;
+ return Objects.equals(mWeek, that.mWeek) && Objects.equals(mYear, that.mYear);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWeek, mYear);
+ }
+ }
+}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8c989607e8db..ad002335a010 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -103,4 +103,11 @@ interface IThermalService {
* {@hide}
*/
List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type);
+
+ /**
+ * @param forecastSeconds how many seconds ahead to forecast the provided headroom
+ * @return forecasted thermal headroom, normalized such that 1.0 indicates that throttling will
+ * occur; returns NaN if the headroom or forecast is unavailable
+ */
+ float getThermalHeadroom(int forecastSeconds);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a8fa6db232a2..199b5d55bb39 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -18,7 +18,9 @@ package android.os;
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -39,6 +41,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
/**
* This class gives you control of the power state of the device.
@@ -916,20 +919,22 @@ public final class PowerManager {
final IPowerManager mService;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final Handler mHandler;
+ final IThermalService mThermalService;
/** We lazily initialize it.*/
private DeviceIdleManager mDeviceIdleManager;
- IThermalService mThermalService;
private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
mListenerMap = new ArrayMap<>();
/**
* {@hide}
*/
- public PowerManager(Context context, IPowerManager service, Handler handler) {
+ public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
+ Handler handler) {
mContext = context;
mService = service;
+ mThermalService = thermalService;
mHandler = handler;
}
@@ -1877,18 +1882,11 @@ public final class PowerManager {
* thermal throttling.
*/
public @ThermalStatus int getCurrentThermalStatus() {
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- try {
- return mThermalService.getCurrentThermalStatus();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ return mThermalService.getCurrentThermalStatus();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
-
}
/**
@@ -1915,13 +1913,7 @@ public final class PowerManager {
*/
public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- this.addThermalStatusListener(mContext.getMainExecutor(), listener);
- }
+ this.addThermalStatusListener(mContext.getMainExecutor(), listener);
}
/**
@@ -1934,35 +1926,29 @@ public final class PowerManager {
@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- Preconditions.checkArgument(!mListenerMap.containsKey(listener),
- "Listener already registered: " + listener);
- IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
- @Override
- public void onStatusChange(int status) {
- final long token = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- listener.onThermalStatusChanged(status);
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
- try {
- if (mThermalService.registerThermalStatusListener(internalListener)) {
- mListenerMap.put(listener, internalListener);
- } else {
- throw new RuntimeException("Listener failed to set");
+ Preconditions.checkArgument(!mListenerMap.containsKey(listener),
+ "Listener already registered: " + listener);
+ IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+ @Override
+ public void onStatusChange(int status) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ listener.onThermalStatusChanged(status);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
+ };
+ try {
+ if (mThermalService.registerThermalStatusListener(internalListener)) {
+ mListenerMap.put(listener, internalListener);
+ } else {
+ throw new RuntimeException("Listener failed to set");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1973,22 +1959,72 @@ public final class PowerManager {
*/
public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- IThermalStatusListener internalListener = mListenerMap.get(listener);
- Preconditions.checkArgument(internalListener != null, "Listener was not added");
- try {
- if (mThermalService.unregisterThermalStatusListener(internalListener)) {
- mListenerMap.remove(listener);
- } else {
- throw new RuntimeException("Listener failed to remove");
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ IThermalStatusListener internalListener = mListenerMap.get(listener);
+ Preconditions.checkArgument(internalListener != null, "Listener was not added");
+ try {
+ if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+ mListenerMap.remove(listener);
+ } else {
+ throw new RuntimeException("Listener failed to remove");
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @CurrentTimeMillisLong
+ private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L);
+ private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500;
+
+ /**
+ * Provides an estimate of how much thermal headroom the device currently has before hitting
+ * severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors, such as the skin
+ * temperature sensor. This means that there is no benefit to calling this function more
+ * frequently than about once per second, and attempts to call significantly more frequently may
+ * result in the function returning {@code NaN}.
+ *
+ * In addition, in order to be able to provide an accurate forecast, the system does not attempt
+ * to forecast until it has multiple temperature samples from which to extrapolate. This should
+ * only take a few seconds from the time of the first call, but during this time, no forecasting
+ * will occur, and the current headroom will be returned regardless of the value of
+ * {@code forecastSeconds}.
+ *
+ * The value returned is a non-negative float that represents how much of the thermal envelope
+ * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is (or
+ * will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the CPU,
+ * GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping to specific
+ * thermal status levels beyond that point. This means that values greater than 1.0 may
+ * correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+ *
+ * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+ * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+ * linearly with temperature, though temperature changes over time are typically not linear.
+ * Negative values will be clamped to 0.0 before returning.
+ *
+ * @param forecastSeconds how many seconds in the future to forecast. Given that device
+ * conditions may change at any time, forecasts from further in the
+ * future will likely be less accurate than forecasts in the near future.
+ * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
+ * threshold, as described above. Returns NaN if the device does not support this
+ * functionality or if this function is called significantly faster than once per
+ * second.
+ */
+ public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) {
+ // Rate-limit calls into the thermal service
+ long now = SystemClock.elapsedRealtime();
+ long timeSinceLastUpdate = now - mLastHeadroomUpdate.get();
+ if (timeSinceLastUpdate < MINIMUM_HEADROOM_TIME_MILLIS) {
+ return Float.NaN;
+ }
+
+ try {
+ float forecast = mThermalService.getThermalHeadroom(forecastSeconds);
+ mLastHeadroomUpdate.set(SystemClock.elapsedRealtime());
+ return forecast;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 6d1f646f943b..84fd58063d39 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -33,8 +33,8 @@ import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.admin.DevicePolicyManager;
import android.app.PropertyInvalidatedCache;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -151,12 +151,23 @@ public class UserManager {
public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 0x1;
/**
+ * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode without
+ * asking for credentials. This is used when managed profile password is forgotten. It starts
+ * the user in locked state so that a direct boot aware DPC could reset the password.
+ * Should not be used together with
+ * {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED} or an exception will be thrown.
+ * @hide
+ */
+ public static final int QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL = 0x2;
+
+ /**
* List of flags available for the {@link #requestQuietModeEnabled} method.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "QUIET_MODE_" }, value = {
- QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED })
+ QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED,
+ QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL})
public @interface QuietModeFlag {}
/**
@@ -3521,12 +3532,13 @@ public class UserManager {
boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0);
}
+
/**
* Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
* a target to start when user is unlocked. If {@code target} is specified, caller must have
* the {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
+ * @see #requestQuietModeEnabled(boolean, UserHandle)
* @hide
*/
public boolean requestQuietModeEnabled(
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index e78aef5b7fbb..a2def7fff2d2 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -16,9 +16,12 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IVold;
+import java.util.Set;
+
/**
* Mount service local interface.
*
@@ -97,6 +100,12 @@ public abstract class StorageManagerInternal {
}
/**
+ * Check if fuse is running in target user, if it's running then setup its obb directories.
+ * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ */
+ public abstract void prepareObbDirs(int userId, Set<String> packageList, String processName);
+
+ /**
* Add a listener to listen to reset event in StorageManagerService.
*
* @param listener The listener that will be notified on reset events.
@@ -124,4 +133,14 @@ public abstract class StorageManagerInternal {
* legacy storage, {@code false} otherwise.
*/
public abstract boolean hasLegacyExternalStorage(int uid);
+
+ /**
+ * Makes sure app-private data directories on external storage are setup correctly
+ * after an application is installed or upgraded. The main use for this is OBB dirs,
+ * which can be created/modified by the installer.
+ *
+ * @param packageName the package name of the package
+ * @param uid the uid of the package
+ */
+ public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f86c971d8dec..4523acb0eb26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8595,6 +8595,15 @@ public final class Settings {
public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
/**
+ * When enabled conversations marked as favorites will be set to bubble.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String BUBBLE_IMPORTANT_CONVERSATIONS
+ = "bubble_important_conversations";
+
+ /**
* Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
* swipe).
*
@@ -9084,26 +9093,34 @@ public final class Settings {
* Set to one of {@link #WIFI_SLEEP_POLICY_DEFAULT},
* {@link #WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED}, or
* {@link #WIFI_SLEEP_POLICY_NEVER}.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
/**
* Value for {@link #WIFI_SLEEP_POLICY} to use the default Wi-Fi sleep
* policy, which is to sleep shortly after the turning off
* according to the {@link #STAY_ON_WHILE_PLUGGED_IN} setting.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
/**
* Value for {@link #WIFI_SLEEP_POLICY} to use the default policy when
* the device is on battery, and never go to sleep when the device is
* plugged in.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
/**
* Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_NEVER = 2;
/**
@@ -10197,7 +10214,9 @@ public final class Settings {
/**
* Delay (in seconds) before repeating the Wi-Fi networks available notification.
* Connecting to a network will reset the timer.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
@@ -10227,7 +10246,9 @@ public final class Settings {
/**
* When the number of open networks exceeds this number, the
* least-recently-used excess networks will be removed.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
/**
@@ -10238,8 +10259,8 @@ public final class Settings {
/**
* Setting to allow scans to be enabled even wifi is turned off for connectivity.
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
"wifi_scan_always_enabled";
@@ -10248,8 +10269,8 @@ public final class Settings {
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_P2P_PENDING_FACTORY_RESET =
"wifi_p2p_pending_factory_reset";
@@ -10258,8 +10279,8 @@ public final class Settings {
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
/**
@@ -10304,6 +10325,7 @@ public final class Settings {
* Most readers of this setting should simply check if value == 1 to determined the
* enabled state.
* @hide
+ * @deprecated To be removed.
*/
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
@@ -10343,13 +10365,11 @@ public final class Settings {
/**
* Whether wifi scan throttle is enabled or not.
- * This is intended to be used via adb commands or a menu in developer option to turn off
- * the default wifi scan throttling mechanism for apps.
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
/**
@@ -10451,8 +10471,8 @@ public final class Settings {
* Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
* will enable it. In the future, additional values may be supported.
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_VERBOSE_LOGGING_ENABLED =
"wifi_verbose_logging_enabled";
@@ -10477,8 +10497,8 @@ public final class Settings {
* Default values are provided by code or device configurations.
* Errors in the parameters will cause the entire setting to be ignored.
* @hide
+ * @deprecated This is no longer used or set by the platform.
*/
- @SystemApi
public static final String WIFI_SCORE_PARAMS =
"wifi_score_params";
@@ -10520,8 +10540,8 @@ public final class Settings {
/**
* The Wi-Fi peer-to-peer device name
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
/**
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 9847695ca5ee..ab465ab74ee5 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -33,6 +33,8 @@ public final class ConversationChannelWrapper implements Parcelable {
private CharSequence mGroupLabel;
private CharSequence mParentChannelLabel;
private ShortcutInfo mShortcutInfo;
+ private String mPkg;
+ private int mUid;
public ConversationChannelWrapper() {}
@@ -41,6 +43,8 @@ public final class ConversationChannelWrapper implements Parcelable {
mGroupLabel = in.readCharSequence();
mParentChannelLabel = in.readCharSequence();
mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mPkg = in.readStringNoHelper();
+ mUid = in.readInt();
}
@Override
@@ -49,6 +53,8 @@ public final class ConversationChannelWrapper implements Parcelable {
dest.writeCharSequence(mGroupLabel);
dest.writeCharSequence(mParentChannelLabel);
dest.writeParcelable(mShortcutInfo, flags);
+ dest.writeStringNoHelper(mPkg);
+ dest.writeInt(mUid);
}
@Override
@@ -103,6 +109,22 @@ public final class ConversationChannelWrapper implements Parcelable {
mShortcutInfo = shortcutInfo;
}
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public void setPkg(String pkg) {
+ mPkg = pkg;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public void setUid(int uid) {
+ mUid = uid;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -111,12 +133,14 @@ public final class ConversationChannelWrapper implements Parcelable {
return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
- Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+ Objects.equals(getShortcutInfo(), that.getShortcutInfo()) &&
+ Objects.equals(getPkg(), that.getPkg()) &&
+ getUid() == that.getUid();
}
@Override
public int hashCode() {
return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
- getShortcutInfo());
+ getShortcutInfo(), getPkg(), getUid());
}
}
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index d21a9520e12c..850e9fc0db7e 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -406,9 +406,7 @@ public class CutoutSpecification {
}
currentIndex += RIGHT_MARKER.length();
} else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
- if (!mPositionFromCenterVertical) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += BOTTOM_MARKER.length();
/* prepare to parse the rest path */
@@ -416,9 +414,7 @@ public class CutoutSpecification {
mBindBottomCutout = true;
mPositionFromBottom = true;
} else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
- if (!mPositionFromBottom) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += CENTER_VERTICAL_MARKER.length();
/* prepare to parse the rest path */
@@ -431,14 +427,16 @@ public class CutoutSpecification {
/* prepare to parse the rest path */
resetStatus(sb);
} else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindRightCutout) {
- mBindLeftCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindRightCutout = false;
+ mBindLeftCutout = true;
+
currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
} else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindLeftCutout) {
- mBindRightCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindLeftCutout = false;
+ mBindRightCutout = true;
+
currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
} else {
currentIndex += 1;
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b9868a7e1444..3047385410b0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -28,6 +28,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DeviceProductInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,6 +71,13 @@ public final class DisplayInfo implements Parcelable {
public DisplayAddress address;
/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ */
+ public DeviceProductInfo deviceProductInfo;
+
+ /**
* The human-readable name of the display.
*/
public String name;
@@ -297,6 +305,7 @@ public final class DisplayInfo implements Parcelable {
&& type == other.type
&& displayId == other.displayId
&& Objects.equals(address, other.address)
+ && Objects.equals(deviceProductInfo, other.deviceProductInfo)
&& Objects.equals(uniqueId, other.uniqueId)
&& appWidth == other.appWidth
&& appHeight == other.appHeight
@@ -336,6 +345,7 @@ public final class DisplayInfo implements Parcelable {
type = other.type;
displayId = other.displayId;
address = other.address;
+ deviceProductInfo = other.deviceProductInfo;
name = other.name;
uniqueId = other.uniqueId;
appWidth = other.appWidth;
@@ -373,6 +383,7 @@ public final class DisplayInfo implements Parcelable {
type = source.readInt();
displayId = source.readInt();
address = source.readParcelable(null);
+ deviceProductInfo = source.readParcelable(null);
name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
@@ -418,6 +429,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(type);
dest.writeInt(displayId);
dest.writeParcelable(address, flags);
+ dest.writeParcelable(deviceProductInfo, flags);
dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
@@ -645,6 +657,8 @@ public final class DisplayInfo implements Parcelable {
if (address != null) {
sb.append(", address ").append(address);
}
+ sb.append(", deviceProductInfo ");
+ sb.append(deviceProductInfo);
sb.append(", state ");
sb.append(Display.stateToString(state));
if (ownerUid != 0 || ownerPackageName != null) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 360deddf544d..58e5b2dfaa37 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -28,6 +29,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Vibrator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -138,6 +141,19 @@ public final class InputDevice implements Parcelable {
*/
public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SOURCE_CLASS_" }, value = {
+ SOURCE_CLASS_NONE,
+ SOURCE_CLASS_BUTTON,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_TRACKBALL,
+ SOURCE_CLASS_POSITION,
+ SOURCE_CLASS_JOYSTICK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InputSourceClass {}
+
/**
* The input source is unknown.
*/
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 4ac6a666a21b..13d6dd67bb19 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.system.OsConstants.EINVAL;
+
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -89,7 +91,8 @@ public class Surface implements Parcelable {
private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled);
private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
- private static native int nativeSetFrameRate(long nativeObject, float frameRate);
+ private static native int nativeSetFrameRate(
+ long nativeObject, float frameRate, int compatibility);
public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@@ -184,6 +187,28 @@ public class Surface implements Parcelable {
*/
public static final int ROTATION_270 = 3;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
+ value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE})
+ public @interface FrameRateCompatibility {}
+
+ // From native_window.h. Keep these in sync.
+ /**
+ * There are no inherent restrictions on the frame rate of this surface.
+ */
+ public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0;
+
+ /**
+ * This surface is being used to display content with an inherently fixed frame rate,
+ * e.g. a video that has a specific frame rate. When the system selects a frame rate
+ * other than what the app requested, the app will need to do pull down or use some
+ * other technique to adapt to the system's frame rate. The user experience is likely
+ * to be worse (e.g. more frame stuttering) than it would be if the system had chosen
+ * the app's requested frame rate.
+ */
+ public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
+
/**
* Create an empty surface, which will later be filled in by readFromParcel().
* @hide
@@ -864,11 +889,23 @@ public class Surface implements Parcelable {
* called. The frameRate param does *not* need to be a valid refresh rate for this
* device's display - e.g., it's fine to pass 30fps to a device that can only run the
* display at 60fps.
+ *
+ * @param compatibility The frame rate compatibility of this surface. The
+ * compatibility value may influence the system's choice of display frame rate. See
+ * the FRAME_RATE_COMPATIBILITY_* values for more info.
+ *
+ * @throws IllegalArgumentException If frameRate or compatibility are invalid.
*/
- public void setFrameRate(@FloatRange(from = 0.0) float frameRate) {
- int error = nativeSetFrameRate(mNativeObject, frameRate);
- if (error != 0) {
- throw new RuntimeException("Failed to set frame rate on Surface");
+ public void setFrameRate(
+ @FloatRange(from = 0.0) float frameRate, @FrameRateCompatibility int compatibility) {
+ synchronized (mLock) {
+ checkNotReleasedLocked();
+ int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility);
+ if (error == -EINVAL) {
+ throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()");
+ } else if (error != 0) {
+ throw new RuntimeException("Failed to set frame rate on Surface");
+ }
}
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf48c52825e9..0816e8433e66 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
import android.os.Build;
@@ -213,7 +214,7 @@ public final class SurfaceControl implements Parcelable {
@Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
private static native void nativeSetFrameRate(
- long transactionObj, long nativeObject, float frameRate);
+ long transactionObj, long nativeObject, float frameRate, int compatibility);
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
@@ -1286,12 +1287,14 @@ public final class SurfaceControl implements Parcelable {
public boolean isInternal;
public float density;
public boolean secure;
+ public DeviceProductInfo deviceProductInfo;
@Override
public String toString() {
return "DisplayInfo{isInternal=" + isInternal
+ ", density=" + density
- + ", secure=" + secure + "}";
+ + ", secure=" + secure
+ + ", deviceProductInfo=" + deviceProductInfo + "}";
}
}
@@ -2735,13 +2738,17 @@ public final class SurfaceControl implements Parcelable {
* isn't called. The frameRate param does *not* need to be a valid refresh
* rate for this device's display - e.g., it's fine to pass 30fps to a
* device that can only run the display at 60fps.
+ * @param compatibility The frame rate compatibility of this surface. The compatibility
+ * value may influence the system's choice of display frame rate. See
+ * the Surface.FRAME_RATE_COMPATIBILITY_* values for more info.
* @return This transaction object.
*/
@NonNull
- public Transaction setFrameRate(
- @NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate) {
+ public Transaction setFrameRate(@NonNull SurfaceControl sc,
+ @FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility) {
checkPreconditions(sc);
- nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate);
+ nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate, compatibility);
return this;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 5566e0e4292e..deff79d5486a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -788,9 +788,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
boolean redrawNeeded = false;
+ getLocationInSurface(mLocation);
+ final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
+ || mWindowSpaceTop != mLocation[1];
+ final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
+ || getHeight() != mScreenRect.height();
- if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha
- && alphaChanged) || windowVisibleChanged) {
+
+ if (creating || formatChanged || sizeChanged || visibleChanged ||
+ (mUseAlpha && alphaChanged) || windowVisibleChanged ||
+ positionChanged || layoutSizeChanged) {
getLocationInWindow(mLocation);
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
@@ -922,6 +929,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
}
+ } else if ((layoutSizeChanged || positionChanged) &&
+ WindowManagerGlobal.useBLAST()) {
+ viewRoot.setUseBLASTSyncTransaction();
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if (sizeChanged && !creating) {
@@ -1058,11 +1068,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
} else {
// Calculate the window position in case RT loses the window
// and we need to fallback to a UI-thread driven position update
- getLocationInSurface(mLocation);
- final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
- || mWindowSpaceTop != mLocation[1];
- final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
- || getHeight() != mScreenRect.height();
if (positionChanged || layoutSizeChanged) { // Only the position has changed
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
@@ -1540,21 +1545,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
/**
- * @hide
- * Note: Base class method is @UnsupportedAppUsage
- */
- @Override
- public void invalidate(boolean invalidateCache) {
- super.invalidate(invalidateCache);
- if (!WindowManagerGlobal.useBLAST()) {
- return;
- }
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null) return;
- viewRoot.setUseBLASTSyncTransaction();
- }
-
- /**
* Display the view-hierarchy embedded within a {@link SurfaceControlViewHost.SurfacePackage}
* within this SurfaceView. If this SurfaceView is above it's host Surface (see
* {@link #setZOrderOnTop} then the embedded Surface hierarchy will be able to receive
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b9be33ca0f59..4c7307ee5b8c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@ import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.InputDevice.InputSourceClass;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation.Bounds;
@@ -5205,6 +5206,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private int mExplicitStyle;
/**
+ * Specifies which input source classes should provide unbuffered input events to this view
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ */
+ @InputSourceClass
+ int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -8013,6 +8022,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mKeyDispatchState.reset(this);
}
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
@@ -15820,12 +15833,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* system not batch {@link MotionEvent}s but instead deliver them as soon as they're
* available. This method should only be called for touch events.
*
- * <p class="note">This api is not intended for most applications. Buffered dispatch
+ * <p class="note">This API is not intended for most applications. Buffered dispatch
* provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
* streams will not improve your input latency. Side effects include: increased latency,
* jittery scrolls and inability to take advantage of system resampling. Talk to your input
* professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
* you.</p>
+ *
+ * To receive unbuffered events for arbitrary input device source classes, use
+ * {@link #requestUnbufferedDispatch(int)},
+ *
+ * @see View#requestUnbufferedDispatch(int)
*/
public final void requestUnbufferedDispatch(MotionEvent event) {
final int action = event.getAction();
@@ -15837,6 +15855,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mUnbufferedDispatchRequested = true;
}
+ /**
+ * Request unbuffered dispatch of the given event source class to this view.
+ * This is similar to {@link View#requestUnbufferedDispatch(MotionEvent)}, but does not
+ * automatically terminate, and allows the specification of arbitrary input source classes.
+ *
+ * @param source The combined input source class to request unbuffered dispatch for. All
+ * events coming from these source classes will not be buffered. Set to
+ * {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour.
+ *
+ * @see View#requestUnbufferedDispatch(MotionEvent)
+ */
+ public final void requestUnbufferedDispatch(@InputSourceClass int source) {
+ if (mUnbufferedInputSource == source) {
+ return;
+ }
+ mUnbufferedInputSource = source;
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
+
private boolean hasSize() {
return (mBottom > mTop) && (mRight > mLeft);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0367536919cf..b6c46be66761 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3692,6 +3692,31 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
childrenForAccessibility.clear();
}
+ info.setAvailableExtraData(Collections.singletonList(
+ AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param info The info to which to add the extra data. Never {@code null}.
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method. Never
+ * {@code null}.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
+ * {@code null} if the service provided no arguments.
+ *
+ */
+ @Override
+ public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info,
+ @NonNull String extraDataKey, @Nullable Bundle arguments) {
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ info.setExtraRenderingInfo(extraRenderingInfo);
+ }
}
@Override
@@ -9004,4 +9029,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
getChildAt(i).encode(encoder);
}
}
+
+ /** @hide */
+ @Override
+ public final void onDescendantUnbufferedRequested() {
+ // First look at the focused child for focused events
+ int focusedChildNonPointerSource = InputDevice.SOURCE_CLASS_NONE;
+ if (mFocused != null) {
+ focusedChildNonPointerSource = mFocused.mUnbufferedInputSource
+ & (~InputDevice.SOURCE_CLASS_POINTER);
+ }
+ mUnbufferedInputSource = focusedChildNonPointerSource;
+
+ // Request unbuffered dispatch for pointer events for this view if any child requested
+ // unbuffered dispatch for pointer events. This is because we can't expect that the pointer
+ // source would dispatch to the focused view.
+ for (int i = 0; i < mChildrenCount; i++) {
+ final View child = mChildren[i];
+ if ((child.mUnbufferedInputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ mUnbufferedInputSource |= InputDevice.SOURCE_CLASS_POINTER;
+ break;
+ }
+ }
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f25206dce8ff..775c15e77d5d 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -675,4 +675,18 @@ public interface ViewParent {
*/
default void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
}
+
+ /**
+ * Unbuffered dispatch has been requested by a child of this view parent.
+ * This method is called by the View hierarchy to signal ancestors that a View needs to
+ * request unbuffered dispatch.
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ * @hide
+ */
+ default void onDescendantUnbufferedRequested() {
+ if (getParent() != null) {
+ getParent().onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 043e5be8a050..204d2c8bc2ce 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,6 +18,7 @@ package android.view;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.View.PFLAG_DRAW_ANIMATION;
@@ -116,6 +117,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl.Transaction;
@@ -499,6 +501,10 @@ public final class ViewRootImpl implements ViewParent,
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
boolean mUnbufferedInputDispatch;
+ boolean mUnbufferedInputDispatchBySource;
+ @InputSourceClass
+ int mUnbufferedInputSource = SOURCE_CLASS_NONE;
+
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -1849,7 +1855,7 @@ public final class ViewRootImpl implements ViewParent,
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- if (!mUnbufferedInputDispatch) {
+ if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
@@ -7505,6 +7511,9 @@ public final class ViewRootImpl implements ViewParent,
writer.print(mTraversalScheduled);
writer.print(innerPrefix); writer.print("mIsAmbientMode=");
writer.print(mIsAmbientMode);
+ writer.print(innerPrefix); writer.print("mUnbufferedInputSource=");
+ writer.print(Integer.toHexString(mUnbufferedInputSource));
+
if (mTraversalScheduled) {
writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
} else {
@@ -8109,6 +8118,7 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ processUnbufferedRequest(event);
List<InputEvent> processedEvents;
try {
processedEvents =
@@ -8134,7 +8144,7 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onBatchedInputEventPending() {
- if (mUnbufferedInputDispatch) {
+ if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
@@ -8151,6 +8161,17 @@ public final class ViewRootImpl implements ViewParent,
unscheduleConsumeBatchedInput();
super.dispose();
}
+
+ private void processUnbufferedRequest(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ return;
+ }
+ mUnbufferedInputDispatchBySource =
+ (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+ if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) {
+ scheduleConsumeBatchedInputImmediately();
+ }
+ }
}
WindowInputEventReceiver mInputEventReceiver;
@@ -9601,4 +9622,9 @@ public final class ViewRootImpl implements ViewParent,
return mSurfaceControl;
}
}
+
+ @Override
+ public void onDescendantUnbufferedRequested() {
+ mUnbufferedInputSource = mView.mUnbufferedInputSource;
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 62f3fa4f6ed4..87dcba0490ee 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -163,6 +163,15 @@ public class WindowlessWindowManager implements IWindowSession {
return !PixelFormat.formatHasAlpha(attrs.format);
}
+ /** @hide */
+ protected SurfaceControl getSurfaceControl(View rootView) {
+ final State s = mStateForWindow.get(rootView.getViewRootImpl().mWindow.asBinder());
+ if (s == null) {
+ return null;
+ }
+ return s.mSurfaceControl;
+ }
+
@Override
public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf2de14e6811..eb4f9db39e3e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,8 +50,12 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.util.Size;
+import android.util.TypedValue;
import android.view.TouchDelegate;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.util.CollectionUtils;
@@ -634,6 +638,25 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ /**
+ * Key used to request extra data for accessibility scanning tool's purposes.
+ * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
+ * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
+ * argument.
+ * <p>
+ * The data can be retrieved from the {@link ExtraRenderingInfo} returned by
+ * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams},
+ * {@link ExtraRenderingInfo#getTextSizeInPx()} and
+ * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
+ * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
+ * {@link TextView}.
+ *
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+
+ public static final String EXTRA_DATA_RENDERING_INFO_KEY =
+ "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
+
/** @hide */
public static final String EXTRA_DATA_REQUESTED_KEY =
"android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
@@ -804,6 +827,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private TouchDelegateInfo mTouchDelegateInfo;
+ private ExtraRenderingInfo mExtraRenderingInfo;
+
private IBinder mLeashedChild;
private IBinder mLeashedParent;
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
@@ -991,6 +1016,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param extraDataKey The extra data requested. Data that must be requested
* with this mechanism is generally expensive to retrieve, so should only be
* requested when needed. See
+ * {@link #EXTRA_DATA_RENDERING_INFO_KEY},
* {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
* {@link #getAvailableExtraData()}.
* @param args A bundle of arguments for the request. These depend on the particular request.
@@ -1547,6 +1573,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* {@link #refreshWithExtraData(String, Bundle)}.
*
* @return An unmodifiable list of keys corresponding to extra data that can be requested.
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
* @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
*/
public List<String> getAvailableExtraData() {
@@ -2375,6 +2402,32 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the conformance info if the node is meant to be refreshed with extra data.
+ *
+ * @return The conformance info.
+ */
+ @Nullable
+ public ExtraRenderingInfo getExtraRenderingInfo() {
+ return mExtraRenderingInfo;
+ }
+
+ /**
+ * Sets the conformance info if the node is meant to be refreshed with extra data.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param extraRenderingInfo The conformance info.
+ * @hide
+ */
+ public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
+ enforceNotSealed();
+ mExtraRenderingInfo = extraRenderingInfo;
+ }
+
+ /**
* Gets if the content of this node is invalid. For example,
* a date is not well-formed.
*
@@ -3695,6 +3748,10 @@ public class AccessibilityNodeInfo implements Parcelable {
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mExtraRenderingInfo, DEFAULT.mExtraRenderingInfo)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mLeashedChild != DEFAULT.mLeashedChild) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3833,6 +3890,12 @@ public class AccessibilityNodeInfo implements Parcelable {
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeValue(mExtraRenderingInfo.getLayoutParams());
+ parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
+ parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeStrongBinder(mLeashedChild);
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -3941,6 +4004,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = (other.mCollectionItemInfo != null)
? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
+ ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -3955,6 +4021,9 @@ public class AccessibilityNodeInfo implements Parcelable {
mCollectionItemInfo = (cii == null) ? null
: new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
cii.mColumnSpan, cii.mHeading, cii.mSelected);
+ ExtraRenderingInfo ti = other.mExtraRenderingInfo;
+ mExtraRenderingInfo = (ti == null) ? null
+ : new ExtraRenderingInfo(ti);
}
/**
@@ -4083,6 +4152,14 @@ public class AccessibilityNodeInfo implements Parcelable {
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+ mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null);
+ mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
+ mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
mLeashedChild = parcel.readStrongBinder();
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5679,6 +5756,134 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Class with information of a view useful to evaluate accessibility needs. Developers can
+ * refresh the node with the key {@link #EXTRA_DATA_RENDERING_INFO_KEY} to fetch the text size
+ * and unit if it is {@link TextView} and the height and the width of layout params from
+ * {@link ViewGroup} or {@link TextView}.
+ *
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+ public static final class ExtraRenderingInfo {
+ private static final int UNDEFINED_VALUE = -1;
+ private static final int MAX_POOL_SIZE = 20;
+ private static final SynchronizedPool<ExtraRenderingInfo> sPool =
+ new SynchronizedPool<>(MAX_POOL_SIZE);
+
+ private Size mLayoutParams;
+ private float mTextSizeInPx = UNDEFINED_VALUE;
+ private int mTextSizeUnit = UNDEFINED_VALUE;
+
+ /**
+ * Obtains a pooled instance.
+ * @hide
+ */
+ @NonNull
+ public static ExtraRenderingInfo obtain() {
+ final ExtraRenderingInfo info = sPool.acquire();
+ if (info == null) {
+ return new ExtraRenderingInfo(null);
+ }
+ return info;
+ }
+
+ /** Obtains a pooled instance that is a clone of another one. */
+ private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
+ ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
+ extraRenderingInfo.mLayoutParams = other.mLayoutParams;
+ extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
+ extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
+ return extraRenderingInfo;
+ }
+
+ /**
+ * Creates a new conformance info of a view, and this new instance is initialized from
+ * the given <code>other</code>.
+ *
+ * @param other The instance to clone.
+ */
+ private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
+ if (other != null) {
+ mLayoutParams = other.mLayoutParams;
+ mTextSizeInPx = other.mTextSizeInPx;
+ mTextSizeUnit = other.mTextSizeUnit;
+ }
+ }
+
+ /**
+ * @return a {@link Size} stores layout height and layout width of the view,
+ * or null otherwise.
+ */
+ public @Nullable Size getLayoutParams() {
+ return mLayoutParams;
+ }
+
+ /**
+ * Sets layout width and layout height of the view.
+ *
+ * @param width The layout width.
+ * @param height The layout height.
+ * @hide
+ */
+ public void setLayoutParams(int width, int height) {
+ mLayoutParams = new Size(width, height);
+ }
+
+ /**
+ * @return the text size of a {@code TextView}, or -1 otherwise.
+ */
+ public float getTextSizeInPx() {
+ return mTextSizeInPx;
+ }
+
+ /**
+ * Sets text size of the view.
+ *
+ * @param textSizeInPx The text size in pixels.
+ * @hide
+ */
+ public void setTextSizeInPx(float textSizeInPx) {
+ mTextSizeInPx = textSizeInPx;
+ }
+
+ /**
+ * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
+ * {@code TextView}, or -1 otherwise.
+ *
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
+ * Sets text size unit of the view.
+ *
+ * @param textSizeUnit The text size unit.
+ * @hide
+ */
+ public void setTextSizeUnit(int textSizeUnit) {
+ mTextSizeUnit = textSizeUnit;
+ }
+
+ /**
+ * Recycles this instance.
+ *
+ * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mLayoutParams = null;
+ mTextSizeInPx = UNDEFINED_VALUE;
+ mTextSizeUnit = UNDEFINED_VALUE;
+ }
+ }
+
+ /**
* @see android.os.Parcelable.Creator
*/
public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0182975c13c0..815cc5cbb10d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -727,6 +728,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@UnsupportedAppUsage
private Layout mLayout;
private boolean mLocalesChanged = false;
+ private int mTextSizeUnit = -1;
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
@@ -3842,6 +3844,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
int mTextSize = -1;
+ int mTextSizeUnit = -1;
LocaleList mTextLocales = null;
String mFontFamily = null;
Typeface mFontTypeface = null;
@@ -3869,6 +3872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
+ " mTextColorHint:" + mTextColorHint + "\n"
+ " mTextColorLink:" + mTextColorLink + "\n"
+ " mTextSize:" + mTextSize + "\n"
+ + " mTextSizeUnit:" + mTextSizeUnit + "\n"
+ " mTextLocales:" + mTextLocales + "\n"
+ " mFontFamily:" + mFontFamily + "\n"
+ " mFontTypeface:" + mFontTypeface + "\n"
@@ -3980,6 +3984,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.TextAppearance_textSize:
attributes.mTextSize =
appearance.getDimensionPixelSize(attr, attributes.mTextSize);
+ attributes.mTextSizeUnit = appearance.peekValue(attr).getComplexUnit();
break;
case com.android.internal.R.styleable.TextAppearance_textLocale:
final String localeString = appearance.getString(attr);
@@ -4073,6 +4078,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (attributes.mTextSize != -1) {
+ mTextSizeUnit = attributes.mTextSizeUnit;
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
@@ -4295,6 +4301,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
r = c.getResources();
}
+ mTextSizeUnit = unit;
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
@@ -4315,6 +4322,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Gets the text size unit defined by the developer. It may be specified in resources or be
+ * passed as the unit argument of {@link #setTextSize(int, float)} at runtime.
+ *
+ * @return the dimension type of the text size unit originally defined.
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
* Gets the extent by which text should be stretched horizontally.
* This will usually be 1.0.
* @return The horizontal scale factor.
@@ -11769,8 +11787,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
- info.setAvailableExtraData(
- Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ ));
+ } else {
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY
+ ));
}
if (isFocused()) {
@@ -11824,11 +11848,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void addExtraDataToAccessibilityNodeInfo(
AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
- // The only extra data we support requires arguments.
- if (arguments == null) {
- return;
- }
- if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
int positionInfoStartIndex = arguments.getInt(
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
int positionInfoLength = arguments.getInt(
@@ -11856,6 +11876,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
info.getExtras().putParcelableArray(extraDataKey, boundingRects);
+ return;
+ }
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ extraRenderingInfo.setTextSizeInPx(getTextSize());
+ extraRenderingInfo.setTextSizeUnit(getTextSizeUnit());
+ info.setExtraRenderingInfo(extraRenderingInfo);
}
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index e8f84aacb7c9..b4a0208ccc91 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.AppGlobals;
+import android.app.admin.DevicePolicyEventLogger;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +28,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -251,6 +253,8 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
abstract @Nullable ViewGroup getInactiveAdapterView();
+ abstract String getMetricsCategory();
+
/**
* Rebuilds the tab that is currently visible to the user.
* <p>Returns {@code true} if rebuild has completed.
@@ -282,6 +286,10 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
if (listUserHandle == mWorkProfileUserHandle
&& mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
R.string.resolver_turn_on_work_apps,
@@ -294,11 +302,19 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
if (listUserHandle == mPersonalProfileUserHandle) {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
R.string.resolver_cant_share_with_personal_apps,
R.string.resolver_cant_share_cross_profile_explanation);
} else {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
+ .setStrings(getMetricsCategory())
+ .write();
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
R.string.resolver_cant_share_with_work_apps,
@@ -315,6 +331,12 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
UserHandle listUserHandle = listAdapter.getUserHandle();
if (UserHandle.myUserId() == listUserHandle.getIdentifier()
|| !hasAppsInOtherProfile(listAdapter)) {
+ if (mWorkProfileUserHandle != null) {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
+ .setStrings(getMetricsCategory())
+ .write();
+ }
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
R.string.resolver_no_apps_available,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8618bcfc3932..a201a335622a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2698,6 +2698,11 @@ public class ChooserActivity extends ResolverActivity implements
@Override
protected void resetButtonBar() {}
+ @Override
+ protected String getMetricsCategory() {
+ return METRICS_CATEGORY_CHOOSER;
+ }
+
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3095,7 +3100,7 @@ public class ChooserActivity extends ResolverActivity implements
final ViewGroup viewGroup = (ViewGroup) holder.itemView;
int start = getListPosition(position);
int startType = getRowType(start);
- if (viewGroup.getForeground() == null) {
+ if (viewGroup.getForeground() == null && position > 0) {
viewGroup.setForeground(
getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index e3501422f915..d4404023c4f4 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -165,6 +165,11 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
return getListViewForIndex(1 - getCurrentPage());
}
+ @Override
+ String getMetricsCategory() {
+ return ResolverActivity.METRICS_CATEGORY_CHOOSER;
+ }
+
class ChooserProfileDescriptor extends ProfileDescriptor {
private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 409ae347f515..96bfe7394440 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -33,6 +33,7 @@ import android.app.ActivityThread;
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
+import android.app.admin.DevicePolicyEventLogger;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -60,6 +61,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -84,6 +86,7 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
+import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.content.PackageMonitor;
@@ -99,6 +102,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
+
/**
* This activity is displayed when the system attempts to start an Intent for
* which there is more than one matching activity, allowing the user to decide
@@ -152,6 +156,8 @@ public class ResolverActivity extends Activity implements
private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
+ protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
+ protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
/**
* TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
@@ -1201,12 +1207,14 @@ public class ResolverActivity extends Activity implements
if (!mSafeForwardingMode) {
if (cti.startAsUser(this, null, currentUserHandle)) {
onActivityStarted(cti);
+ maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
}
return;
}
try {
if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
onActivityStarted(cti);
+ maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
}
} catch (RuntimeException e) {
String launchedFromPackage;
@@ -1222,6 +1230,18 @@ public class ResolverActivity extends Activity implements
}
}
+ private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
+ if (!hasWorkProfile() || currentUserHandle == getUser()) {
+ return;
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
+ .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+ .setStrings(getMetricsCategory(),
+ cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
+ .write();
+ }
+
public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
int userId) {
@@ -1425,7 +1445,8 @@ public class ResolverActivity extends Activity implements
* - The target app has declared it supports cross-profile communication via manifest metadata
*/
private boolean maybeAutolaunchIfCrossProfileSupported() {
- int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
+ ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+ int count = activeListAdapter.getUnfilteredCount();
if (count != 1) {
return false;
}
@@ -1434,7 +1455,7 @@ public class ResolverActivity extends Activity implements
if (inactiveListAdapter.getUnfilteredCount() != 1) {
return false;
}
- TargetInfo activeProfileTarget = mMultiProfilePagerAdapter.getActiveListAdapter()
+ TargetInfo activeProfileTarget = activeListAdapter
.targetInfoForPosition(0, false);
TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
@@ -1449,6 +1470,11 @@ public class ResolverActivity extends Activity implements
return false;
}
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
+ .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+ .setStrings(getMetricsCategory())
+ .write();
safelyStartActivity(activeProfileTarget);
finish();
return true;
@@ -1530,11 +1556,17 @@ public class ResolverActivity extends Activity implements
viewPager.setCurrentItem(1);
}
setupViewVisibilities();
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
+ .setInt(viewPager.getCurrentItem())
+ .setStrings(getMetricsCategory())
+ .write();
});
viewPager.setVisibility(View.VISIBLE);
tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
mMultiProfilePagerAdapter.setOnProfileSelectedListener(tabHost::setCurrentTab);
+ findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
private void resetTabsHeaderStyle(TabWidget tabWidget) {
@@ -1697,6 +1729,10 @@ public class ResolverActivity extends Activity implements
&& Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
}
+ protected String getMetricsCategory() {
+ return METRICS_CATEGORY_RESOLVER;
+ }
+
@Override // ResolverListCommunicator
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 96dc83a3f683..21e7fd9fcca6 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -150,6 +150,11 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
return getListViewForIndex(1 - getCurrentPage());
}
+ @Override
+ String getMetricsCategory() {
+ return ResolverActivity.METRICS_CATEGORY_RESOLVER;
+ }
+
class ResolverProfileDescriptor extends ProfileDescriptor {
private ResolverListAdapter resolverListAdapter;
final ListView listView;
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index c68e50692c2a..b61b9dea2554 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -16,14 +16,15 @@
package com.android.internal.policy;
-import android.graphics.Rect;
-
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import android.content.res.Resources;
+import android.graphics.Rect;
+
/**
* Utility functions for docked stack divider used by both window manager and System UI.
*
@@ -105,23 +106,6 @@ public class DockedDividerUtils {
return start + (end - start) / 2 - dividerSize / 2;
}
- public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
- boolean isHorizontalDivision) {
- if (dockOnTopOrLeft) {
- if (isHorizontalDivision) {
- return DOCKED_TOP;
- } else {
- return DOCKED_LEFT;
- }
- } else {
- if (isHorizontalDivision) {
- return DOCKED_BOTTOM;
- } else {
- return DOCKED_RIGHT;
- }
- }
- }
-
public static int invertDockSide(int dockSide) {
switch (dockSide) {
case DOCKED_LEFT:
@@ -136,4 +120,21 @@ public class DockedDividerUtils {
return DOCKED_INVALID;
}
}
+
+ /** Returns the inset distance from the divider window edge to the dividerview. */
+ public static int getDividerInsets(Resources res) {
+ return res.getDimensionPixelSize(com.android.internal.R.dimen.docked_stack_divider_insets);
+ }
+
+ /** Returns the size of the divider */
+ public static int getDividerSize(Resources res, int dividerInsets) {
+ final int windowWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ return windowWidth - 2 * dividerInsets;
+ }
+
+ /** Returns the docked-stack side */
+ public static int getDockSide(int displayWidth, int displayHeight) {
+ return displayWidth > displayHeight ? DOCKED_LEFT : DOCKED_TOP;
+ }
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index b01083bba643..8a53bd0db8af 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -413,10 +413,15 @@ static jint nativeSetAutoRefreshEnabled(JNIEnv* env, jclass clazz, jlong nativeO
return anw->perform(surface, NATIVE_WINDOW_SET_AUTO_REFRESH, int(enabled));
}
-static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate) {
+static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate,
+ jint compatibility) {
Surface* surface = reinterpret_cast<Surface*>(nativeObject);
ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
- return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate));
+ // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
+ // NATIVE_WINDOW_SET_FRAME_RATE takes an
+ // ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The values are identical
+ // though, so no need to explicitly convert.
+ return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate), compatibility);
}
// ----------------------------------------------------------------------------
@@ -453,7 +458,7 @@ static const JNINativeMethod gSurfaceMethods[] = {
(void*)nativeAttachAndQueueBufferWithColorSpace},
{"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
{"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
- {"nativeSetFrameRate", "(JF)I", (void*)nativeSetFrameRate},
+ {"nativeSetFrameRate", "(JFI)I", (void*)nativeSetFrameRate},
};
int register_android_view_Surface(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 3f81b112d9f1..a9ef257359c2 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <system/graphics.h>
#include <ui/ConfigStoreTypes.h>
+#include <ui/DeviceProductInfo.h>
#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayedFrameStats.h>
@@ -65,9 +66,19 @@ static const char* const OutOfResourcesException =
static struct {
jclass clazz;
jmethodID ctor;
+} gIntegerClassInfo;
+
+static jobject toInteger(JNIEnv* env, int32_t i) {
+ return env->NewObject(gIntegerClassInfo.clazz, gIntegerClassInfo.ctor, i);
+}
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID isInternal;
jfieldID density;
jfieldID secure;
+ jfieldID deviceProductInfo;
} gDisplayInfoClassInfo;
static struct {
@@ -111,6 +122,16 @@ static struct {
static struct {
jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoManufactureDateClassInfo;
+
+static struct {
+ jclass clazz;
jmethodID builder;
} gGraphicBufferClassInfo;
@@ -592,11 +613,14 @@ static void nativeSetShadowRadius(JNIEnv* env, jclass clazz, jlong transactionOb
}
static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
- jfloat frameRate) {
+ jfloat frameRate, jint compatibility) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->setFrameRate(ctrl, frameRate);
+ // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
+ // Transaction::setFrameRate() takes an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The
+ // values are identical though, so no need to convert anything.
+ transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility));
}
static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
@@ -773,6 +797,41 @@ static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
}
}
+static jobject convertDeviceProductInfoToJavaObject(
+ JNIEnv* env, const std::optional<DeviceProductInfo>& info) {
+ using ModelYear = android::DeviceProductInfo::ModelYear;
+ using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
+
+ if (!info) return nullptr;
+ jstring name = env->NewStringUTF(info->name.data());
+ jstring manufacturerPnpId = env->NewStringUTF(info->manufacturerPnpId.data());
+ jobject productId = env->NewStringUTF(info->productId.data());
+ const auto& date = info->manufactureOrModelDate;
+ jobject modelYear, manufactureDate;
+ if (const auto* model = std::get_if<ModelYear>(&date)) {
+ modelYear = toInteger(env, model->year);
+ manufactureDate = nullptr;
+ } else if (const auto* manufactureWeekAndYear = std::get_if<ManufactureWeekAndYear>(&date)) {
+ modelYear = nullptr;
+ manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+ gDeviceProductInfoManufactureDateClassInfo.ctor,
+ toInteger(env, manufactureWeekAndYear->week),
+ toInteger(env, manufactureWeekAndYear->year));
+ } else if (const auto* manufactureYear = std::get_if<ManufactureYear>(&date)) {
+ modelYear = nullptr;
+ manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+ gDeviceProductInfoManufactureDateClassInfo.ctor,
+ nullptr,
+ toInteger(env, manufactureYear->year));
+ } else {
+ LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name,
+ manufacturerPnpId, productId, modelYear, manufactureDate);
+}
+
static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
DisplayInfo info;
if (const auto token = ibinderForJavaObject(env, tokenObj);
@@ -785,6 +844,8 @@ static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj)
info.connectionType == DisplayConnectionType::Internal);
env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
+ env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo,
+ convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
return object;
}
@@ -1409,7 +1470,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetLayerStack },
{"nativeSetShadowRadius", "(JJF)V",
(void*)nativeSetShadowRadius },
- {"nativeSetFrameRate", "(JJF)V",
+ {"nativeSetFrameRate", "(JJFI)V",
(void*)nativeSetFrameRate },
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
@@ -1527,12 +1588,19 @@ int register_android_view_SurfaceControl(JNIEnv* env)
int err = RegisterMethodsOrDie(env, "android/view/SurfaceControl",
sSurfaceControlMethods, NELEM(sSurfaceControlMethods));
+ jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+ gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass);
+ gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V");
+
jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
+ gDisplayInfoClassInfo.deviceProductInfo =
+ GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
+ "Landroid/hardware/display/DeviceProductInfo;");
jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
@@ -1573,6 +1641,25 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gHdrCapabilitiesClassInfo.ctor = GetMethodIDOrDie(env, hdrCapabilitiesClazz, "<init>",
"([IFFF)V");
+ jclass deviceProductInfoClazz =
+ FindClassOrDie(env, "android/hardware/display/DeviceProductInfo");
+ gDeviceProductInfoClassInfo.clazz = MakeGlobalRefOrDie(env, deviceProductInfoClazz);
+ gDeviceProductInfoClassInfo.ctor =
+ GetMethodIDOrDie(env, deviceProductInfoClazz, "<init>",
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/lang/Integer;"
+ "Landroid/hardware/display/DeviceProductInfo$ManufactureDate;)V");
+
+ jclass deviceProductInfoManufactureDateClazz =
+ FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate");
+ gDeviceProductInfoManufactureDateClassInfo.clazz =
+ MakeGlobalRefOrDie(env, deviceProductInfoManufactureDateClazz);
+ gDeviceProductInfoManufactureDateClassInfo.ctor =
+ GetMethodIDOrDie(env, deviceProductInfoManufactureDateClazz, "<init>",
+ "(Ljava/lang/Integer;Ljava/lang/Integer;)V");
+
jclass graphicsBufferClazz = FindClassOrDie(env, "android/graphics/GraphicBuffer");
gGraphicBufferClassInfo.clazz = MakeGlobalRefOrDie(env, graphicsBufferClazz);
gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d91911c3a6c0..e4141e02ab1c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -105,10 +105,12 @@ namespace {
using namespace std::placeholders;
using android::String8;
+using android::base::ReadFileToString;
using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
+using android::base::GetProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -174,6 +176,12 @@ static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
+/**
+ * Property to enable app data isolation for sdcard obb or data in vold.
+ */
+static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
+ "persist.sys.vold_app_data_isolation_enabled";
+
static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
@@ -603,6 +611,15 @@ static void EnableDebugger() {
}
}
+static bool IsFilesystemSupported(const std::string& fsType) {
+ std::string supported;
+ if (!ReadFileToString("/proc/filesystems", &supported)) {
+ ALOGE("Failed to read supported filesystems");
+ return false;
+ }
+ return supported.find(fsType + "\n") != std::string::npos;
+}
+
static void PreApplicationInit() {
// The child process sets this to indicate it's not the zygote.
android_mallopt(M_SET_ZYGOTE_CHILD, nullptr, 0);
@@ -782,6 +799,31 @@ static void MountAppDataTmpFs(const std::string& target_dir,
}
}
+static void BindMountObbPackage(std::string_view package_name, int userId, fail_fn_t fail_fn) {
+
+ // TODO(148772775): Pass primary volume name from zygote argument to here
+ std::string source;
+ if (IsFilesystemSupported("sdcardfs")) {
+ source = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb/%s",
+ userId, package_name.data());
+ } else {
+ source = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb/%s",
+ userId, userId, package_name.data());
+ }
+ std::string target(
+ StringPrintf("/storage/emulated/%d/Android/obb/%s", userId, package_name.data()));
+
+ if (access(source.c_str(), F_OK) != 0) {
+ fail_fn(CREATE_ERROR("Cannot access source %s: %s", source.c_str(), strerror(errno)));
+ }
+
+ if (access(target.c_str(), F_OK) != 0) {
+ fail_fn(CREATE_ERROR("Cannot access target %s: %s", target.c_str(), strerror(errno)));
+ }
+
+ BindMount(source, target, fail_fn);
+}
+
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static void MountEmulatedStorage(uid_t uid, jint mount_mode,
@@ -1504,6 +1546,60 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
}
}
+// Bind mount all obb directories that are visible to this app.
+// If app data isolation is not enabled for this process, bind mount the whole obb
+// directory instead.
+static void BindMountAppObbDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
+ uid_t uid, const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
+
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+ const userid_t user_id = multiuser_get_user_id(uid);
+
+ // If FUSE is not ready for this user, skip it
+ // TODO(148772775): Pass primary volume name from zygote argument to here
+ std::string tmp = GetProperty("vold.fuse_running_users", "");
+ std::istringstream fuse_running_users(tmp);
+ bool user_found = false;
+ std::string s;
+ std::string user_id_str = std::to_string(user_id);
+ while (!user_found && std::getline(fuse_running_users, s, ',')) {
+ if (user_id_str == s) {
+ user_found = true;
+ }
+ }
+ if (!user_found) {
+ ALOGI("User %d is not running fuse yet, fuse_running_users=%s", user_id, tmp.c_str());
+ return;
+ }
+
+ // Fuse is ready, so we can start using fuse path.
+ int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
+
+ if (size == 0) {
+ // App data isolation is not enabled for this process, so we bind mount to whole obb/ dir.
+ std::string source;
+ if (IsFilesystemSupported("sdcardfs")) {
+ source = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb", user_id);
+ } else {
+ source = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb", user_id, user_id);
+ }
+ std::string target(StringPrintf("/storage/emulated/%d/Android/obb", user_id));
+
+ if (access(source.c_str(), F_OK) != 0) {
+ fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
+ }
+ BindMount(source, target, fail_fn);
+ return;
+ }
+
+ // Bind mount each package obb directory
+ for (int i = 0; i < size; i += 3) {
+ jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
+ std::string packageName = extract_fn(package_str).value();
+ BindMountObbPackage(packageName, user_id, fail_fn);
+ }
+}
+
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
@@ -1552,6 +1648,12 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
+ if ((mount_external != MOUNT_EXTERNAL_INSTALLER) &&
+ GetBoolProperty(kPropFuse, false) &&
+ GetBoolProperty(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ BindMountAppObbDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ }
+
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0b9ce6f9968..08db4544d5e7 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -275,6 +275,7 @@ message TaskProto {
optional float adjust_divider_amount = 25;
optional bool animating_bounds = 26;
optional float minimize_amount = 27;
+ optional bool created_by_organizer = 28;
}
/* represents ActivityRecordProto */
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index 2de5b7fd45f6..bb654f05b02d 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -33,8 +33,9 @@ message GraphicsStatsServiceDumpProto {
message GraphicsStatsProto {
enum PipelineType {
- GL = 0;
- VULKAN = 1;
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
}
option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 782b2694c453..3d8108d2a4c8 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -179,4 +179,11 @@ enum EventId {
PROVISIONING_DPC_SETUP_STARTED = 152;
PROVISIONING_DPC_SETUP_COMPLETED = 153;
PROVISIONING_ORGANIZATION_OWNED_MANAGED_PROFILE = 154;
+ RESOLVER_CROSS_PROFILE_TARGET_OPENED = 155;
+ RESOLVER_SWITCH_TABS = 156;
+ RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED = 157;
+ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL= 158;
+ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159;
+ RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160;
+ RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 488698a8f461..62e57e477ca6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -645,6 +645,9 @@
<protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+ <!-- Added in R -->
+ <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" />
+
<!-- For tether entitlement recheck-->
<protected-broadcast
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
@@ -1197,6 +1200,15 @@
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
+ <!-- Allows an application assigned to the Dialer role to be granted access to the telephony
+ call audio streams, both TX and RX.
+ <p>Protection level: signature|appop
+ -->
+ <permission android:name="android.permission.ACCESS_CALL_AUDIO"
+ android.label="@string/permlab_accessCallAudio"
+ android:description="@string/permdesc_accessCallAudio"
+ android:protectionLevel="signature|appop" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device microphone -->
<!-- ====================================================================== -->
@@ -1951,6 +1963,18 @@
android:description="@string/permdesc_modifyAudioSettings"
android:protectionLevel="normal" />
+ <!-- ======================================== -->
+ <!-- Permissions for factory reset protection -->
+ <!-- ======================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows an application to set a factory reset protection (FRP) policy.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_FACTORY_RESET_PROTECTION"
+ android:protectionLevel="signature|privileged"/>
+
<!-- ================================== -->
<!-- Permissions for accessing hardware -->
<!-- ================================== -->
@@ -4953,16 +4977,6 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
- <!-- Feature Id for Country Detector. -->
- <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
- <!-- Feature Id for Location service. -->
- <feature android:featureId="LocationService" android:label="@string/location_service"/>
- <!-- Feature Id for Sensor Notification service. -->
- <feature android:featureId="SensorNotificationService"
- android:label="@string/sensor_notification_service"/>
- <!-- Feature Id for Twilight service. -->
- <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
-
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 24a21ebcad80..c0de6936dd12 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -78,6 +78,14 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index b4e628620853..4359b1011064 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -94,6 +94,13 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
@@ -102,10 +109,7 @@
android:id="@+id/profile_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"
- android:dividerHeight="1dp"/>
+ android:minHeight="@dimen/resolver_empty_state_height" />
</FrameLayout>
</LinearLayout>
</TabHost>
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index d481eff9702c..c4f9ed90b49a 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -29,7 +29,6 @@
android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
android:scrollbarStyle="outsideOverlay"
- android:scrollIndicators="top|bottom"
android:divider="@null"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index b54673834ea9..72e8b0c2c5d5 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -175,6 +175,14 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
@@ -183,10 +191,7 @@
android:id="@+id/profile_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:dividerHeight="1dp"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"/>
+ android:minHeight="@dimen/resolver_empty_state_height" />
</FrameLayout>
</LinearLayout>
</TabHost>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e7ad8eba3188..d513e2b5a9e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -421,14 +421,6 @@
[CHAR LIMIT=NONE] -->
<string name="location_changed_notification_text">Tap to see your location settings.</string>
- <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
- <string name="country_detector">Country Detector</string>
- <!-- Feature Id for Location service. [CHAR LIMIT=NONE]-->
- <string name="location_service">Location Service</string>
- <!-- Feature Id for Sensor Notification service. [CHAR LIMIT=NONE]-->
- <string name="sensor_notification_service">Sensor Notification Service</string>
- <!-- Feature Id for Twilight service. [CHAR LIMIT=NONE]-->
- <string name="twilight_service">Twilight Service</string>
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
@@ -5372,4 +5364,9 @@
<string name="resolver_no_apps_available_explanation">We couldn\u2019t find any apps</string>
<!-- Button which switches on the disabled work profile [CHAR LIMIT=NONE] -->
<string name="resolver_switch_on_work">Switch on work</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permlab_accessCallAudio">Record or play audio in telephony calls</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_accessCallAudio">Allows this app, when assigned as default dialer application, to record or play audio in telephony calls.</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b8dd418da173..85c2a2a24749 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3874,6 +3874,7 @@
<java-symbol type="id" name="resolver_empty_state_title" />
<java-symbol type="id" name="resolver_empty_state_subtitle" />
<java-symbol type="id" name="resolver_empty_state_button" />
+ <java-symbol type="id" name="resolver_tab_divider" />
<java-symbol type="string" name="resolver_cant_share_with_work_apps" />
<java-symbol type="string" name="resolver_cant_share_with_personal_apps" />
<java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index ea0a0fd49f9f..37e8d5937bc5 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -244,6 +244,28 @@ public class PowerManagerTest extends AndroidTestCase {
}
@Test
+ public void testGetThermalHeadroom() throws Exception {
+ float headroom = mPm.getThermalHeadroom(0);
+ // If the device doesn't support thermal headroom, return early
+ if (Float.isNaN(headroom)) {
+ return;
+ }
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+ // Call again immediately to ensure rate limiting works
+ headroom = mPm.getThermalHeadroom(0);
+ assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom));
+
+ // Sleep for a second before attempting to call again so as to not get rate limited
+ Thread.sleep(1000);
+ headroom = mPm.getThermalHeadroom(5);
+ assertFalse("Expected data to still be available", Float.isNaN(headroom));
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+ }
+
+ @Test
public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() {
// Can't use assumption framework with AndroidTestCase :(
if (mPm.isRebootingUserspaceSupported()) {
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 1f831bb2f9a8..b41f90c4689e 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -69,6 +69,13 @@ public class CutoutSpecificationTest {
+ "z\n"
+ "@right\n"
+ "@bind_right_cutout\n"
+ + "@bottom\n"
+ + "M 0,0\n"
+ + "h -24\n"
+ + "v -48\n"
+ + "h 48\n"
+ + "v 48\n"
+ + "z\n"
+ "@dp";
private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
+ "h 1\n"
@@ -141,13 +148,66 @@ public class CutoutSpecificationTest {
}
@Test
+ public void parse_withBindMarker_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
public void parse_withBindMarker_shouldHaveRightBound() {
CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
}
@Test
- public void parse_tallCutout_shouldBeDone() {
+ public void parse_withBindMarker_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(456, 1752, 624, 1920));
+ }
+
+ @Test
+ public void parse_withBindMarker_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 168, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveLeftBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 540, 168, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveRightBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(1752, 540, 1920, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(876, 912, 1044, 1080));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_tallCutout_topBoundShouldMatchExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "L -44.3940446283, 36.0595537175\n"
@@ -162,7 +222,7 @@ public class CutoutSpecificationTest {
}
@Test
- public void parse_wideCutout_shouldBeDone() {
+ public void parse_wideCutout_topBoundShouldMatchExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -177,7 +237,7 @@ public class CutoutSpecificationTest {
}
@Test
- public void parse_narrowCutout_shouldBeDone() {
+ public void parse_narrowCutout_topBoundShouldHaveExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -24, 0\n"
+ "L -21.9940446283, 20.0595537175\n"
@@ -192,7 +252,7 @@ public class CutoutSpecificationTest {
}
@Test
- public void parse_doubleCutout_shouldBeDone() {
+ public void parse_doubleCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -217,7 +277,7 @@ public class CutoutSpecificationTest {
}
@Test
- public void parse_cornerCutout_shouldBeDone() {
+ public void parse_cornerCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "C -48,48 -48,48 0,48\n"
@@ -229,7 +289,7 @@ public class CutoutSpecificationTest {
}
@Test
- public void parse_holeCutout_shouldBeDone() {
+ public void parse_holeCutout_shouldMatchExpectedInset() {
CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
+ "h 136\n"
+ "v 136\n"
@@ -259,4 +319,38 @@ public class CutoutSpecificationTest {
assertThat(cutoutSpecification.getSafeInset())
.isEqualTo(new Rect(6, 0, 8, 0));
}
+
+ @Test
+ public void parse_bottomLeftSpec_withBindLeftMarker_shouldBeLeftBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@left\n"
+ + "@bind_left_cutout");
+
+ assertThat(cutoutSpecification.getLeftBound())
+ .isEqualTo(new Rect(0, 190, 10, 200));
+ }
+
+ @Test
+ public void parse_bottomRightSpec_withBindRightMarker_shouldBeRightBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@right\n"
+ + "@bind_right_cutout");
+
+ assertThat(cutoutSpecification.getRightBound())
+ .isEqualTo(new Rect(390, 190, 400, 200));
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ade1e0de7102..79e7c50e7987 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 38;
+ private static final int NUM_MARSHALLED_PROPERTIES = 39;
/**
* The number of properties that are purposely not marshalled
diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index dd5676c9c961..745393ce1a3d 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -30,8 +30,9 @@ message GraphicsStatsServiceDumpProto {
message GraphicsStatsProto {
enum PipelineType {
- GL = 0;
- VULKAN = 1;
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
}
// The package name of the app
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ba793e83f1fb..0af6cbf3cb40 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -547,16 +547,11 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
}
void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction,
- ASurfaceControl* aSurfaceControl, float frameRate) {
+ ASurfaceControl* aSurfaceControl, float frameRate,
+ int8_t compatibility) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
-
- sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
- if (frameRate < 0) {
- ALOGE("Failed to set frame ate - invalid frame rate");
- return;
- }
-
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- transaction->setFrameRate(surfaceControl, frameRate);
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ transaction->setFrameRate(surfaceControl, frameRate, compatibility);
}
diff --git a/packages/OsuLogin/AndroidManifest.xml b/packages/OsuLogin/AndroidManifest.xml
index 123559adf6fd..a428cb36e97e 100644
--- a/packages/OsuLogin/AndroidManifest.xml
+++ b/packages/OsuLogin/AndroidManifest.xml
@@ -17,7 +17,7 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.hotspot2">
+ package="com.android.hotspot2.osulogin">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
@@ -28,7 +28,7 @@
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation|screenSize"
android:supportsRtl="true">
- <activity android:name="com.android.hotspot2.osu.OsuLoginActivity"
+ <activity android:name="com.android.hotspot2.osulogin.OsuLoginActivity"
android:label="@string/action_bar_label"
android:theme="@style/AppTheme"
android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/packages/OsuLogin/res/layout/osu_web_view.xml b/packages/OsuLogin/res/layout/osu_web_view.xml
index fb3c06513430..4436aab39e13 100644
--- a/packages/OsuLogin/res/layout/osu_web_view.xml
+++ b/packages/OsuLogin/res/layout/osu_web_view.xml
@@ -3,7 +3,7 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context="com.android.hotspot2.osu.OsuLoginActivity">
+ tools:context="com.android.hotspot2.osulogin.OsuLoginActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osulogin/OsuLoginActivity.java
index 3a994d741956..d554745819e6 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osulogin/OsuLoginActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hotspot2.osu;
+package com.android.hotspot2.osulogin;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
@@ -42,8 +42,6 @@ import android.widget.Toast;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import com.android.hotspot2.R;
-
import java.net.MalformedURLException;
import java.net.URL;
@@ -194,7 +192,7 @@ public class OsuLoginActivity extends Activity {
// Check if the key event was the Back button.
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
// If there is a history to move back
- if (mWebView.canGoBack()){
+ if (mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
@@ -278,6 +276,6 @@ public class OsuLoginActivity extends Activity {
mPageError = true;
Log.e(TAG, "onReceived Error for MainFrame: " + error.getErrorCode());
}
- }
+ }
}
}
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index 28b2d21d0043..a5b8cfa5d60b 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -14,7 +14,7 @@
~ limitations under the License
-->
-<com.android.systemui.qs.QSCarrier
+<com.android.systemui.qs.carrier.QSCarrier
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_carrier"
android:layout_width="wrap_content"
@@ -46,4 +46,4 @@
android:singleLine="true"
android:maxEms="7"/>
-</com.android.systemui.qs.QSCarrier> \ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrier> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index f2b0606979cc..fd53a8bb1a7c 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -15,7 +15,7 @@
-->
<!-- Extends LinearLayout -->
-<com.android.systemui.qs.QSCarrierGroup
+<com.android.systemui.qs.carrier.QSCarrierGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_mobile"
android:layout_width="0dp"
@@ -71,4 +71,4 @@
android:layout_weight="1"
android:visibility="gone"/>
-</com.android.systemui.qs.QSCarrierGroup> \ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrierGroup> \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2d288ff40b2c..b1d39f59f789 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -123,4 +123,9 @@ interface ISystemUiProxy {
*/
void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
+
+ /**
+ * Sets the split-screen divider minimized state
+ */
+ void setSplitScreenMinimized(boolean minimized) = 22;
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index d317b7ebd8e7..69bc2596d411 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -60,6 +60,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
@@ -327,6 +328,7 @@ public class Dependency {
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
+ @Inject Lazy<Divider> mDivider;
@Inject
public Dependency() {
@@ -527,6 +529,7 @@ public class Dependency {
mProviders.put(AutoHideController.class, mAutoHideController::get);
mProviders.put(RecordingController.class, mRecordingController::get);
+ mProviders.put(Divider.class, mDivider::get);
sDependency = this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
deleted file mode 100644
index 5c0df179dd27..000000000000
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IDockedStackListener;
-import android.view.WindowManagerGlobal;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility wrapper to listen for whether or not a docked stack exists, to be
- * used for things like the different overview icon in that mode.
- */
-public class DockedStackExistsListener {
-
- private static final String TAG = "DockedStackExistsListener";
-
- private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
- private static boolean mLastExists;
-
- static {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- new IDockedStackListener.Stub() {
- @Override
- public void onDividerVisibilityChanged(boolean b) throws RemoteException {
-
- }
-
- @Override
- public void onDockedStackExistsChanged(boolean exists)
- throws RemoteException {
- DockedStackExistsListener.onDockedStackExistsChanged(exists);
- }
-
- @Override
- public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
- throws RemoteException {
-
- }
-
- @Override
- public void onAdjustedForImeChanged(boolean b, long l)
- throws RemoteException {
-
- }
-
- @Override
- public void onDockSideChanged(int i) throws RemoteException {
-
- }
- });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed registering docked stack exists listener", e);
- }
- }
-
-
- private static void onDockedStackExistsChanged(boolean exists) {
- mLastExists = exists;
- synchronized (sCallbacks) {
- sCallbacks.removeIf(wf -> {
- Consumer<Boolean> l = wf.get();
- if (l != null) l.accept(exists);
- return l == null;
- });
- }
- }
-
- public static void register(Consumer<Boolean> callback) {
- callback.accept(mLastExists);
- synchronized (sCallbacks) {
- sCallbacks.add(new WeakReference<>(callback));
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/TransactionPool.java b/packages/SystemUI/src/com/android/systemui/TransactionPool.java
new file mode 100644
index 000000000000..801cf8a7523b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/TransactionPool.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.util.Pools;
+import android.view.SurfaceControl;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
+ */
+@Singleton
+public class TransactionPool {
+ private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
+ new Pools.SynchronizedPool<>(4);
+
+ @Inject
+ TransactionPool() {
+ }
+
+ /** Gets a transaction from the pool. */
+ public SurfaceControl.Transaction acquire() {
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (t == null) {
+ return new SurfaceControl.Transaction();
+ }
+ return t;
+ }
+
+ /**
+ * Return a transaction to the pool. DO NOT call {@link SurfaceControl.Transaction#close()} if
+ * returning to pool.
+ */
+ public void release(SurfaceControl.Transaction t) {
+ if (!mTransactionPool.release(t)) {
+ t.close();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
index 7f45cc8f4c56..0329183e6048 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
@@ -38,10 +38,10 @@ import java.util.Locale;
class DistanceClassifier extends FalsingClassifier {
private static final float HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN = 1;
- private static final float VERTICAL_FLING_THRESHOLD_DISTANCE_IN = 1;
+ private static final float VERTICAL_FLING_THRESHOLD_DISTANCE_IN = 1.5f;
private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
- private static final float VELOCITY_TO_DISTANCE = 80f;
+ private static final float VELOCITY_TO_DISTANCE = 30f;
private static final float SCREEN_FRACTION_MAX_DISTANCE = 0.8f;
private final float mVerticalFlingThresholdPx;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index 957ea8d127d2..a796f3c6d547 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -42,8 +42,8 @@ class ZigZagClassifier extends FalsingClassifier {
// most swipes will follow somewhat of a 'C' or 'S' shape, we allow more deviance along the
// `SECONDARY` axis.
private static final float MAX_X_PRIMARY_DEVIANCE = .05f;
- private static final float MAX_Y_PRIMARY_DEVIANCE = .1f;
- private static final float MAX_X_SECONDARY_DEVIANCE = .6f;
+ private static final float MAX_Y_PRIMARY_DEVIANCE = .15f;
+ private static final float MAX_X_SECONDARY_DEVIANCE = .4f;
private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
private final float mMaxXPrimaryDeviance;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 019cb1459838..17aaff1f7383 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -25,10 +25,13 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.Animatable;
import android.util.AttributeSet;
+import android.util.Pair;
import android.util.SparseArray;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -42,6 +45,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
public class QSDetail extends LinearLayout {
@@ -274,6 +278,32 @@ public class QSDetail extends LinearLayout {
}
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ DisplayCutout cutout = insets.getDisplayCutout();
+
+ Pair<Integer, Integer> padding = StatusBarWindowView.cornerCutoutMargins(
+ cutout, getDisplay());
+
+ if (padding == null) {
+ mQsDetailHeader.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+ getPaddingTop(),
+ getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+ getPaddingBottom()
+ );
+ } else {
+ mQsDetailHeader.setPadding(
+ padding.first,
+ getPaddingTop(),
+ padding.second,
+ getPaddingBottom()
+ );
+ }
+
+ return super.onApplyWindowInsets(insets);
+ }
+
private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
mSwitchState = state;
if (mAnimatingOpen) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8cd70cf63b8e..d422dd7f4175 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -60,6 +60,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.qs.carrier.QSCarrierGroup;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
@@ -435,23 +436,22 @@ public class QuickStatusBarHeader extends RelativeLayout implements
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // Handle padding of SystemIconsView
DisplayCutout cutout = insets.getDisplayCutout();
-
- // Handle padding of QuickStatusBarHeader
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, mRoundedCornerPadding);
- setPadding(padding.first, 0, padding.second, getPaddingBottom());
-
- // Handle padding of SystemIconsView
final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start),
- waterfallTopInset,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end),
- 0);
+ int statusBarPaddingLeft = isLayoutRtl()
+ ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end)
+ : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start);
+ int statusBarPaddingRight = isLayoutRtl()
+ ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start)
+ : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end);
+ mSystemIconsView.setPadding(padding.first + statusBarPaddingLeft, waterfallTopInset,
+ padding.second + statusBarPaddingRight, 0);
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 867677a46a58..d899acbade4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import com.android.systemui.R;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
new file mode 100644
index 000000000000..663f3f0e9ddb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.carrier
+
+/**
+ * Represents the state of cell signal for a particular slot.
+ *
+ * To be used between [QSCarrierGroupController] and [QSCarrier].
+ */
+data class CellSignalState(
+ @JvmField val visible: Boolean = false,
+ @JvmField val mobileSignalIconId: Int = 0,
+ @JvmField val contentDescription: String? = null,
+ @JvmField val typeContentDescription: String? = null,
+ @JvmField val roaming: Boolean = false
+) {
+ /**
+ * Changes the visibility of this state by returning a copy with the visibility changed.
+ *
+ * If the visibility would not change, the same state is returned.
+ *
+ * @param visible the new visibility state
+ * @return `this` if `this.visible == visible`. Else, a new copy with the visibility changed.
+ */
+ fun changeVisibility(visible: Boolean): CellSignalState {
+ if (this.visible == visible) return this
+ else return copy(visible = visible)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 5a9c360e3104..ad275f1fa6ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -29,6 +29,7 @@ import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
+import com.android.systemui.qs.QuickStatusBarHeader;
import java.util.Objects;
@@ -41,7 +42,7 @@ public class QSCarrier extends LinearLayout {
private DualToneHandler mDualToneHandler;
private ColorStateList mColorForegroundStateList;
private float mColorForegroundIntensity;
- private QSCarrierGroupController.CellSignalState mLastSignalState;
+ private CellSignalState mLastSignalState;
public QSCarrier(Context context) {
super(context);
@@ -76,8 +77,13 @@ public class QSCarrier extends LinearLayout {
mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
}
- public void updateState(QSCarrierGroupController.CellSignalState state) {
- if (Objects.equals(state, mLastSignalState)) return;
+ /**
+ * Update the state of this view
+ * @param state the current state of the signal for this view
+ * @return true if the state was actually changed
+ */
+ public boolean updateState(CellSignalState state) {
+ if (Objects.equals(state, mLastSignalState)) return false;
mLastSignalState = state;
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
@@ -103,6 +109,7 @@ public class QSCarrier extends LinearLayout {
}
mMobileSignal.setContentDescription(contentDescription);
}
+ return true;
}
private boolean hasValidTypeContentDescription(String typeContentDescription) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
index 346c75da6a89..d03563ffb342 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index eb5b4cc4c0df..f9b14737332b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
@@ -29,7 +29,6 @@ import android.util.Log;
import android.view.View;
import android.widget.TextView;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
@@ -38,7 +37,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
-import java.util.Objects;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -82,11 +80,13 @@ public class QSCarrierGroupController {
Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
return;
}
- mInfos[slotIndex].visible = statusIcon.visible;
- mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
- mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
- mInfos[slotIndex].typeContentDescription = typeContentDescription.toString();
- mInfos[slotIndex].roaming = roaming;
+ mInfos[slotIndex] = new CellSignalState(
+ statusIcon.visible,
+ statusIcon.icon,
+ statusIcon.contentDescription,
+ typeContentDescription.toString(),
+ roaming
+ );
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@@ -94,7 +94,7 @@ public class QSCarrierGroupController {
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
}
}
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
@@ -236,7 +236,7 @@ public class QSCarrierGroupController {
+ info.subscriptionIds[i]);
continue;
}
- mInfos[slot].visible = true;
+ mInfos[slot] = mInfos[slot].changeVisibility(true);
slotSeen[slot] = true;
mCarrierGroups[slot].setCarrierText(
info.listOfCarriers[i].toString().trim());
@@ -244,7 +244,7 @@ public class QSCarrierGroupController {
}
for (int i = 0; i < SIM_SLOTS; i++) {
if (!slotSeen[i]) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
mCarrierGroups[i].setVisibility(View.GONE);
}
}
@@ -255,7 +255,7 @@ public class QSCarrierGroupController {
// No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
// info.carrierText in a different view.
for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
mCarrierGroups[i].setCarrierText("");
mCarrierGroups[i].setVisibility(View.GONE);
}
@@ -295,35 +295,6 @@ public class QSCarrierGroupController {
}
}
- static final class CellSignalState {
- boolean visible;
- int mobileSignalIconId;
- String contentDescription;
- String typeContentDescription;
- boolean roaming;
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) return true;
- if (!(obj instanceof CellSignalState)) return false;
- CellSignalState other = (CellSignalState) obj;
- return this.visible == other.visible
- && this.mobileSignalIconId == other.mobileSignalIconId
- && Objects.equals(this.contentDescription, other.contentDescription)
- && Objects.equals(this.typeContentDescription, other.typeContentDescription)
- && this.roaming == other.roaming;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(visible,
- mobileSignalIconId,
- contentDescription,
- typeContentDescription,
- roaming);
- }
- }
-
public static class Builder {
private QSCarrierGroup mView;
private final ActivityStarter mActivityStarter;
@@ -343,7 +314,7 @@ public class QSCarrierGroupController {
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
}
- Builder setQSCarrierGroup(QSCarrierGroup view) {
+ public Builder setQSCarrierGroup(QSCarrierGroup view) {
mView = view;
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ad4936494e2f..34cad51e1c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -380,6 +380,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
taskId, mHandler, null);
}
+ @Override
+ public void setSplitScreenMinimized(boolean minimized) {
+ Divider divider = mDividerOptional.get();
+ if (divider != null) {
+ divider.setMinimized(minimized);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 5ae095421a80..4f20492c60a3 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -22,10 +22,8 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LE
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
-import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
@@ -94,29 +92,24 @@ public class ShortcutKeyDispatcher extends SystemUI
}
private void handleDockKey(long shortcutCode) {
- try {
- int dockSide = mWindowManagerService.getDockedStackSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
- // Split the screen
- mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
- } else {
- // If there is already a docked window, we respond by resizing the docking pane.
- DividerView dividerView = mDivider.getView();
- DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
- int dividerPosition = dividerView.getCurrentPosition();
- DividerSnapAlgorithm.SnapTarget currentTarget =
- snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
- DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
- ? snapAlgorithm.getPreviousTarget(currentTarget)
- : snapAlgorithm.getNextTarget(currentTarget);
- dividerView.startDragging(true /* animate */, false /* touching */);
- dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
- true /* logMetrics */);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "handleDockKey() failed.");
+ if (mDivider == null || !mDivider.inSplitMode()) {
+ // Split the screen
+ mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+ } else {
+ // If there is already a docked window, we respond by resizing the docking pane.
+ DividerView dividerView = mDivider.getView();
+ DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+ int dividerPosition = dividerView.getCurrentPosition();
+ DividerSnapAlgorithm.SnapTarget currentTarget =
+ snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+ DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
+ ? snapAlgorithm.getPreviousTarget(currentTarget)
+ : snapAlgorithm.getNextTarget(currentTarget);
+ dividerView.startDragging(true /* animate */, false /* touching */);
+ dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
+ true /* logMetrics */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 90cc0e57f50c..2daefbde45a8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,69 +17,283 @@
package com.android.systemui.stackdivider;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
-import android.view.IDockedStackListener;
+import android.util.Slog;
+import android.view.IWindowContainer;
import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.WindowContainerTransaction;
+import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.TransactionPool;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayChangeController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplayLayout;
+import com.android.systemui.wm.SystemWindows;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Optional;
+import java.util.function.Consumer;
+
+import javax.inject.Singleton;
import dagger.Lazy;
/**
* Controls the docked stack divider.
*/
-public class Divider extends SystemUI implements DividerView.DividerCallbacks {
+@Singleton
+public class Divider extends SystemUI implements DividerView.DividerCallbacks,
+ DisplayController.OnDisplaysChangedListener {
private static final String TAG = "Divider";
+
+ static final boolean DEBUG = true;
+
+ static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
private final Optional<Lazy<Recents>> mRecentsOptionalLazy;
private DividerWindowManager mWindowManager;
private DividerView mView;
private final DividerState mDividerState = new DividerState();
- private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
private boolean mAdjustedForIme = false;
private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
+ private SystemWindows mSystemWindows;
+ final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private DisplayController mDisplayController;
+ private DisplayImeController mImeController;
+ final TransactionPool mTransactionPool;
+
+ // Keeps track of real-time split geometry including snap positions and ime adjustments
+ private SplitDisplayLayout mSplitLayout;
+
+ // Transient: this contains the layout calculated for a new rotation requested by WM. This is
+ // kept around so that we can wait for a matching configuration change and then use the exact
+ // layout that we sent back to WM.
+ private SplitDisplayLayout mRotateSplitLayout;
+
+ private Handler mHandler;
+ private KeyguardStateController mKeyguardStateController;
+
+ private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+ new ArrayList<>();
+
+ private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
+
+ private DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, t) -> {
+ DisplayLayout displayLayout =
+ new DisplayLayout(mDisplayController.getDisplayLayout(display));
+ SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
+ sdl.rotateTo(toRotation);
+ mRotateSplitLayout = sdl;
+ int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position
+ : mView.getCurrentPosition();
+ DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
+ final DividerSnapAlgorithm.SnapTarget target =
+ snap.calculateNonDismissingSnapTarget(position);
+ sdl.resizeSplits(target.position, t);
+
+ if (inSplitMode()) {
+ WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
+ }
+ };
+
+ private IWindowContainer mLastImeTarget = null;
+ private boolean mShouldAdjustForIme = false;
+
+ private DisplayImeController.ImePositionProcessor mImePositionProcessor =
+ new DisplayImeController.ImePositionProcessor() {
+ private int mStartTop = 0;
+ private int mFinalTop = 0;
+ @Override
+ public void onImeStartPositioning(int displayId, int imeTop, int finalImeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ mStartTop = imeTop;
+ mFinalTop = finalImeTop;
+ if (showing) {
+ try {
+ mLastImeTarget = ActivityTaskManager.getTaskOrganizerController()
+ .getImeTarget(displayId);
+ mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape()
+ && (mLastImeTarget.asBinder()
+ == mSplits.mSecondary.token.asBinder());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get IME target", e);
+ }
+ }
+ if (!mShouldAdjustForIme) {
+ setAdjustedForIme(false);
+ return;
+ }
+ mView.setAdjustedForIme(showing, showing
+ ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
+ : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
+ // Reposition the server's secondary split position so that it evaluates
+ // insets properly.
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (showing) {
+ mSplitLayout.updateAdjustedBounds(finalImeTop, imeTop, finalImeTop);
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
+ } else {
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ setAdjustedForIme(showing);
+ }
+
+ @Override
+ public void onImePositionChanged(int displayId, int imeTop,
+ SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ final boolean showing = mFinalTop < mStartTop;
+ final float progress = ((float) (imeTop - mStartTop)) / (mFinalTop - mStartTop);
+ final float fraction = showing ? progress : 1.f - progress;
+ mView.setResizeDimLayer(t, true /* primary */, fraction * 0.3f);
+ }
+
+ @Override
+ public void onImeEndPositioning(int displayId, int imeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ mView.setResizeDimLayer(t, true /* primary */, showing ? 0.3f : 0.f);
+ }
+ };
- public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+ public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, Handler handler,
+ KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
super(context);
+ mDisplayController = displayController;
+ mSystemWindows = systemWindows;
+ mImeController = imeController;
+ mHandler = handler;
+ mKeyguardStateController = keyguardStateController;
mRecentsOptionalLazy = recentsOptionalLazy;
+ mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
+ mTransactionPool = transactionPool;
}
@Override
public void start() {
- mWindowManager = new DividerWindowManager(mContext);
- update(mContext.getResources().getConfiguration());
- mDockDividerVisibilityListener = new DockDividerVisibilityListener();
+ mWindowManager = new DividerWindowManager(mSystemWindows);
+ mDisplayController.addDisplayWindowListener(this);
+ // Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
+ // everything, it is actually transparent except for notifications, so we still need to
+ // hide any surfaces that are below it.
+ // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+
+ }
+
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (!inSplitMode() || mView == null) {
+ return;
+ }
+ mView.setHidden(mKeyguardStateController.isShowing());
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+
+ }
+ });
+ // Don't initialize the divider or anything until we get the default display.
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ mImeController.addPositionProcessor(mImePositionProcessor);
+ mDisplayController.addDisplayChangingController(mRotationController);
try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- mDockDividerVisibilityListener);
+ mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession);
+ // Set starting tile bounds based on middle target
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ mSplitLayout.resizeSplits(midPos, tct);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
} catch (Exception e) {
- Log.e(TAG, "Failed to register docked stack listener", e);
+ Slog.e(TAG, "Failed to register docked stack listener", e);
}
- mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+ update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ if (mRotateSplitLayout == null) {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ mSplitLayout.resizeSplits(midPos, tct);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ } else if (mRotateSplitLayout != null
+ && mSplitLayout.mDisplayLayout.rotation()
+ == mRotateSplitLayout.mDisplayLayout.rotation()) {
+ mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
+ mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
+ mRotateSplitLayout = null;
+ }
update(newConfig);
}
+ Handler getHandler() {
+ return mHandler;
+ }
+
public DividerView getView() {
return mView;
}
@@ -92,18 +306,25 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
return mHomeStackResizable;
}
+ /** {@code true} if this is visible */
+ public boolean inSplitMode() {
+ return mView != null && mView.getVisibility() == View.VISIBLE;
+ }
+
private void addDivider(Configuration configuration) {
+ Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
- LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
- mView.injectDependencies(mWindowManager, mDividerState, this);
+ LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+ DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
+ mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- final int size = mContext.getResources().getDimensionPixelSize(
+ final int size = dctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? size : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : size;
- mWindowManager.add(mView, width, height);
+ final int width = landscape ? size : displayLayout.width();
+ final int height = landscape ? displayLayout.height() : size;
+ mWindowManager.add(mView, width, height, mContext.getDisplayId());
}
private void removeDivider() {
@@ -116,63 +337,84 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
private void update(Configuration configuration) {
removeDivider();
addDivider(configuration);
- if (mMinimized) {
+ if (mMinimized && mView != null) {
mView.setMinimizedDockStack(true, mHomeStackResizable);
updateTouchable();
}
}
- private void updateVisibility(final boolean visible) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- if (mVisible != visible) {
- mVisible = visible;
- mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
- // Update state because animations won't finish.
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- }
+ void updateVisibility(final boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+
+ if (visible) {
+ mView.enterSplitMode(mHomeStackResizable);
+ // Update state because animations won't finish.
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
+ } else {
+ mView.exitSplitMode();
+ // un-minimize so that next entry triggers minimize anim.
+ mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
}
- });
+ // Notify existence listeners
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.removeIf(wf -> {
+ Consumer<Boolean> l = wf.get();
+ if (l != null) l.accept(visible);
+ return l == null;
+ });
+ }
+ }
+ }
+
+ private void setHomeStackResizable(boolean resizable) {
+ if (mHomeStackResizable == resizable) {
+ return;
+ }
+ mHomeStackResizable = resizable;
+ if (!inSplitMode()) {
+ return;
+ }
+ WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token);
}
private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
final boolean isHomeStackResizable) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mHomeStackResizable = isHomeStackResizable;
- if (mMinimized != minimized) {
- mMinimized = minimized;
- updateTouchable();
- if (animDuration > 0) {
- mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
- } else {
- mView.setMinimizedDockStack(minimized, isHomeStackResizable);
- }
- }
- }
- });
+ setHomeStackResizable(isHomeStackResizable);
+ if (animDuration > 0) {
+ mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
+ } else {
+ mView.setMinimizedDockStack(minimized, isHomeStackResizable);
+ }
+ updateTouchable();
}
- private void notifyDockedStackExistsChanged(final boolean exists) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mForcedResizableController.notifyDockedStackExistsChanged(exists);
+ /** Switch to minimized state if appropriate */
+ public void setMinimized(final boolean minimized) {
+ mHandler.post(() -> {
+ if (!inSplitMode()) {
+ return;
+ }
+ if (mMinimized == minimized) {
+ return;
}
+ mMinimized = minimized;
+ mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
+ updateTouchable();
});
}
- private void updateTouchable() {
- mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
+ void setAdjustedForIme(boolean adjustedForIme) {
+ if (mAdjustedForIme == adjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = adjustedForIme;
+ updateTouchable();
}
- public void onRecentsActivityStarting() {
- if (mView != null) {
- mView.onRecentsActivityStarting();
- }
+ private void updateTouchable() {
+ mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
/**
@@ -206,6 +448,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
}
public void onAppTransitionFinished() {
+ if (mView == null) {
+ return;
+ }
mForcedResizableController.onAppTransitionFinished();
}
@@ -231,46 +476,66 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
}
- class DockDividerVisibilityListener extends IDockedStackListener.Stub {
+ long getAnimDuration() {
+ float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen
+ .config_appTransitionAnimationDurationScaleDefault));
+ final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
+ return (long) (transitionDuration * transitionScale);
+ }
- @Override
- public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
- updateVisibility(visible);
+ /** Register a listener that gets called whenever the existence of the divider changes */
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ listener.accept(inSplitMode());
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.add(new WeakReference<>(listener));
}
+ }
- @Override
- public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
- notifyDockedStackExistsChanged(exists);
- }
+ void startEnterSplit() {
+ // Set resizable directly here because applyEnterSplit already resizes home stack.
+ mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+ }
- @Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
- boolean isHomeStackResizable) throws RemoteException {
- mHomeStackResizable = isHomeStackResizable;
- updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
+ void ensureMinimizedSplit() {
+ final boolean wasMinimized = mMinimized;
+ mMinimized = true;
+ setHomeStackResizable(mSplits.mSecondary.isResizable());
+ if (!inSplitMode()) {
+ // Wasn't in split-mode yet, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " entering split mode with minimized=true");
+ }
+ updateVisibility(true /* visible */);
+ } else if (!wasMinimized) {
+ if (DEBUG) {
+ Log.d(TAG, " in split mode, but minimizing ");
+ }
+ // Was already in split-mode, update just minimized state.
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
+ }
- @Override
- public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
- throws RemoteException {
- mView.post(() -> {
- if (mAdjustedForIme != adjustedForIme) {
- mAdjustedForIme = adjustedForIme;
- updateTouchable();
- if (!mMinimized) {
- if (animDuration > 0) {
- mView.setAdjustedForIme(adjustedForIme, animDuration);
- } else {
- mView.setAdjustedForIme(adjustedForIme);
- }
- }
- }
- });
+ void ensureNormalSplit() {
+ if (!inSplitMode()) {
+ // Wasn't in split-mode, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " enter split mode unminimized ");
+ }
+ mMinimized = false;
+ updateVisibility(true /* visible */);
}
-
- @Override
- public void onDockSideChanged(final int newDockSide) throws RemoteException {
- mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+ if (mMinimized) {
+ // Was in minimized state, so leave that.
+ if (DEBUG) {
+ Log.d(TAG, " in split mode already, but unminimizing ");
+ }
+ mMinimized = false;
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 49f4d5e91659..3b7f3152ec76 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -17,8 +17,15 @@
package com.android.systemui.stackdivider;
import android.content.Context;
+import android.os.Handler;
+import com.android.systemui.TransactionPool;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.SystemWindows;
import java.util.Optional;
@@ -35,7 +42,11 @@ import dagger.Provides;
public class DividerModule {
@Singleton
@Provides
- static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
- return new Divider(context, recentsOptionalLazy);
+ static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, @Main Handler handler,
+ KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
+ return new Divider(context, recentsOptionalLazy, displayController, systemWindows,
+ imeController, handler, keyguardStateController, transactionPool);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9fe6e844e0d2..375d9bbbd4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,12 +16,8 @@
package com.android.systemui.stackdivider;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -40,10 +36,11 @@ import android.os.Message;
import android.util.AttributeSet;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -75,6 +72,7 @@ import com.android.systemui.statusbar.FlingAnimationUtils;
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
+ private static final String TAG = "DividerView";
public interface DividerCallbacks {
void onDraggingStart();
@@ -123,14 +121,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private int mTouchSlop;
private boolean mBackgroundLifted;
private boolean mIsInMinimizeInteraction;
- private SnapTarget mSnapTargetBeforeMinimized;
+ SnapTarget mSnapTargetBeforeMinimized;
private int mDividerInsets;
private final Display mDefaultDisplay;
- private int mDisplayWidth;
- private int mDisplayHeight;
- private int mDisplayRotation;
- private int mDividerWindowWidth;
+
private int mDividerSize;
private int mTouchElevation;
private int mLongPressEntraceAnimDuration;
@@ -147,8 +142,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
- private DividerSnapAlgorithm mSnapAlgorithm;
- private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
+ private SplitDisplayLayout mSplitLayout;
private DividerCallbacks mCallback;
private final Rect mStableInsets = new Rect();
@@ -163,6 +157,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private DividerState mState;
private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
+ private SplitScreenTaskOrganizer mTiles;
+ boolean mFirstLayout = true;
+ int mDividerPositionX;
+ int mDividerPositionY;
// The view is removed or in the process of been removed from the system.
private boolean mRemoved;
@@ -172,7 +170,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESIZE_STACK:
- resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+ resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
break;
default:
super.handleMessage(msg);
@@ -228,16 +226,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public boolean performAccessibilityAction(View host, int action, Bundle args) {
int currentPosition = getCurrentPosition();
SnapTarget nextTarget = null;
+ DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm();
if (action == R.id.action_move_tl_full) {
- nextTarget = mSnapAlgorithm.getDismissEndTarget();
+ nextTarget = snapAlgorithm.getDismissEndTarget();
} else if (action == R.id.action_move_tl_70) {
- nextTarget = mSnapAlgorithm.getLastSplitTarget();
+ nextTarget = snapAlgorithm.getLastSplitTarget();
} else if (action == R.id.action_move_tl_50) {
- nextTarget = mSnapAlgorithm.getMiddleTarget();
+ nextTarget = snapAlgorithm.getMiddleTarget();
} else if (action == R.id.action_move_tl_30) {
- nextTarget = mSnapAlgorithm.getFirstSplitTarget();
+ nextTarget = snapAlgorithm.getFirstSplitTarget();
} else if (action == R.id.action_move_rb_full) {
- nextTarget = mSnapAlgorithm.getDismissStartTarget();
+ nextTarget = snapAlgorithm.getDismissStartTarget();
}
if (nextTarget != null) {
startDragging(true /* animate */, false /* touching */);
@@ -284,11 +283,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground = findViewById(R.id.docked_divider_background);
mMinimizedShadow = findViewById(R.id.minimized_dock_shadow);
mHandle.setOnTouchListener(this);
- mDividerWindowWidth = getResources().getDimensionPixelSize(
+ final int dividerWindowWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ mDividerSize = dividerWindowWidth - 2 * mDividerInsets;
mTouchElevation = getResources().getDimensionPixelSize(
R.dimen.docked_stack_divider_lift_elevation);
mLongPressEntraceAnimDuration = getResources().getInteger(
@@ -296,7 +295,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f);
- updateDisplayInfo();
boolean landscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
@@ -314,6 +312,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
&& !mIsInMinimizeInteraction) {
saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
}
+ mFirstLayout = true;
}
void onDividerRemoved() {
@@ -341,17 +340,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|| mStableInsets.bottom != insets.getStableInsetBottom()) {
mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
insets.getStableInsetRight(), insets.getStableInsetBottom());
- if (mSnapAlgorithm != null || mMinimizedSnapAlgorithm != null) {
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
- }
}
return super.onApplyWindowInsets(insets);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mFirstLayout) {
+ // Wait for first layout so that the ViewRootImpl surface has been created.
+ initializeSurfaceState();
+ mFirstLayout = false;
+ }
super.onLayout(changed, left, top, right, bottom);
int minimizeLeft = 0;
int minimizeTop = 0;
@@ -372,19 +371,16 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
- DividerCallbacks callback) {
+ DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl) {
mWindowManager = windowManager;
mState = dividerState;
mCallback = callback;
-
- // Set the previous position ratio before minimized state after attaching this divider
- if (mStableInsets.isEmpty()) {
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- }
+ mTiles = tiles;
+ mSplitLayout = sdl;
if (mState.mRatioPositionBeforeMinimized == 0) {
// Set the middle target as the initial state
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
+ mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
repositionSnapTargetBeforeMinimized();
}
@@ -411,18 +407,35 @@ public class DividerView extends FrameLayout implements OnTouchListener,
return mOtherTaskRect;
}
+ private boolean inSplitMode() {
+ return getVisibility() == VISIBLE;
+ }
+
+ /** Unlike setVisible, this directly hides the surface without changing view visibility. */
+ void setHidden(boolean hidden) {
+ post(() -> {
+ final SurfaceControl sc = getWindowSurfaceControl();
+ if (sc == null) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ if (hidden) {
+ t.hide(sc);
+ } else {
+ t.show(sc);
+ }
+ t.apply();
+ mTiles.releaseTransaction(t);
+ });
+ }
+
public boolean startDragging(boolean animate, boolean touching) {
cancelFlingAnimation();
if (touching) {
mHandle.setTouching(true, animate);
}
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
- // Update snap algorithm if rotation has occurred
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
- updateDisplayInfo();
- }
- initializeSnapAlgorithm();
mWindowManagerProxy.setResizing(true);
if (touching) {
mWindowManager.setSlippery(false);
@@ -431,7 +444,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mCallback != null) {
mCallback.onDraggingStart();
}
- return mDockSide != WindowManager.DOCKED_INVALID;
+ return inSplitMode();
}
public void stopDragging(int position, float velocity, boolean avoidDismissStart,
@@ -467,38 +480,22 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private void updateDockSide() {
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
mMinimizedShadow.setDockSide(mDockSide);
}
- private void initializeSnapAlgorithm() {
- if (mSnapAlgorithm == null) {
- mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
- mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide);
- if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) {
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
- }
- }
- if (mMinimizedSnapAlgorithm == null) {
- mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
- mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
- mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable);
- }
- }
-
public DividerSnapAlgorithm getSnapAlgorithm() {
- initializeSnapAlgorithm();
- return mDockedStackMinimized && mHomeStackResizable ? mMinimizedSnapAlgorithm :
- mSnapAlgorithm;
+ return mDockedStackMinimized
+ && mHomeStackResizable ? mSplitLayout.getMinimizedSnapAlgorithm()
+ : mSplitLayout.getSnapAlgorithm();
}
public int getCurrentPosition() {
- getLocationOnScreen(mTempInt2);
- if (isHorizontalDivision()) {
- return mTempInt2[1] + mDividerInsets;
- } else {
- return mTempInt2[0] + mDividerInsets;
- }
+ return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX;
+ }
+
+ public boolean isMinimized() {
+ return mDockedStackMinimized;
}
@Override
@@ -557,25 +554,25 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private void logResizeEvent(SnapTarget snapTarget) {
- if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
LOG_VALUE_RESIZE_50_50);
- } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_SMALLER
: LOG_VALUE_RESIZE_DOCKED_LARGER);
- } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_LARGER
@@ -625,12 +622,16 @@ public class DividerView extends FrameLayout implements OnTouchListener,
: snapTarget.taskPosition,
snapTarget));
Runnable endAction = () -> {
- commitSnapFlags(snapTarget);
+ boolean dismissed = commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
updateDockSide();
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
+ if (!dismissed) {
+ WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
+ ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+ }
if (mCallback != null) {
mCallback.onDraggingEnd();
}
@@ -642,12 +643,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
// position isn't negative.
final SnapTarget saveTarget;
if (snapTarget.position < 0) {
- saveTarget = mSnapAlgorithm.getMiddleTarget();
+ saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
saveTarget = snapTarget;
}
- if (saveTarget.position != mSnapAlgorithm.getDismissEndTarget().position
- && saveTarget.position != mSnapAlgorithm.getDismissStartTarget().position) {
+ final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm();
+ if (saveTarget.position != snapAlgo.getDismissEndTarget().position
+ && saveTarget.position != snapAlgo.getDismissStartTarget().position) {
saveSnapTargetBeforeMinimized(saveTarget);
}
}
@@ -701,11 +703,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
- private void commitSnapFlags(SnapTarget target) {
+ private boolean commitSnapFlags(SnapTarget target) {
if (target.flag == SnapTarget.FLAG_NONE) {
- return;
+ return false;
}
- boolean dismissOrMaximize;
+ final boolean dismissOrMaximize;
if (target.flag == SnapTarget.FLAG_DISMISS_START) {
dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
|| mDockSide == WindowManager.DOCKED_TOP;
@@ -713,12 +715,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|| mDockSide == WindowManager.DOCKED_BOTTOM;
}
- if (dismissOrMaximize) {
- mWindowManagerProxy.dismissDockedStack();
- } else {
- mWindowManagerProxy.maximizeDockedStack();
- }
- mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
+ mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
+ Transaction t = mTiles.getTransaction();
+ setResizeDimLayer(t, true /* primary */, 0f);
+ setResizeDimLayer(t, false /* primary */, 0f);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ return true;
}
private void liftBackground() {
@@ -765,6 +768,28 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackgroundLifted = false;
}
+ private void initializeSurfaceState() {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ // Recalculate the split-layout's internal tile bounds
+ mSplitLayout.resizeSplits(midPos);
+ Transaction t = mTiles.getTransaction();
+ if (mDockedStackMinimized) {
+ int position = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget().position;
+ calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
+ mDividerPositionX = mDividerPositionY = position;
+ resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary,
+ mOtherRect, mSplitLayout.mSecondary);
+ } else {
+ resizeSplitSurfaces(t, mSplitLayout.mPrimary, null,
+ mSplitLayout.mSecondary, null);
+ }
+ setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
+ setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ }
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -789,15 +814,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mDockedStackMinimized = minimized;
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
+ if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) {
// Splitscreen to minimize is about to starts after rotating landscape to seascape,
// update insets, display info and snap algorithm targets
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
repositionSnapTargetBeforeMinimized();
- updateDisplayInfo();
- } else {
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
}
if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) {
cancelFlingAnimation();
@@ -805,15 +826,64 @@ public class DividerView extends FrameLayout implements OnTouchListener,
// Relayout to recalculate the divider shadow when minimizing
requestLayout();
mIsInMinimizeInteraction = true;
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
} else {
- resizeStack(mSnapTargetBeforeMinimized);
+ resizeStackSurfaces(mSnapTargetBeforeMinimized);
mIsInMinimizeInteraction = false;
}
}
}
}
+ void enterSplitMode(boolean isHomeStackResizable) {
+ post(() -> {
+ final SurfaceControl sc = getWindowSurfaceControl();
+ if (sc == null) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.show(sc).apply();
+ mTiles.releaseTransaction(t);
+ });
+ if (isHomeStackResizable) {
+ SnapTarget miniMid = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget();
+ if (mDockedStackMinimized) {
+ mDividerPositionY = mDividerPositionX = miniMid.position;
+ }
+ }
+ }
+
+ /**
+ * Tries to grab a surface control from ViewRootImpl. If this isn't available for some reason
+ * (ie. the window isn't ready yet), it will get the surfacecontrol that the WindowlessWM has
+ * assigned to it.
+ */
+ private SurfaceControl getWindowSurfaceControl() {
+ if (getViewRootImpl() == null) {
+ return null;
+ }
+ SurfaceControl out = getViewRootImpl().getSurfaceControl();
+ if (out != null && out.isValid()) {
+ return out;
+ }
+ return mWindowManager.mSystemWindows.getViewSurface(this);
+ }
+
+ void exitSplitMode() {
+ // Reset tile bounds
+ post(() -> {
+ final SurfaceControl sc = getWindowSurfaceControl();
+ if (sc == null) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.hide(sc).apply();
+ mTiles.releaseTransaction(t);
+ });
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
+ }
+
public void setMinimizedDockStack(boolean minimized, long animDuration,
boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -844,14 +914,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mIsInMinimizeInteraction = true;
- mMinimizedSnapAlgorithm = null;
mDockedStackMinimized = minimized;
- initializeSnapAlgorithm();
stopDragging(minimized
? mSnapTargetBeforeMinimized.position
: getCurrentPosition(),
minimized
- ? mMinimizedSnapAlgorithm.getMiddleTarget()
+ ? mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget()
: mSnapTargetBeforeMinimized,
animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
setAdjustedForIme(false, animDuration);
@@ -865,18 +933,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
.start();
}
- public void setAdjustedForIme(boolean adjustedForIme) {
- updateDockSide();
- mHandle.setAlpha(adjustedForIme ? 0f : 1f);
- if (!adjustedForIme) {
- resetBackground();
- } else if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.setScaleY(ADJUSTED_FOR_IME_SCALE);
- }
- mAdjustedForIme = adjustedForIme;
- }
-
public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
updateDockSide();
mHandle.animate()
@@ -902,7 +958,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void saveSnapTargetBeforeMinimized(SnapTarget target) {
mSnapTargetBeforeMinimized = target;
mState.mRatioPositionBeforeMinimized = (float) target.position /
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth);
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width());
}
private void resetBackground() {
@@ -916,51 +973,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateDisplayInfo();
- }
-
- public void notifyDockSideChanged(int newDockSide) {
- int oldDockSide = mDockSide;
- mDockSide = newDockSide;
- mMinimizedShadow.setDockSide(mDockSide);
- requestLayout();
-
- // Update the snap position to the new docked side with correct insets
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
-
- if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
- || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
- repositionSnapTargetBeforeMinimized();
- }
-
- // Landscape to seascape rotation requires minimized to resize docked app correctly
- if (mHomeStackResizable && mDockedStackMinimized) {
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
- }
}
private void repositionSnapTargetBeforeMinimized() {
int position = (int) (mState.mRatioPositionBeforeMinimized *
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
- mSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width()));
// Set the snap target before minimized but do not save until divider is attached and not
// minimized because it does not know its minimized state yet.
- mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- }
-
- private void updateDisplayInfo() {
- mDisplayRotation = mDefaultDisplay.getRotation();
- final DisplayInfo info = new DisplayInfo();
- mDefaultDisplay.getDisplayInfo(info);
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ mSnapTargetBeforeMinimized =
+ mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position);
}
private int calculatePosition(int touchX, int touchY) {
@@ -994,8 +1017,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
- mDisplayHeight, mDividerSize);
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
+ mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
+ mDividerSize);
}
public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
@@ -1005,16 +1029,60 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mSfChoreographer.scheduleAtSfVsync(mHandler, message);
}
- private void resizeStack(SnapTarget taskSnapTarget) {
- resizeStack(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+ private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
+ resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+ }
+
+ void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
+ resizeSplitSurfaces(t, dockedRect, null, otherRect, null);
}
- public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect,
+ Rect otherRect, Rect otherTaskRect) {
+ dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect;
+ otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect;
+
+ mDividerPositionX = dockedRect.right;
+ mDividerPositionY = dockedRect.bottom;
+
+ t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
+ Rect crop = new Rect(dockedRect);
+ crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
+ -Math.min(dockedTaskRect.top - dockedRect.top, 0));
+ t.setWindowCrop(mTiles.mPrimarySurface, crop);
+ t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top);
+ crop.set(otherRect);
+ crop.offsetTo(-(otherTaskRect.left - otherRect.left),
+ -(otherTaskRect.top - otherRect.top));
+ t.setWindowCrop(mTiles.mSecondarySurface, crop);
+ final SurfaceControl dividerCtrl = getWindowSurfaceControl();
+ if (dividerCtrl != null) {
+ if (isHorizontalDivision()) {
+ t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets);
+ } else {
+ t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
+ }
+ }
+ }
+
+ void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
+ SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
+ if (alpha <= 0.f) {
+ t.hide(dim);
+ } else {
+ t.setAlpha(dim, alpha);
+ t.show(dim);
+ }
+ }
+
+ void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) {
if (mRemoved) {
// This divider view has been removed so shouldn't have any additional influence.
return;
}
calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
return;
@@ -1025,6 +1093,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground.invalidate();
}
+ Transaction t = mTiles.getTransaction();
mLastResizeRect.set(mDockedRect);
if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1037,8 +1106,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
- mDockedTaskRect.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
+ mOtherTaskRect);
+ t.apply();
+ mTiles.releaseTransaction(t);
return;
}
@@ -1052,8 +1123,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
mOtherTaskRect);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1066,8 +1136,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mDockSide == DOCKED_RIGHT) {
mDockedTaskRect.offset(position - mStableInsets.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
@@ -1078,7 +1147,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(),
+ mSplitLayout.mDisplayLayout.height());
alignTopLeft(mDockedRect, mDockedTaskRect);
alignTopLeft(mOtherRect, mOtherTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1094,15 +1164,15 @@ public class DividerView extends FrameLayout implements OnTouchListener,
taskPositionDocked);
applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
taskPositionOther);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else {
- mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
+ resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null);
}
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
- mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
- getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
+ setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
+ t.apply();
+ mTiles.releaseTransaction(t);
}
private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1156,10 +1226,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
SnapTarget snapTarget) {
if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
- return Math.max(mSnapAlgorithm.getFirstSplitTarget().position, mStartPosition);
+ return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position,
+ mStartPosition);
} else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
&& dockSideBottomRight(dockSide)) {
- return Math.min(mSnapAlgorithm.getLastSplitTarget().position, mStartPosition);
+ return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position,
+ mStartPosition);
} else {
return taskPosition;
}
@@ -1171,19 +1243,19 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
int position, int taskPosition) {
float fraction = Math.min(1, Math.max(0,
- mSnapAlgorithm.calculateDismissingFraction(position)));
+ mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position)));
SnapTarget dismissTarget = null;
SnapTarget splitTarget = null;
int start = 0;
- if (position <= mSnapAlgorithm.getLastSplitTarget().position
+ if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideTopLeft(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissStartTarget();
- splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget();
start = taskPosition;
- } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
+ } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideBottomRight(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissEndTarget();
- splitTarget = mSnapAlgorithm.getLastSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget();
start = splitTarget.position;
}
if (dismissTarget != null && fraction > 0f
@@ -1236,14 +1308,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
- private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
- if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+ private boolean isDismissTargetPrimary(SnapTarget dismissTarget) {
+ return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
|| (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
- && dockSideBottomRight(mDockSide))) {
- return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- } else {
- return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
+ && dockSideBottomRight(mDockSide));
}
/**
@@ -1297,7 +1365,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void onDockedFirstAnimationFrame() {
- saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onDockedTopTask() {
@@ -1307,8 +1375,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
updateDockSide();
mEntranceAnimationRunning = true;
- resizeStack(calculatePositionForInsetBounds(), mSnapAlgorithm.getMiddleTarget().position,
- mSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(calculatePositionForInsetBounds(),
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onRecentsDrawn() {
@@ -1337,13 +1406,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void onUndockingTask() {
- int dockSide = mWindowManagerProxy.getDockSide();
- if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
- || !mDockedStackMinimized)) {
+ int dockSide = mSplitLayout.getPrimarySplitSide();
+ if (inSplitMode() && (mHomeStackResizable || !mDockedStackMinimized)) {
startDragging(false /* animate */, false /* touching */);
SnapTarget target = dockSideTopLeft(dockSide)
- ? mSnapAlgorithm.getDismissEndTarget()
- : mSnapAlgorithm.getDismissStartTarget();
+ ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget()
+ : mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
// Don't start immediately - give a little bit time to settle the drag resize change.
mExitAnimationRunning = true;
@@ -1354,8 +1422,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private int calculatePositionForInsetBounds() {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- mTmpRect.inset(mStableInsets);
+ mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect);
return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2486d6534e8d..3020a25dfa47 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -26,12 +26,13 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.view.View;
import android.view.WindowManager;
+import com.android.systemui.wm.SystemWindows;
+
/**
* Manages the window parameters of the docked stack divider.
*/
@@ -39,15 +40,16 @@ public class DividerWindowManager {
private static final String WINDOW_TITLE = "DockedStackDivider";
- private final WindowManager mWindowManager;
+ final SystemWindows mSystemWindows;
private WindowManager.LayoutParams mLp;
private View mView;
- public DividerWindowManager(Context ctx) {
- mWindowManager = ctx.getSystemService(WindowManager.class);
+ public DividerWindowManager(SystemWindows systemWindows) {
+ mSystemWindows = systemWindows;
}
- public void add(View view, int width, int height) {
+ /** Add a divider view */
+ public void add(View view, int width, int height, int displayId) {
mLp = new WindowManager.LayoutParams(
width, height, TYPE_DOCK_DIVIDER,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
@@ -60,13 +62,13 @@ public class DividerWindowManager {
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mWindowManager.addView(view, mLp);
+ mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER);
mView = view;
}
public void remove() {
if (mView != null) {
- mWindowManager.removeView(mView);
+ mSystemWindows.removeView(mView);
}
mView = null;
}
@@ -81,7 +83,7 @@ public class DividerWindowManager {
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
@@ -95,7 +97,7 @@ public class DividerWindowManager {
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index c6ac309a0f4e..db7996eed7f0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -31,6 +31,8 @@ import com.android.systemui.R;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.function.Consumer;
+
/**
* Controller that decides when to show the {@link ForcedResizableInfoActivity}.
*/
@@ -52,6 +54,12 @@ public class ForcedResizableInfoActivityController {
}
};
+ private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
+ if (!exists) {
+ mPackagesShownInSession.clear();
+ }
+ };
+
/** Record of force resized task that's pending to be handled. */
private class PendingTaskRecord {
int taskId;
@@ -67,7 +75,7 @@ public class ForcedResizableInfoActivityController {
}
}
- public ForcedResizableInfoActivityController(Context context) {
+ public ForcedResizableInfoActivityController(Context context, Divider divider) {
mContext = context;
ActivityManagerWrapper.getInstance().registerTaskStackListener(
new TaskStackChangeListener() {
@@ -87,12 +95,7 @@ public class ForcedResizableInfoActivityController {
activityLaunchOnSecondaryDisplayFailed();
}
});
- }
-
- public void notifyDockedStackExistsChanged(boolean exists) {
- if (!exists) {
- mPackagesShownInSession.clear();
- }
+ divider.registerInSplitScreenListener(mDockedStackExistsListener);
}
public void onAppTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
new file mode 100644
index 000000000000..b19f560f2f50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 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.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.TypedValue;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.wm.DisplayLayout;
+
+/**
+ * Handles split-screen related internal display layout. In general, this represents the
+ * WM-facing understanding of the splits.
+ */
+public class SplitDisplayLayout {
+ /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top stack remains visible.*/
+ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+ private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
+ SplitScreenTaskOrganizer mTiles;
+ DisplayLayout mDisplayLayout;
+ Context mContext;
+
+ // Lazy stuff
+ boolean mResourcesValid = false;
+ int mDividerSize;
+ int mDividerSizeInactive;
+ private DividerSnapAlgorithm mSnapAlgorithm = null;
+ private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null;
+ Rect mPrimary = null;
+ Rect mSecondary = null;
+ Rect mAdjustedPrimary = null;
+ Rect mAdjustedSecondary = null;
+
+ public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) {
+ mTiles = taskTiles;
+ mDisplayLayout = dl;
+ mContext = ctx;
+ }
+
+ void rotateTo(int newRotation) {
+ mDisplayLayout.rotateTo(mContext.getResources(), newRotation);
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = mDisplayLayout.getOrientation();
+ Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density());
+ config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density());
+ mContext = mContext.createConfigurationContext(config);
+ mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
+ mResourcesValid = false;
+ }
+
+ private void updateResources() {
+ if (mResourcesValid) {
+ return;
+ }
+ mResourcesValid = true;
+ Resources res = mContext.getResources();
+ mDividerSize = DockedDividerUtils.getDividerSize(res,
+ DockedDividerUtils.getDividerInsets(res));
+ mDividerSizeInactive = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics());
+ }
+
+ int getPrimarySplitSide() {
+ return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP;
+ }
+
+ boolean isMinimized() {
+ return mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS;
+ }
+
+ DividerSnapAlgorithm getSnapAlgorithm() {
+ if (mSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide());
+ }
+ return mSnapAlgorithm;
+ }
+
+ DividerSnapAlgorithm getMinimizedSnapAlgorithm() {
+ if (mMinimizedSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(),
+ true /* isMinimized */);
+ }
+ return mMinimizedSnapAlgorithm;
+ }
+
+ void resizeSplits(int position) {
+ mPrimary = mPrimary == null ? new Rect() : mPrimary;
+ mSecondary = mSecondary == null ? new Rect() : mSecondary;
+ calcSplitBounds(position, mPrimary, mSecondary);
+ }
+
+ void resizeSplits(int position, WindowContainerTransaction t) {
+ resizeSplits(position);
+ t.setBounds(mTiles.mPrimary.token, mPrimary);
+ t.setBounds(mTiles.mSecondary.token, mSecondary);
+
+ t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
+ t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
+ }
+
+ void calcSplitBounds(int position, @NonNull Rect outPrimary, @NonNull Rect outSecondary) {
+ int dockSide = getPrimarySplitSide();
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outPrimary,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), outSecondary, mDisplayLayout.width(),
+ mDisplayLayout.height(), mDividerSize);
+ }
+
+ Rect calcMinimizedHomeStackBounds() {
+ DividerSnapAlgorithm.SnapTarget miniMid = getMinimizedSnapAlgorithm().getMiddleTarget();
+ Rect homeBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(miniMid.position,
+ DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+ return homeBounds;
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ void updateAdjustedBounds(int currImeTop, int startTop, int finalTop) {
+ updateAdjustedBounds(mDisplayLayout, currImeTop, startTop, finalTop, mDividerSize,
+ mDividerSizeInactive, mPrimary, mSecondary);
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ private void updateAdjustedBounds(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ adjustForIME(dl, currImeTop, startTop, finalTop, dividerWidth, dividerWidthInactive,
+ primaryBounds, secondaryBounds);
+ }
+
+ /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */
+ private void adjustForIME(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ if (mAdjustedPrimary == null) {
+ mAdjustedPrimary = new Rect();
+ mAdjustedSecondary = new Rect();
+ }
+
+ final Rect displayStableRect = new Rect();
+ dl.getStableBounds(displayStableRect);
+
+ final boolean showing = finalTop < startTop;
+ final float progress = ((float) (currImeTop - startTop)) / (finalTop - startTop);
+ final float dividerSquish = showing ? progress : 1.f - progress;
+ final int currDividerWidth =
+ (int) (dividerWidthInactive * dividerSquish + dividerWidth * (1.f - dividerSquish));
+
+ final int minTopStackBottom = displayStableRect.top
+ + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+ final int minImeTop = minTopStackBottom + currDividerWidth;
+
+ // Calculate an offset which shifts the stacks up by the height of the IME, but still
+ // leaves at least 30% of the top stack visible.
+ final int yOffset = Math.max(0, dl.height() - Math.max(currImeTop, minImeTop));
+
+ // TOP
+ // Reduce the offset by an additional small amount to squish the divider bar.
+ mAdjustedPrimary.set(primaryBounds);
+ mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth));
+
+ // BOTTOM
+ mAdjustedSecondary.set(secondaryBounds);
+ mAdjustedSecondary.offset(0, -yOffset);
+ }
+
+ static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl,
+ Rect bounds) {
+ int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(),
+ DockedDividerUtils.getDividerInsets(context.getResources()));
+
+ int minWidth = Integer.MAX_VALUE;
+
+ // Go through all screen orientations and find the orientation in which the task has the
+ // smallest width.
+ Rect tmpRect = new Rect();
+ Rect rotatedDisplayRect = new Rect();
+ Rect displayRect = new Rect(0, 0, dl.width(), dl.height());
+
+ DisplayLayout tmpDL = new DisplayLayout();
+ for (int rotation = 0; rotation < 4; rotation++) {
+ tmpDL.set(dl);
+ tmpDL.rotateTo(context.getResources(), rotation);
+ DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
+
+ tmpRect.set(bounds);
+ DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation());
+ rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
+ final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
+ tmpDL.getOrientation());
+ final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide,
+ dividerSize);
+
+ final int snappedPosition =
+ snap.calculateNonDismissingSnapTarget(position).position;
+ DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect,
+ tmpDL.width(), tmpDL.height(), dividerSize);
+ Rect insettedDisplay = new Rect(rotatedDisplayRect);
+ insettedDisplay.inset(tmpDL.stableInsets());
+ tmpRect.intersect(insettedDisplay);
+ minWidth = Math.min(tmpRect.width(), minWidth);
+ }
+ return (int) (minWidth / dl.density());
+ }
+
+ static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl,
+ int dividerSize) {
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = dl.getOrientation();
+ Rect tmpRect = new Rect(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / dl.density());
+ config.screenHeightDp = (int) (tmpRect.height() / dl.density());
+ final Context rotationContext = context.createConfigurationContext(config);
+ return new DividerSnapAlgorithm(
+ rotationContext.getResources(), dl.width(), dl.height(), dividerSize,
+ config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets());
+ }
+
+ /**
+ * Get the current primary-split side. Determined by its location of {@param bounds} within
+ * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+ * if allowed in its respected {@param orientation}.
+ *
+ * @param bounds bounds of the primary split task to get which side is docked
+ * @param displayRect bounds of the display that contains the primary split task
+ * @param orientation the origination of device
+ * @return current primary-split side
+ */
+ static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) {
+ if (orientation == ORIENTATION_PORTRAIT) {
+ // Portrait mode, docked either at the top or the bottom.
+ final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+ if (diff < 0) {
+ return DOCKED_BOTTOM;
+ } else {
+ // Top is default
+ return DOCKED_TOP;
+ }
+ } else if (orientation == ORIENTATION_LANDSCAPE) {
+ // Landscape mode, docked either on the left or on the right.
+ final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+ if (diff < 0) {
+ return DOCKED_RIGHT;
+ }
+ return DOCKED_LEFT;
+ }
+ return DOCKED_INVALID;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
new file mode 100644
index 000000000000..5cc87996269b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2020 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.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
+ private static final String TAG = "SplitScreenTaskOrganizer";
+ private static final boolean DEBUG = Divider.DEBUG;
+
+ RunningTaskInfo mPrimary;
+ RunningTaskInfo mSecondary;
+ SurfaceControl mPrimarySurface;
+ SurfaceControl mSecondarySurface;
+ SurfaceControl mPrimaryDim;
+ SurfaceControl mSecondaryDim;
+ final Divider mDivider;
+
+ SplitScreenTaskOrganizer(Divider divider) {
+ mDivider = divider;
+ }
+
+ void init(ITaskOrganizerController organizerController, SurfaceSession session)
+ throws RemoteException {
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimarySurface = mPrimary.token.getLeash();
+ mSecondarySurface = mSecondary.token.getLeash();
+
+ // Initialize dim surfaces:
+ mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface)
+ .setColorLayer().setName("Primary Divider Dim").build();
+ mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface)
+ .setColorLayer().setName("Secondary Divider Dim").build();
+ SurfaceControl.Transaction t = getTransaction();
+ t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+ t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+ t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+ t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ t.apply();
+ releaseTransaction(t);
+ }
+
+ SurfaceControl.Transaction getTransaction() {
+ return mDivider.mTransactionPool.acquire();
+ }
+
+ void releaseTransaction(SurfaceControl.Transaction t) {
+ mDivider.mTransactionPool.release(t);
+ }
+
+ @Override
+ public void taskAppeared(RunningTaskInfo taskInfo) {
+ }
+
+ @Override
+ public void taskVanished(IWindowContainer container) {
+ }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ if (taskInfo.displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
+ }
+
+ /**
+ * This is effectively a finite state machine which moves between the various split-screen
+ * presentations based on the contents of the split regions.
+ */
+ private void handleTaskInfoChanged(RunningTaskInfo info) {
+ final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (info.token.asBinder() == mPrimary.token.asBinder()) {
+ mPrimary = info;
+ } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
+ mSecondary = info;
+ }
+ final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (DEBUG) {
+ Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary);
+ }
+ if (primaryIsEmpty || secondaryIsEmpty) {
+ // At-least one of the splits is empty which means we are currently transitioning
+ // into or out-of split-screen mode.
+ if (DEBUG) {
+ Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
+ + " " + mSecondary.topActivityType);
+ }
+ if (mDivider.inSplitMode()) {
+ // Was in split-mode, which means we are leaving split, so continue that.
+ // This happens when the stack in the primary-split is dismissed.
+ if (DEBUG) {
+ Log.d(TAG, " was in split, so this means leave it "
+ + mPrimary.topActivityType + " " + mSecondary.topActivityType);
+ }
+ WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
+ mDivider.updateVisibility(false /* visible */);
+ } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
+ // Wasn't in split-mode (both were empty), but now that the primary split is
+ // populated, we should fully enter split by moving everything else into secondary.
+ // This just tells window-manager to reparent things, the UI will respond
+ // when it gets new task info for the secondary split.
+ if (DEBUG) {
+ Log.d(TAG, " was not in split, but primary is populated, so enter it");
+ }
+ mDivider.startEnterSplit();
+ }
+ } else if (mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS) {
+ // Both splits are populated but the secondary split has a home/recents stack on top,
+ // so enter minimized mode.
+ mDivider.ensureMinimizedSplit();
+ } else {
+ // Both splits are populated by normal activities, so make sure we aren't minimized.
+ mDivider.ensureNormalSplit();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 228aab5e3eec..76857337f73e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,16 +16,25 @@
package com.android.systemui.stackdivider;
-import static android.view.WindowManager.DOCKED_INVALID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Display;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -35,88 +44,20 @@ import java.util.concurrent.Executors;
public class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy";
+ private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
@GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
- private final Rect mTempDockedTaskRect = new Rect();
- private final Rect mTempDockedInsetRect = new Rect();
- private final Rect mTempOtherTaskRect = new Rect();
- private final Rect mTempOtherInsetRect = new Rect();
private final Rect mTmpRect1 = new Rect();
- private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
- private final Rect mTmpRect4 = new Rect();
- private final Rect mTmpRect5 = new Rect();
@GuardedBy("mDockedRect")
private final Rect mTouchableRegion = new Rect();
- private boolean mDimLayerVisible;
- private int mDimLayerTargetWindowingMode;
- private float mDimLayerAlpha;
-
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final Runnable mResizeRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mDockedRect) {
- mTmpRect1.set(mDockedRect);
- mTmpRect2.set(mTempDockedTaskRect);
- mTmpRect3.set(mTempDockedInsetRect);
- mTmpRect4.set(mTempOtherTaskRect);
- mTmpRect5.set(mTempOtherInsetRect);
- }
- try {
- ActivityTaskManager.getService()
- .resizeDockedStack(mTmpRect1,
- mTmpRect2.isEmpty() ? null : mTmpRect2,
- mTmpRect3.isEmpty() ? null : mTmpRect3,
- mTmpRect4.isEmpty() ? null : mTmpRect4,
- mTmpRect5.isEmpty() ? null : mTmpRect5);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDismissRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(false /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove stack: " + e);
- }
- }
- };
-
- private final Runnable mMaximizeRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(true /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDimLayerRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
- mDimLayerTargetWindowingMode, mDimLayerAlpha);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
@Override
public void run() {
@@ -139,40 +80,9 @@ public class WindowManagerProxy {
return sInstance;
}
- public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
- Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
- synchronized (mDockedRect) {
- mDockedRect.set(docked);
- if (tempDockedTaskRect != null) {
- mTempDockedTaskRect.set(tempDockedTaskRect);
- } else {
- mTempDockedTaskRect.setEmpty();
- }
- if (tempDockedInsetRect != null) {
- mTempDockedInsetRect.set(tempDockedInsetRect);
- } else {
- mTempDockedInsetRect.setEmpty();
- }
- if (tempOtherTaskRect != null) {
- mTempOtherTaskRect.set(tempOtherTaskRect);
- } else {
- mTempOtherTaskRect.setEmpty();
- }
- if (tempOtherInsetRect != null) {
- mTempOtherInsetRect.set(tempOtherInsetRect);
- } else {
- mTempOtherInsetRect.setEmpty();
- }
- }
- mExecutor.execute(mResizeRunnable);
- }
-
- public void dismissDockedStack() {
- mExecutor.execute(mDismissRunnable);
- }
-
- public void maximizeDockedStack() {
- mExecutor.execute(mMaximizeRunnable);
+ void dismissOrMaximizeDocked(
+ final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
+ mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
}
public void setResizing(final boolean resizing) {
@@ -188,26 +98,204 @@ public class WindowManagerProxy {
});
}
- public int getDockSide() {
+ /** Sets a touch region */
+ public void setTouchRegion(Rect region) {
+ synchronized (mDockedRect) {
+ mTouchableRegion.set(region);
+ }
+ mExecutor.execute(mSetTouchableRegionRunnable);
+ }
+
+ static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ splitLayout.resizeSplits(position, t);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private static boolean getHomeAndRecentsTasks(List<IWindowContainer> out,
+ IWindowContainer parent) {
+ boolean resizable = false;
try {
- return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
+ List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
+ ? ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+ : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent,
+ HOME_AND_RECENTS);
+ for (int i = 0, n = rootTasks.size(); i < n; ++i) {
+ final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
+ out.add(ti.token);
+ if (ti.topActivityType == ACTIVITY_TYPE_HOME) {
+ resizable = ti.isResizable();
+ }
+ }
} catch (RemoteException e) {
- Log.w(TAG, "Failed to get dock side: " + e);
}
- return DOCKED_INVALID;
+ return resizable;
}
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- mDimLayerVisible = visible;
- mDimLayerTargetWindowingMode = targetWindowingMode;
- mDimLayerAlpha = alpha;
- mExecutor.execute(mDimLayerRunnable);
+ static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
+ applyHomeTasksMinimized(layout, parent, null /* transaction */);
}
- public void setTouchRegion(Rect region) {
- synchronized (mDockedRect) {
- mTouchableRegion.set(region);
+ /**
+ * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
+ * split is minimized. This actually "sticks out" of the secondary split area, but when in
+ * minimized mode, the secondary split gets a 'negative' crop to expose it.
+ */
+ static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent,
+ WindowContainerTransaction t) {
+ // Resize the home/recents stacks to the larger minimized-state size
+ final Rect homeBounds;
+ final ArrayList<IWindowContainer> homeStacks = new ArrayList<>();
+ boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent);
+ if (isHomeResizable) {
+ homeBounds = layout.calcMinimizedHomeStackBounds();
+ } else {
+ homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(),
+ layout.mDisplayLayout.height());
+ }
+ WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction();
+ for (int i = homeStacks.size() - 1; i >= 0; --i) {
+ wct.setBounds(homeStacks.get(i), homeBounds);
+ }
+ if (t != null) {
+ return isHomeResizable;
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to resize home stacks ", e);
+ }
+ return isHomeResizable;
+ }
+
+ /**
+ * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split.
+ * This assumes there is already something in the primary split since that is usually what
+ * triggers a call to this. In the same transaction, this overrides the home task bounds via
+ * {@link #applyHomeTasksMinimized}.
+ *
+ * @return whether the home stack is resizable
+ */
+ static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
+ try {
+ // Set launchtile first so that any stack created after
+ // getAllStackInfos and before reparent (even if unlikely) are placed
+ // correctly.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(
+ DEFAULT_DISPLAY, tiles.mSecondary.token);
+ List<ActivityManager.RunningTaskInfo> rootTasks =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY,
+ null /* activityTypes */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (rootTasks.isEmpty()) {
+ return false;
+ }
+ for (int i = rootTasks.size() - 1; i >= 0; --i) {
+ if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_FULLSCREEN) {
+ continue;
+ }
+ wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
+ true /* onTop */);
+ }
+ boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ return isHomeResizable;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e);
+ }
+ return false;
+ }
+
+ private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
+ final int atype = ti.configuration.windowConfiguration.getActivityType();
+ return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
+ }
+
+ /**
+ * Reparents all tile members back to their display and resets home task override bounds.
+ * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary
+ * split (thus resulting in the top of the secondary split becoming
+ * fullscreen. {@code false} resolves the other way.
+ */
+ static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) {
+ try {
+ // Set launch root first so that any task created after getChildContainers and
+ // before reparent (pretty unlikely) are put into fullscreen.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY,
+ null);
+ // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
+ // plus specific APIs to clean this up.
+ List<ActivityManager.RunningTaskInfo> primaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mPrimary.token, null /* activityTypes */);
+ List<ActivityManager.RunningTaskInfo> secondaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mSecondary.token, null /* activityTypes */);
+ // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
+ // as a result, the above will not capture any tasks; yet, we need to clean-up the
+ // home task bounds.
+ List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+ if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
+ && freeHomeAndRecents.isEmpty()) {
+ return;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (dismissOrMaximize) {
+ // Dismissing, so move all primary split tasks first
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Don't need to worry about home tasks because they are already in the "proper"
+ // order within the secondary split.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ if (isHomeOrRecentTask(ti)) {
+ wct.setBounds(ti.token, null);
+ }
+ }
+ } else {
+ // Maximize, so move non-home secondary split first
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ if (isHomeOrRecentTask(secondaryChildren.get(i))) {
+ continue;
+ }
+ wct.reparent(secondaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Find and place home tasks in-between. This simulates the fact that there was
+ // nothing behind the primary split's tasks.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ if (isHomeOrRecentTask(ti)) {
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ // reset bounds too
+ wct.setBounds(ti.token, null);
+ }
+ }
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ }
+ for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
+ wct.setBounds(freeHomeAndRecents.get(i).token, null);
+ }
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
}
- mExecutor.execute(mSetTouchableRegionRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d0af106d4a0c..d0a601015ec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar;
-import static com.android.systemui.Dependency.MAIN_HANDLER;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
@@ -36,7 +35,6 @@ import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.AsyncTask;
-import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -52,6 +50,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -73,6 +72,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -110,7 +110,7 @@ public class NotificationMediaManager implements Dumpable {
@Nullable
private LockscreenWallpaper mLockscreenWallpaper;
- private final Handler mHandler = Dependency.get(MAIN_HANDLER);
+ private final Executor mMainExecutor;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
@@ -181,7 +181,8 @@ public class NotificationMediaManager implements Dumpable {
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController) {
+ KeyguardBypassController keyguardBypassController,
+ @Main Executor mainExecutor) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -194,6 +195,7 @@ public class NotificationMediaManager implements Dumpable {
mStatusBarLazy = statusBarLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mEntryManager = notificationEntryManager;
+ mMainExecutor = mainExecutor;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
@@ -623,7 +625,7 @@ public class NotificationMediaManager implements Dumpable {
mBackdrop.setVisibility(View.GONE);
mBackdropFront.animate().cancel();
mBackdropBack.setImageDrawable(null);
- mHandler.post(mHideBackdropFront);
+ mMainExecutor.execute(mHideBackdropFront);
});
if (mKeyguardStateController.isKeyguardFadingAway()) {
mBackdrop.animate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 0b37c229555f..cd5bb775de8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -43,6 +43,8 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
+import java.util.concurrent.Executor;
+
import javax.inject.Singleton;
import dagger.Lazy;
@@ -88,14 +90,16 @@ public interface StatusBarDependenciesModule {
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController) {
+ KeyguardBypassController keyguardBypassController,
+ @Main Executor mainExecutor) {
return new NotificationMediaManager(
context,
statusBarLazy,
notificationShadeWindowController,
notificationEntryManager,
mediaArtworkProcessor,
- keyguardBypassController);
+ keyguardBypassController,
+ mainExecutor);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 93f58053f486..55a20fae4ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,10 +51,10 @@ import android.util.Pair;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
@@ -80,11 +80,13 @@ public class InstantAppNotifier extends SystemUI
private final CommandQueue mCommandQueue;
private boolean mDockedStackExists;
private KeyguardStateController mKeyguardStateController;
+ private final Divider mDivider;
@Inject
public InstantAppNotifier(Context context, CommandQueue commandQueue,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor, Divider divider) {
super(context);
+ mDivider = divider;
mCommandQueue = commandQueue;
mUiBgExecutor = uiBgExecutor;
}
@@ -103,7 +105,7 @@ public class InstantAppNotifier extends SystemUI
mCommandQueue.addCallback(this);
mKeyguardStateController.addCallback(this);
- DockedStackExistsListener.register(
+ mDivider.registerInSplitScreenListener(
exists -> {
mDockedStackExists = exists;
updateForegroundInstantApps();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 2981252f148c..e612c07ac18a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
+import java.util.Comparator
import java.util.Objects
import javax.inject.Inject
@@ -73,6 +74,9 @@ open class NotificationRankingManager @Inject constructor(
val aIsPeople = a.isPeopleNotification()
val bIsPeople = b.isPeopleNotification()
+ val aIsImportantPeople = a.isImportantPeopleNotification()
+ val bIsImportantPeople = b.isImportantPeopleNotification()
+
val aMedia = isImportantMedia(a)
val bMedia = isImportantMedia(b)
@@ -87,6 +91,8 @@ open class NotificationRankingManager @Inject constructor(
when {
usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+ usePeopleFiltering && aIsImportantPeople != bIsImportantPeople ->
+ if (aIsImportantPeople) -1 else 1
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
// Provide consistent ranking with headsUpManager
aHeadsUp -> headsUpManager.compare(a, b)
@@ -192,6 +198,9 @@ open class NotificationRankingManager @Inject constructor(
private fun NotificationEntry.isPeopleNotification() =
peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)
+ private fun NotificationEntry.isImportantPeopleNotification() =
+ peopleNotificationIdentifier.isImportantPeopleNotification(sbn, ranking)
+
private fun NotificationEntry.isHighPriority() =
highPriorityProvider.isHighPriority(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 4672de046c49..e15fa2eac4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -23,6 +23,7 @@ import javax.inject.Singleton
interface PeopleNotificationIdentifier {
fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
+ fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
}
@Singleton
@@ -32,4 +33,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor(
override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
ranking.isConversation || personExtractor.isPersonNotification(sbn)
+
+ override fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
+ isPeopleNotification(sbn, ranking) && ranking.channel.isImportantConversation
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 551731824570..234ab936345b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1197,6 +1197,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onConfigurationChanged();
}
+ if (mImageResolver != null) {
+ mImageResolver.updateMaxImageSizes();
+ }
}
public void onUiModeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 248e5feba703..60eda06dd3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -112,7 +113,8 @@ public class NotificationConversationInfo extends LinearLayout implements
boolean mSkipPost = false;
@Retention(SOURCE)
- @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+ @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
+ ACTION_UNBUBBLE, ACTION_SETTINGS})
private @interface Action {}
static final int ACTION_BUBBLE = 0;
static final int ACTION_HOME = 1;
@@ -128,8 +130,6 @@ public class NotificationConversationInfo extends LinearLayout implements
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
- Settings.Global.putInt(
- mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
}
closeControls(v, true);
};
@@ -225,7 +225,9 @@ public class NotificationConversationInfo extends LinearLayout implements
mShortcutInfo = shortcuts.get(0);
}
- mIsBubbleable = mEntry.getBubbleMetadata() != null;
+ mIsBubbleable = mEntry.getBubbleMetadata() != null
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
mStartedAsBubble = mEntry.isBubble();
createConversationChannelIfNeeded();
@@ -382,6 +384,11 @@ public class NotificationConversationInfo extends LinearLayout implements
((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
}
+ private boolean bubbleImportantConversations() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
+ }
+
private void bindDelegate() {
TextView delegateView = findViewById(R.id.delegate_name);
@@ -579,6 +586,10 @@ public class NotificationConversationInfo extends LinearLayout implements
case ACTION_FAVORITE:
mChannelToUpdate.setImportantConversation(
!mChannelToUpdate.isImportantConversation());
+ if (mChannelToUpdate.isImportantConversation()
+ && bubbleImportantConversations()) {
+ mChannelToUpdate.setAllowBubbles(true);
+ }
break;
case ACTION_MUTE:
if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index fa4bc2aba21a..52f7c2cfee96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -19,12 +19,17 @@ package com.android.systemui.statusbar.notification.row;
import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageResolver;
import com.android.internal.widget.LocalImageResolver;
import com.android.internal.widget.MessagingMessage;
@@ -36,6 +41,10 @@ import java.util.Set;
/**
* Custom resolver with built-in image cache for image messages.
+ *
+ * If the URL points to a bitmap that's larger than the maximum width or height, the bitmap
+ * will be resized down to that maximum size before being cached. See {@link #getMaxImageWidth()},
+ * {@link #getMaxImageHeight()}, and {@link #resolveImage(Uri)} for the downscaling implementation.
*/
public class NotificationInlineImageResolver implements ImageResolver {
private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
@@ -44,6 +53,13 @@ public class NotificationInlineImageResolver implements ImageResolver {
private final ImageCache mImageCache;
private Set<Uri> mWantedUriSet;
+ // max allowed bitmap width, in pixels
+ @VisibleForTesting
+ protected int mMaxImageWidth;
+ // max allowed bitmap height, in pixels
+ @VisibleForTesting
+ protected int mMaxImageHeight;
+
/**
* Constructor.
* @param context Context.
@@ -56,6 +72,8 @@ public class NotificationInlineImageResolver implements ImageResolver {
if (mImageCache != null) {
mImageCache.setImageResolver(this);
}
+
+ updateMaxImageSizes();
}
/**
@@ -66,14 +84,49 @@ public class NotificationInlineImageResolver implements ImageResolver {
return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
}
+ private boolean isLowRam() {
+ return ActivityManager.isLowRamDeviceStatic();
+ }
+
+ /**
+ * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+ */
+ public void updateMaxImageSizes() {
+ mMaxImageWidth = getMaxImageWidth();
+ mMaxImageHeight = getMaxImageHeight();
+ }
+
+ @VisibleForTesting
+ protected int getMaxImageWidth() {
+ return mContext.getResources().getDimensionPixelSize(isLowRam()
+ ? R.dimen.notification_custom_view_max_image_width_low_ram
+ : R.dimen.notification_custom_view_max_image_width);
+ }
+
+ @VisibleForTesting
+ protected int getMaxImageHeight() {
+ return mContext.getResources().getDimensionPixelSize(isLowRam()
+ ? R.dimen.notification_custom_view_max_image_height_low_ram
+ : R.dimen.notification_custom_view_max_image_height);
+ }
+
+ @VisibleForTesting
+ protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException {
+ return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext);
+ }
+
/**
- * To resolve image from specified uri directly.
+ * To resolve image from specified uri directly. If the resulting image is larger than the
+ * maximum allowed size, scale it down.
* @param uri Uri of the image.
* @return Drawable of the image.
* @throws IOException Throws if failed at resolving the image.
*/
Drawable resolveImage(Uri uri) throws IOException {
- return LocalImageResolver.resolveImage(uri, mContext);
+ BitmapDrawable image = resolveImageInternal(uri);
+ Bitmap bitmap = image.getBitmap();
+ image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
+ return image;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d790cbc4a54d..84aecd4e0759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,7 +63,6 @@ import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistHandleViewController;
@@ -75,6 +74,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.policy.DeadZone;
@@ -869,7 +869,8 @@ public class NavigationBarView extends FrameLayout implements
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
- DockedStackExistsListener.register(mDockedListener);
+ Divider divider = Dependency.get(Divider.class);
+ divider.registerInSplitScreenListener(mDockedListener);
updateOrientationViews();
reloadNavIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index d8d96c1845b8..0d3b09a634e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -163,7 +163,6 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -1412,8 +1411,11 @@ public class StatusBar extends SystemUI implements DemoMode,
if (!mRecentsOptional.isPresent()) {
return false;
}
- int dockSide = WindowManagerProxy.getInstance().getDockSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
+ Divider divider = null;
+ if (mDividerOptional.isPresent()) {
+ divider = mDividerOptional.get();
+ }
+ if (divider == null || !divider.inSplitMode()) {
final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
@@ -1423,16 +1425,13 @@ public class StatusBar extends SystemUI implements DemoMode,
: SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
} else {
- if (mDividerOptional.isPresent()) {
- Divider divider = mDividerOptional.get();
- if (divider.isMinimized() && !divider.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- divider.onUndockingTask();
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
- }
+ if (divider.isMinimized() && !divider.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
+ } else {
+ divider.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index f6e1681e7b58..cebcf760a990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -15,24 +15,18 @@
*/
package com.android.systemui.statusbar.policy;
-import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
-
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
-import android.telephony.DataSpecificRegistrationInfo;
-import android.telephony.NetworkRegistrationInfo;
+import android.telephony.DisplayInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -60,16 +54,10 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class MobileSignalController extends SignalController<
MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
-
- // The message to display Nr5G icon gracfully by CarrierConfig timeout
- private static final int MSG_DISPLAY_GRACE = 1;
-
private final TelephonyManager mPhone;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
@@ -86,19 +74,15 @@ public class MobileSignalController extends SignalController<
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
// of code.
- private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private boolean mCA = false;
- private boolean mCAPlus = false;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
+ private DisplayInfo mDisplayInfo = new DisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
private ServiceState mServiceState;
private SignalStrength mSignalStrength;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
- private final Handler mDisplayGraceHandler;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- @VisibleForTesting
- boolean mIsShowingIconGracefully = false;
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
@@ -136,16 +120,6 @@ public class MobileSignalController extends SignalController<
updateTelephony();
}
};
-
- mDisplayGraceHandler = new Handler(receiverLooper) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_DISPLAY_GRACE) {
- mIsShowingIconGracefully = false;
- updateTelephony();
- }
- }
- };
}
public void setConfiguration(Config config) {
@@ -190,7 +164,8 @@ public class MobileSignalController extends SignalController<
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY
| PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE
- | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE
+ | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),
true, mObserver);
mContext.getContentResolver().registerContentObserver(Global.getUriFor(
@@ -268,52 +243,60 @@ public class MobileSignalController extends SignalController<
mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.FOUR_G);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
- TelephonyIcons.FOUR_G);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G);
} else {
- mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
- TelephonyIcons.FOUR_G_PLUS);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G_PLUS);
}
} else {
mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
- TelephonyIcons.LTE);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE);
} else {
- mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
- TelephonyIcons.LTE_PLUS);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE_PLUS);
}
}
- mNetworkToIconLookup.put(toIconKeyCAPlus(TelephonyManager.NETWORK_TYPE_LTE),
- TelephonyIcons.LTE_CA_5G_E);
mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
TelephonyIcons.WFC);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO), TelephonyIcons.LTE_CA_5G_E);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA), TelephonyIcons.NR_5G);
+ mNetworkToIconLookup.put(toDisplayIconKey(
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE), TelephonyIcons.NR_5G_PLUS);
}
private String getIconKey() {
- if (mCA) {
- return toIconKeyCA(mDataNetType);
- } else if (mCAPlus) {
- return toIconKeyCAPlus(mDataNetType);
+ if (mDisplayInfo.getOverrideNetworkType() == DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
+ return toIconKey(mDisplayInfo.getNetworkType());
} else {
- return toIconKey(mDataNetType);
+ return toDisplayIconKey(mDisplayInfo.getOverrideNetworkType());
}
}
- // Some specific carriers have 5GE network which is special CA network.
- private String toIconKeyCAPlus(@Annotation.NetworkType int networkType) {
- return toIconKeyCA(networkType) + "_Plus";
- }
-
- private String toIconKeyCA(@Annotation.NetworkType int networkType) {
- return toIconKey(networkType) + "_CA";
- }
-
private String toIconKey(@Annotation.NetworkType int networkType) {
return Integer.toString(networkType);
}
+ private String toDisplayIconKey(@Annotation.OverrideNetworkType int displayNetworkType) {
+ switch (displayNetworkType) {
+ case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA:
+ return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA";
+ case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
+ return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
+ case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
+ return "5G";
+ case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
+ return "5G_Plus";
+ default:
+ return "unsupported";
+ }
+ }
+
private void updateInflateSignalStrength() {
mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext,
mSubscriptionInfo.getSubscriptionId());
@@ -465,26 +448,6 @@ public class MobileSignalController extends SignalController<
}
}
- private boolean isCarrierSpecificDataIcon() {
- if (mConfig.patternOfCarrierSpecificDataIcon == null
- || mConfig.patternOfCarrierSpecificDataIcon.length() == 0) {
- return false;
- }
-
- Pattern stringPattern = Pattern.compile(mConfig.patternOfCarrierSpecificDataIcon);
- String[] operatorNames = new String[]{mServiceState.getOperatorAlphaLongRaw(),
- mServiceState.getOperatorAlphaShortRaw()};
- for (String opName : operatorNames) {
- if (!TextUtils.isEmpty(opName)) {
- Matcher matcher = stringPattern.matcher(opName);
- if (matcher.find()) {
- return true;
- }
- }
- }
- return false;
- }
-
/**
* Updates the network's name based on incoming spn and plmn.
*/
@@ -538,18 +501,18 @@ public class MobileSignalController extends SignalController<
}
/**
- * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
- * mDataState, and mSimState. It should be called any time one of these is updated.
+ * Updates the current state based on mServiceState, mSignalStrength, mDataState,
+ * mDisplayInfo, and mSimState. It should be called any time one of these is updated.
* This will call listeners if necessary.
*/
private final void updateTelephony() {
if (DEBUG) {
Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
- Utils.isInService(mServiceState) + " ss=" + mSignalStrength);
+ Utils.isInService(mServiceState) + " ss=" + mSignalStrength
+ + " displayInfo=" + mDisplayInfo);
}
checkDefaultData();
- mCurrentState.connected = Utils.isInService(mServiceState)
- && mSignalStrength != null;
+ mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null;
if (mCurrentState.connected) {
if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
mCurrentState.level = getCdmaLevel();
@@ -558,17 +521,8 @@ public class MobileSignalController extends SignalController<
}
}
- // When the device is camped on a 5G Non-Standalone network, the data network type is still
- // LTE. In this case, we first check which 5G icon should be shown.
- MobileIconGroup nr5GIconGroup = getNr5GIconGroup();
- if (mConfig.nrIconDisplayGracePeriodMs > 0) {
- nr5GIconGroup = adjustNr5GIconGroupByDisplayGraceTime(nr5GIconGroup);
- }
-
String iconKey = getIconKey();
- if (nr5GIconGroup != null) {
- mCurrentState.iconGroup = nr5GIconGroup;
- } else if (mNetworkToIconLookup.get(iconKey) != null) {
+ if (mNetworkToIconLookup.get(iconKey) != null) {
mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
mCurrentState.iconGroup = mDefaultIcons;
@@ -580,8 +534,7 @@ public class MobileSignalController extends SignalController<
if (isCarrierNetworkChangeActive()) {
mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
} else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
- if (mSubscriptionInfo.getSubscriptionId()
- != mDefaults.getDefaultDataSubId()) {
+ if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) {
mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
} else {
mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
@@ -623,91 +576,6 @@ public class MobileSignalController extends SignalController<
notifyListenersIfNecessary();
}
- private int getNrState(ServiceState serviceState) {
- NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (nri != null) {
- return nri.getNrState();
- }
- return NetworkRegistrationInfo.NR_STATE_NONE;
- }
-
- private MobileIconGroup getNr5GIconGroup() {
- if (mServiceState == null) return null;
-
- int nrState = getNrState(mServiceState);
- if (nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
- // Check if the NR 5G is using millimeter wave and the icon is config.
- if (mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE) {
- if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED_MMWAVE)) {
- return mConfig.nr5GIconMap.get(Config.NR_CONNECTED_MMWAVE);
- }
- }
-
- // If NR 5G is not using millimeter wave or there is no icon for millimeter wave, we
- // check the normal 5G icon.
- if (mConfig.nr5GIconMap.containsKey(Config.NR_CONNECTED)) {
- return mConfig.nr5GIconMap.get(Config.NR_CONNECTED);
- }
- } else if (nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED) {
- if (mCurrentState.activityDormant) {
- if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_IDLE)) {
- return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_IDLE);
- }
- } else {
- if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_CON)) {
- return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_CON);
- }
- }
- } else if (nrState == NetworkRegistrationInfo.NR_STATE_RESTRICTED) {
- if (mConfig.nr5GIconMap.containsKey(Config.NR_RESTRICTED)) {
- return mConfig.nr5GIconMap.get(Config.NR_RESTRICTED);
- }
- }
-
- return null;
- }
-
- /**
- * The function to adjust MobileIconGroup depend on CarrierConfig's time
- * nextIconGroup == null imply next state could be 2G/3G/4G/4G+
- * nextIconGroup != null imply next state will be 5G/5G+
- * Flag : mIsShowingIconGracefully
- * ---------------------------------------------------------------------------------
- * | Last state | Current state | Flag | Action |
- * ---------------------------------------------------------------------------------
- * | 5G/5G+ | 2G/3G/4G/4G+ | true | return previous IconGroup |
- * | 5G/5G+ | 5G/5G+ | true | Bypass |
- * | 2G/3G/4G/4G+ | 5G/5G+ | true | Bypass |
- * | 2G/3G/4G/4G+ | 2G/3G/4G/4G+ | true | Bypass |
- * | SS.connected | SS.disconnect | T|F | Reset timer |
- * |NETWORK_TYPE_LTE|!NETWORK_TYPE_LTE| T|F | Reset timer |
- * | 5G/5G+ | 2G/3G/4G/4G+ | false| Bypass |
- * | 5G/5G+ | 5G/5G+ | false| Bypass |
- * | 2G/3G/4G/4G+ | 5G/5G+ | false| SendMessageDelay(time), flag->true |
- * | 2G/3G/4G/4G+ | 2G/3G/4G/4G+ | false| Bypass |
- * ---------------------------------------------------------------------------------
- */
- private MobileIconGroup adjustNr5GIconGroupByDisplayGraceTime(
- MobileIconGroup candidateIconGroup) {
- if (mIsShowingIconGracefully && candidateIconGroup == null) {
- candidateIconGroup = (MobileIconGroup) mCurrentState.iconGroup;
- } else if (!mIsShowingIconGracefully && candidateIconGroup != null
- && mLastState.iconGroup != candidateIconGroup) {
- mDisplayGraceHandler.sendMessageDelayed(
- mDisplayGraceHandler.obtainMessage(MSG_DISPLAY_GRACE),
- mConfig.nrIconDisplayGracePeriodMs);
- mIsShowingIconGracefully = true;
- } else if (!mCurrentState.connected || mDataState == TelephonyManager.DATA_DISCONNECTED
- || candidateIconGroup == null) {
- mDisplayGraceHandler.removeMessages(MSG_DISPLAY_GRACE);
- mIsShowingIconGracefully = false;
- candidateIconGroup = null;
- }
-
- return candidateIconGroup;
- }
-
boolean isDataDisabled() {
return !mPhone.isDataConnectionEnabled();
}
@@ -718,8 +586,6 @@ public class MobileSignalController extends SignalController<
|| activity == TelephonyManager.DATA_ACTIVITY_IN;
mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
|| activity == TelephonyManager.DATA_ACTIVITY_OUT;
- mCurrentState.activityDormant = activity == TelephonyManager.DATA_ACTIVITY_DORMANT;
-
notifyListenersIfNecessary();
}
@@ -729,13 +595,10 @@ public class MobileSignalController extends SignalController<
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
pw.println(" mServiceState=" + mServiceState + ",");
pw.println(" mSignalStrength=" + mSignalStrength + ",");
+ pw.println(" mDisplayInfo=" + mDisplayInfo + ",");
pw.println(" mDataState=" + mDataState + ",");
- pw.println(" mDataNetType=" + mDataNetType + ",");
- pw.println(" mCA=" + mCA + ",");
- pw.println(" mCAPlus=" + mCAPlus + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
- pw.println(" mIsShowingIconGracefully=" + mIsShowingIconGracefully + ",");
}
class MobilePhoneStateListener extends PhoneStateListener {
@@ -760,14 +623,8 @@ public class MobileSignalController extends SignalController<
+ " dataState=" + state.getDataRegistrationState());
}
mServiceState = state;
- if (mServiceState != null) {
- NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
- DOMAIN_PS, TRANSPORT_TYPE_WWAN);
- if (regInfo != null) {
- updateDataNetType(regInfo.getAccessNetworkTechnology());
- }
- }
- updateTelephony();
+ // onDisplayInfoChanged is invoked directly after onServiceStateChanged, so not calling
+ // updateTelephony() to prevent icon flickering in case of overrides.
}
@Override
@@ -777,33 +634,10 @@ public class MobileSignalController extends SignalController<
+ " type=" + networkType);
}
mDataState = state;
- updateDataNetType(networkType);
- updateTelephony();
- }
-
- private void updateDataNetType(int networkType) {
- mDataNetType = networkType;
- mCA = false;
- mCAPlus = false;
- if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
- if (isCarrierSpecificDataIcon()) {
- mCAPlus = true;
- } else if (mServiceState != null && isUsingCarrierAggregation(mServiceState)) {
- mCA = true;
- }
- }
- }
-
- private boolean isUsingCarrierAggregation(ServiceState serviceState) {
- NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (nri != null) {
- DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
- if (dsri != null) {
- return dsri.isUsingCarrierAggregation();
- }
+ if (networkType != mDisplayInfo.getNetworkType()) {
+ mDisplayInfo = new DisplayInfo(networkType, DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
}
- return false;
+ updateTelephony();
}
@Override
@@ -820,7 +654,6 @@ public class MobileSignalController extends SignalController<
Log.d(mTag, "onCarrierNetworkChange: active=" + active);
}
mCurrentState.carrierNetworkChangeMode = active;
-
updateTelephony();
}
@@ -830,7 +663,16 @@ public class MobileSignalController extends SignalController<
updateDataSim();
updateTelephony();
}
- };
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ if (DEBUG) {
+ Log.d(mTag, "onDisplayInfoChanged: displayInfo=" + displayInfo);
+ }
+ mDisplayInfo = displayInfo;
+ updateTelephony();
+ }
+ }
static class MobileIconGroup extends SignalController.IconGroup {
final int mDataContentDescription; // mContentDescriptionDataType
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4f382e7049cf..9003de18ec93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -49,7 +49,6 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.SparseArray;
@@ -64,7 +63,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,10 +70,8 @@ import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -846,12 +842,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
pw.println(emergencyToString(mEmergencySource));
pw.println(" - config ------");
- pw.print(" patternOfCarrierSpecificDataIcon=");
- pw.println(mConfig.patternOfCarrierSpecificDataIcon);
- pw.print(" nr5GIconMap=");
- pw.println(mConfig.nr5GIconMap.toString());
- pw.print(" nrIconDisplayGracePeriodMs=");
- pw.println(mConfig.nrIconDisplayGracePeriodMs);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.dump(pw);
@@ -1132,14 +1122,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
@VisibleForTesting
static class Config {
- static final int NR_CONNECTED_MMWAVE = 1;
- static final int NR_CONNECTED = 2;
- static final int NR_NOT_RESTRICTED_RRC_IDLE = 3;
- static final int NR_NOT_RESTRICTED_RRC_CON = 4;
- static final int NR_RESTRICTED = 5;
-
- Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
-
boolean showAtLeast3G = false;
boolean show4gFor3g = false;
boolean alwaysShowCdmaRssi = false;
@@ -1148,22 +1130,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
boolean hspaDataDistinguishable;
boolean inflateSignalStrengths = false;
boolean alwaysShowDataRatIcon = false;
- public String patternOfCarrierSpecificDataIcon = "";
- public long nrIconDisplayGracePeriodMs;
-
- /**
- * Mapping from NR 5G status string to an integer. The NR 5G status string should match
- * those in carrier config.
- */
- private static final Map<String, Integer> NR_STATUS_STRING_TO_INDEX;
- static {
- NR_STATUS_STRING_TO_INDEX = new HashMap<>(5);
- NR_STATUS_STRING_TO_INDEX.put("connected_mmwave", NR_CONNECTED_MMWAVE);
- NR_STATUS_STRING_TO_INDEX.put("connected", NR_CONNECTED);
- NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_idle", NR_NOT_RESTRICTED_RRC_IDLE);
- NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_con", NR_NOT_RESTRICTED_RRC_CON);
- NR_STATUS_STRING_TO_INDEX.put("restricted", NR_RESTRICTED);
- }
static Config readConfig(Context context) {
Config config = new Config();
@@ -1192,64 +1158,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
CarrierConfigManager.KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL);
config.hideLtePlus = b.getBoolean(
CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
- config.patternOfCarrierSpecificDataIcon = b.getString(
- CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
- String nr5GIconConfiguration =
- b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
- if (!TextUtils.isEmpty(nr5GIconConfiguration)) {
- String[] nr5GIconConfigPairs = nr5GIconConfiguration.trim().split(",");
- for (String pair : nr5GIconConfigPairs) {
- add5GIconMapping(pair, config);
- }
- }
- setDisplayGraceTime(
- b.getInt(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT),
- config);
}
return config;
}
-
- /**
- * Add a mapping from NR 5G status to the 5G icon. All the icon resources come from
- * {@link TelephonyIcons}.
- *
- * @param keyValuePair the NR 5G status and icon name separated by a colon.
- * @param config container that used to store the parsed configs.
- */
- @VisibleForTesting
- static void add5GIconMapping(String keyValuePair, Config config) {
- String[] kv = (keyValuePair.trim().toLowerCase()).split(":");
-
- if (kv.length != 2) {
- if (DEBUG) Log.e(TAG, "Invalid 5G icon configuration, config = " + keyValuePair);
- return;
- }
-
- String key = kv[0], value = kv[1];
-
- // There is no icon config for the specific 5G status.
- if (value.equals("none")) return;
-
- if (NR_STATUS_STRING_TO_INDEX.containsKey(key)
- && TelephonyIcons.ICON_NAME_TO_ICON.containsKey(value)) {
- config.nr5GIconMap.put(
- NR_STATUS_STRING_TO_INDEX.get(key),
- TelephonyIcons.ICON_NAME_TO_ICON.get(value));
- }
- }
-
- /**
- * Set display gracefully period time(MS) depend on carrierConfig KEY
- * KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, and this function will convert to ms.
- * {@link CarrierConfigManager}.
- *
- * @param time showing 5G icon gracefully in the period of the time(SECOND)
- * @param config container that used to store the parsed configs.
- */
- @VisibleForTesting
- static void setDisplayGraceTime(int time, Config config) {
- config.nrIconDisplayGracePeriodMs = time * DateUtils.SECOND_IN_MILLIS;
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 749b56ce28c0..3a456918a49b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -261,7 +261,6 @@ public abstract class SignalController<T extends SignalController.State,
boolean enabled;
boolean activityIn;
boolean activityOut;
- public boolean activityDormant;
int level;
IconGroup iconGroup;
int inetCondition;
@@ -278,7 +277,6 @@ public abstract class SignalController<T extends SignalController.State,
inetCondition = state.inetCondition;
activityIn = state.activityIn;
activityOut = state.activityOut;
- activityDormant = state.activityDormant;
rssi = state.rssi;
time = state.time;
}
@@ -302,7 +300,6 @@ public abstract class SignalController<T extends SignalController.State,
.append("iconGroup=").append(iconGroup).append(',')
.append("activityIn=").append(activityIn).append(',')
.append("activityOut=").append(activityOut).append(',')
- .append("activityDormant=").append(activityDormant).append(',')
.append("rssi=").append(rssi).append(',')
.append("lastModified=").append(sSDF.format(time));
}
@@ -320,7 +317,6 @@ public abstract class SignalController<T extends SignalController.State,
&& other.iconGroup == iconGroup
&& other.activityIn == activityIn
&& other.activityOut == activityOut
- && other.activityDormant == activityDormant
&& other.rssi == rssi;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
index bc24ad0118e7..c66f07dd4f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
@@ -101,6 +101,11 @@ public class DisplayController {
return;
}
Display display = getDisplay(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Skipping Display Configuration change on invalid"
+ + " display. It may have been removed.");
+ return;
+ }
Context perDisplayContext = mContext;
if (displayId != Display.DEFAULT_DISPLAY) {
perDisplayContext = mContext.createDisplayContext(display);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7dad05df8f2c..1b62cbfabe39 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -34,6 +34,7 @@ import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
import java.util.ArrayList;
@@ -48,8 +49,8 @@ import javax.inject.Singleton;
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
- static final int ANIMATION_DURATION_SHOW_MS = 275;
- static final int ANIMATION_DURATION_HIDE_MS = 340;
+ public static final int ANIMATION_DURATION_SHOW_MS = 275;
+ public static final int ANIMATION_DURATION_HIDE_MS = 340;
static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
private static final int DIRECTION_NONE = 0;
private static final int DIRECTION_SHOW = 1;
@@ -57,6 +58,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
SystemWindows mSystemWindows;
final Handler mHandler;
+ final TransactionPool mTransactionPool;
final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
@@ -64,9 +66,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Inject
public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- @Main Handler mainHandler) {
+ @Main Handler mainHandler, TransactionPool transactionPool) {
mHandler = mainHandler;
mSystemWindows = syswin;
+ mTransactionPool = transactionPool;
displayController.addDisplayWindowListener(this);
}
@@ -255,18 +258,18 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
mAnimation.addUpdateListener(animation -> {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
t.setPosition(mImeSourceControl.getLeash(), x, value);
dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t);
t.apply();
- t.close();
+ mTransactionPool.release(t);
});
mAnimation.setInterpolator(INTERPOLATOR);
mAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY),
imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW,
@@ -275,11 +278,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.show(mImeSourceControl.getLeash());
}
t.apply();
- t.close();
+ mTransactionPool.release(t);
}
@Override
public void onAnimationEnd(Animator animation) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, endY);
dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY),
mAnimationDirection == DIRECTION_SHOW, t);
@@ -287,7 +290,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.hide(mImeSourceControl.getLeash());
}
t.apply();
- t.close();
+ mTransactionPool.release(t);
mAnimationDirection = DIRECTION_NONE;
mAnimation = null;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 64b0b66a47a9..4652abfa0721 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -35,6 +35,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
@@ -191,6 +192,11 @@ public class DisplayLayout {
return mDensityDpi;
}
+ /** Get the density scale for the display. */
+ public float density() {
+ return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ }
+
/** Get whether this layout is landscape. */
public boolean isLandscape() {
return mWidth > mHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 044a2a6cc4b6..23df991ed743 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -162,6 +162,23 @@ public class SystemWindows {
return pd.getWindow(windowType);
}
+ /**
+ * Gets the SurfaceControl associated with a root view. This is the same surface that backs the
+ * ViewRootImpl.
+ */
+ public SurfaceControl getViewSurface(View rootView) {
+ for (int i = 0; i < mPerDisplay.size(); ++i) {
+ for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) {
+ SurfaceControl out =
+ mPerDisplay.valueAt(i).mWwms.get(iWm).getSurfaceControlForWindow(rootView);
+ if (out != null) {
+ return out;
+ }
+ }
+ }
+ return null;
+ }
+
private class PerDisplay {
final int mDisplayId;
private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>();
@@ -256,6 +273,10 @@ public class SystemWindows {
void updateConfiguration(Configuration configuration) {
setConfiguration(configuration);
}
+
+ SurfaceControl getSurfaceControlForWindow(View rootView) {
+ return getSurfaceControl(rootView);
+ }
}
class ContainerWindow extends IWindow.Stub {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
index 44454d957869..e5ab9be288ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
@@ -60,10 +60,10 @@ public class DistanceClassifierTest extends ClassifierTest {
mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.isFalseTouch(), is(true));
- mClassifier.onTouchEvent(appendMoveEvent(1, 2));
+ mClassifier.onTouchEvent(appendMoveEvent(1, 40));
assertThat(mClassifier.isFalseTouch(), is(true));
- mClassifier.onTouchEvent(appendUpEvent(1, 40));
+ mClassifier.onTouchEvent(appendUpEvent(1, 80));
assertThat(mClassifier.isFalseTouch(), is(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
new file mode 100644
index 000000000000..75be74b13c87
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.carrier
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CellSignalStateTest : SysuiTestCase() {
+
+ @Test
+ fun testChangeVisibility_sameObject() {
+ val c = CellSignalState()
+
+ val other = c.changeVisibility(c.visible)
+
+ assertSame(c, other)
+ }
+
+ @Test
+ fun testChangeVisibility_otherObject() {
+ val c = CellSignalState()
+
+ val other = c.changeVisibility(!c.visible)
+
+ assertNotSame(c, other)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 715087dcd2b1..fa02231426ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
new file mode 100644
index 000000000000..022dc847130d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.carrier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QSCarrierTest extends SysuiTestCase {
+
+ private QSCarrier mQSCarrier;
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestableLooper = TestableLooper.get(this);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mTestableLooper.runWithLooper(() ->
+ mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+ }
+
+ @Test
+ public void testUpdateState_first() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+ }
+
+ @Test
+ public void testUpdateState_same() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+ assertFalse(mQSCarrier.updateState(c));
+ }
+
+ @Test
+ public void testUpdateState_changed() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+
+ CellSignalState other = c.changeVisibility(false);
+
+ assertTrue(mQSCarrier.updateState(other));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index c6b496dd8215..8e330c6f5049 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -145,6 +145,47 @@ class NotificationRankingManagerTest : SysuiTestCase() {
}
@Test
+ fun testSort_importantPeople() {
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val aC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+ aC.setConversationId("parent", "convo")
+ val a = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setChannel(aC)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val bC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+ bC.setConversationId("parent", "convo")
+ bC.setImportantConversation(true)
+ val b = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setChannel(bC)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+
+
+ assertEquals(
+ listOf(b, a),
+ rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
fun testSort_properlySetsAlertingBucket() {
val notif = Notification.Builder(mContext, "test") .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 27b263f3052b..138ea392f8ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -21,6 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -55,6 +57,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -449,6 +452,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Test
public void testBindNotification_bubbleActionVisibleWhenCanBubble() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -469,7 +473,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
- public void testBindNotification_bubbleActionVisibleWhenCannotBubble() {
+ public void testBindNotification_bubbleAction_noBubbleMetadata() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -490,6 +495,28 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
+ public void testBindNotification_bubbleActionGloballyOff() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mBubbleEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
+ assertEquals(View.GONE, bubbleView.getVisibility());
+ }
+
+ @Test
public void testAddToHome() throws Exception {
when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true);
@@ -550,6 +577,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Test
public void testBubble_promotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationChannel.setAllowBubbles(false);
mConversationChannel.setAllowBubbles(false);
@@ -584,6 +612,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Test
public void testBubble_demotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
@@ -617,6 +646,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Test
public void testBubble_noChannelChange() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -645,7 +675,48 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
- public void testFavorite_favorite() throws Exception {
+ public void testFavorite_favorite_noBubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 0);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+ assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+ fave.getContentDescription().toString());
+
+ fave.performClick();
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<NotificationChannel> captor =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), anyInt(), captor.capture());
+ assertTrue(captor.getValue().isImportantConversation());
+ assertFalse(captor.getValue().canBubble());
+ verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry);
+ }
+
+ @Test
+ public void testFavorite_favorite_bubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -673,6 +744,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), anyInt(), captor.capture());
assertTrue(captor.getValue().isImportantConversation());
+ assertTrue(captor.getValue().canBubble());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
new file mode 100644
index 000000000000..7f48cd1313fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 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.notification.row;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationInlineImageResolverTest extends SysuiTestCase {
+
+ NotificationInlineImageResolver mResolver;
+ Bitmap mBitmap;
+ BitmapDrawable mBitmapDrawable;
+ Uri mUri;
+
+ @Before
+ public void setup() {
+ mResolver = spy(new NotificationInlineImageResolver(mContext, null));
+ mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+ mBitmapDrawable = new BitmapDrawable(mContext.getResources(), mBitmap);
+ mUri = mock(Uri.class);
+ }
+
+ @Test
+ public void refreshMaxImageSizes() {
+ assertNotEquals("Starts different height", mResolver.mMaxImageHeight, 20);
+ assertNotEquals("Starts different width", mResolver.mMaxImageWidth, 15);
+
+ doReturn(20).when(mResolver).getMaxImageHeight();
+ doReturn(15).when(mResolver).getMaxImageWidth();
+
+ mResolver.updateMaxImageSizes();
+
+ assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20);
+ assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15);
+ }
+
+ @Test
+ public void resolveImage_sizeTooBig() throws IOException {
+ doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+ mResolver.mMaxImageHeight = 5;
+ mResolver.mMaxImageWidth = 5;
+
+ // original bitmap size is 10x10
+ BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+ Bitmap resolvedBitmap = resolved.getBitmap();
+ assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth());
+ assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight());
+ assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap);
+ }
+
+ @Test
+ public void resolveImage_sizeOK() throws IOException {
+ doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+ mResolver.mMaxImageWidth = 15;
+ mResolver.mMaxImageHeight = 15;
+
+ // original bitmap size is 10x10
+ BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+ Bitmap resolvedBitmap = resolved.getBitmap();
+ assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth());
+ assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight());
+ assertSame("Bitmap not replaced", resolvedBitmap, mBitmap);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5ee439d4bb7..d81b8c2af246 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -50,6 +50,7 @@ import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Handler;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -256,7 +257,8 @@ public class StatusBarTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
IPowerManager powerManagerService = mock(IPowerManager.class);
- mPowerManager = new PowerManager(mContext, powerManagerService,
+ IThermalService thermalService = mock(IThermalService.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
mNotificationInterruptionStateProvider =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 9a0e97aad9c2..a0d551c743c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -45,6 +45,7 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
+import android.telephony.DisplayInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -96,6 +97,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected PhoneStateListener mPhoneStateListener;
protected SignalStrength mSignalStrength;
protected ServiceState mServiceState;
+ protected DisplayInfo mDisplayInfo;
protected NetworkRegistrationInfo mFakeRegInfo;
protected ConnectivityManager mMockCm;
protected WifiManager mMockWm;
@@ -159,6 +161,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
+ mDisplayInfo = mock(DisplayInfo.class);
mFakeRegInfo = new NetworkRegistrationInfo.Builder()
.setTransportType(TRANSPORT_TYPE_WWAN)
@@ -167,6 +170,9 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
.build();
doReturn(mFakeRegInfo).when(mServiceState)
.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mDisplayInfo).getNetworkType();
+ doReturn(DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE).when(mDisplayInfo)
+ .getOverrideNetworkType();
mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF,
CdmaEriInformation.ERI_ICON_MODE_NORMAL);
@@ -260,33 +266,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
}
- public void setupDefaultNr5GIconConfiguration() {
- NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
- NetworkControllerImpl.Config.add5GIconMapping("connected:5g", mConfig);
- }
-
- public void setupNr5GIconConfigurationForNotRestrictedRrcCon() {
- NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
- NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
- NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_con:5g", mConfig);
- }
-
- public void setupNr5GIconConfigurationForNotRestrictedRrcIdle() {
- NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
- NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
- NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_idle:5g", mConfig);
- }
-
- public void setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds() {
- final int enableDisplayGraceTimeSec = 30;
- NetworkControllerImpl.Config.setDisplayGraceTime(enableDisplayGraceTimeSec, mConfig);
- }
-
- public void setupDefaultNr5GIconDisplayGracePeriodTime_disabled() {
- final int disableDisplayGraceTimeSec = 0;
- NetworkControllerImpl.Config.setDisplayGraceTime(disableDisplayGraceTimeSec, mConfig);
- }
-
public void setConnectivityViaBroadcast(
int networkType, boolean validated, boolean isConnected) {
setConnectivityCommon(networkType, validated, isConnected);
@@ -369,6 +348,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected void updateServiceState() {
Log.d(TAG, "Sending Service State: " + mServiceState);
mPhoneStateListener.onServiceStateChanged(mServiceState);
+ mPhoneStateListener.onDisplayInfoChanged(mDisplayInfo);
}
public void updateCallState(int state) {
@@ -384,6 +364,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
.build();
when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
.thenReturn(fakeRegInfo);
+ when(mDisplayInfo.getNetworkType()).thenReturn(dataNetType);
mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index a906d9fa12bd..3eb0c44491fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -3,17 +3,13 @@ package com.android.systemui.statusbar.policy;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.net.NetworkCapabilities;
import android.os.Looper;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -24,7 +20,6 @@ import com.android.settingslib.net.DataUsageController;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -180,254 +175,6 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
}
@Test
- public void testNr5GIcon_NrNotRestrictedRrcCon_show5GIcon() {
- setupNr5GIconConfigurationForNotRestrictedRrcCon();
- setupDefaultSignal();
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G,
- true, DEFAULT_QS_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G, true, true);
- }
-
- @Test
- public void testNr5GIcon_NrNotRestrictedRrcIdle_show5GIcon() {
- setupNr5GIconConfigurationForNotRestrictedRrcIdle();
- setupDefaultSignal();
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_DORMANT);
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
- }
-
- @Test
- public void testNr5GIcon_NrConnectedWithoutMMWave_show5GIcon() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultSignal();
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
- }
-
- @Test
- public void testNr5GIcon_NrConnectedWithMMWave_show5GPlusIcon() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultSignal();
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G_PLUS);
- }
-
- @Test
- public void testNr5GIcon_NrRestricted_showLteIcon() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultSignal();
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_RESTRICTED);
- mPhoneStateListener.onServiceStateChanged(mServiceState);
-
- verifyDataIndicators(TelephonyIcons.ICON_LTE);
- }
-
- @Test
- public void testNr5GIcon_displayGracePeriodTime_enabled() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
- mPhoneStateListener.onServiceStateChanged(mServiceState);
-
- ServiceState ss = Mockito.mock(ServiceState.class);
- // While nrIconDisplayGracePeriodMs > 0 & is Nr5G, mIsShowingIconGracefully should be true
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- assertTrue(mConfig.nrIconDisplayGracePeriodMs > 0);
- assertTrue(mMobileSignalController.mIsShowingIconGracefully);
- }
-
- @Test
- public void testNr5GIcon_displayGracePeriodTime_disabled() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_disabled();
- setupDefaultSignal();
-
- assertTrue(mConfig.nrIconDisplayGracePeriodMs == 0);
-
- // While nrIconDisplayGracePeriodMs <= 0, mIsShowingIconGracefully should be false
- setNrState(mServiceState, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
-
- assertFalse(mMobileSignalController.mIsShowingIconGracefully);
- }
-
- @Test
- public void testNr5GIcon_enableDisplayGracePeriodTime_showIconGracefully() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
- mPhoneStateListener.onServiceStateChanged(mServiceState);
-
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- // Enabled timer Nr5G switch to None Nr5G, showing 5G icon gracefully
- ServiceState ssLte = Mockito.mock(ServiceState.class);
- setNrState(ssLte, NetworkRegistrationInfo.NR_STATE_NONE);
- doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(ssLte).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ssLte);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
- }
-
- @Test
- public void testNr5GIcon_disableDisplayGracePeriodTime_showLatestIconImmediately() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_disabled();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
-
- setNrState(mServiceState, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- setNrState(mServiceState, NetworkRegistrationInfo.NR_STATE_NONE);
- doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
-
- verifyDataIndicators(TelephonyIcons.ICON_LTE);
- }
-
- @Test
- public void testNr5GIcon_resetDisplayGracePeriodTime_whenDataDisconnected() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
- setNrState(mServiceState, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- // Disabled timer, when out of service, reset timer to display latest state
- updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- setNrState(mServiceState, NetworkRegistrationInfo.NR_STATE_NONE);
- doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_DISCONNECTED,
- TelephonyManager.NETWORK_TYPE_UMTS);
-
- verifyDataIndicators(0);
- }
-
- @Test
- public void testNr5GIcon_enableDisplayGracePeriodTime_show5G_switching_5GPlus() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
- mPhoneStateListener.onServiceStateChanged(mServiceState);
-
- ServiceState ss5G = Mockito.mock(ServiceState.class);
- setNrState(ss5G, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss5G).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss5G);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- // When timeout enabled, 5G/5G+ switching should be updated immediately
- ServiceState ss5GPlus = Mockito.mock(ServiceState.class);
- setNrState(ss5GPlus, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(ss5GPlus).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss5GPlus);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G_PLUS);
- }
-
- @Test
- public void testNr5GIcon_carrierDisabledDisplayGracePeriodTime_shouldUpdateIconImmediately() {
- setupDefaultNr5GIconConfiguration();
- setupDefaultNr5GIconDisplayGracePeriodTime_enableThirtySeconds();
- setupDefaultSignal();
- mNetworkController.handleConfigurationChanged();
- mPhoneStateListener.onServiceStateChanged(mServiceState);
-
- ServiceState ss = Mockito.mock(ServiceState.class);
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_CONNECTED);
- doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- // State from NR_5G to NONE NR_5G with timeout, should show previous 5G icon
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_NONE);
- doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(ss).getNrFrequencyRange();
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
- mPhoneStateListener.onServiceStateChanged(ss);
-
- verifyDataIndicators(TelephonyIcons.ICON_5G);
-
- // Update nrIconDisplayGracePeriodMs to 0
- setupDefaultNr5GIconDisplayGracePeriodTime_disabled();
- mNetworkController.handleConfigurationChanged();
-
- // State from NR_5G to NONE NR_STATE_RESTRICTED, showing corresponding icon
- setNrState(ss, NetworkRegistrationInfo.NR_STATE_RESTRICTED);
- mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
- TelephonyManager.NETWORK_TYPE_LTE);
-
- assertTrue(mConfig.nrIconDisplayGracePeriodMs == 0);
- verifyDataIndicators(TelephonyIcons.ICON_LTE);
- }
-
- @Test
public void testDataDisabledIcon_UserNotSetup() {
setupNetworkController();
when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
@@ -488,6 +235,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
.build();
when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
.thenReturn(fakeRegInfo);
+ when(mDisplayInfo.getNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
updateServiceState();
verifyDataIndicators(TelephonyIcons.ICON_H);
}
@@ -523,10 +271,4 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
true, DEFAULT_QS_SIGNAL_STRENGTH, dataIcon, false,
false);
}
-
- private void setNrState(ServiceState ss, int nrState) {
- mFakeRegInfo.setNrState(nrState);
- doReturn(mFakeRegInfo).when(ss)
- .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
- }
}
diff --git a/services/Android.bp b/services/Android.bp
index 416f448a965f..c77e75da66ba 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -127,6 +127,16 @@ droidstubs {
api_file: "api/current.txt",
removed_api_file: "api/removed.txt",
},
+ last_released: {
+ api_file: ":last-released-system-server-api",
+ removed_api_file: "api/removed.txt",
+ baseline_file: ":system-server-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-system-server-api",
+ baseline_file: "api/lint-baseline.txt",
+ },
},
}
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
new file mode 100644
index 000000000000..0b8658cf469d
--- /dev/null
+++ b/services/api/lint-baseline.txt
@@ -0,0 +1,35 @@
+// Baseline format: 1.0
+InternalClasses: com.android.permission.persistence.RuntimePermissionsPersistence:
+ Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState:
+ Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState.PermissionState:
+ Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesPersistence:
+ Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesState:
+ Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService:
+ Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService.TargetUser:
+ Internal classes must not be exposed
+
+
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
+
+
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#delete(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#read(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#write(com.android.permission.persistence.RuntimePermissionsState, android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
+UserHandleName: com.android.role.persistence.RolesPersistence#delete(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.role.persistence.RolesPersistence#read(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.role.persistence.RolesPersistence#write(com.android.role.persistence.RolesState, android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7083281eaa7e..f7eabac3b21f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3430,16 +3430,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
// there is hope for it to become one if it validated, then it is needed.
ensureRunningOnConnectivityServiceThread();
if (nri.request.isRequest() && nai.satisfies(nri.request) &&
- (nai.isSatisfyingRequest(nri.request.requestId) ||
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satisfying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- nri.mSatisfier.getCurrentScore()
- < nai.getCurrentScoreAsValidated())) {
+ (nai.isSatisfyingRequest(nri.request.requestId)
+ // Note that canPossiblyBeat catches two important cases:
+ // 1. Unvalidated slow networks will not be reaped when an unvalidated fast
+ // network is currently satisfying the request. This is desirable for example
+ // when cellular ends up validating but WiFi/Ethernet does not.
+ // 2. Fast networks will not be reaped when a validated slow network is
+ // currently satisfying the request. This is desirable for example when
+ // Ethernet ends up validating and out scoring WiFi, or WiFi/Ethernet ends
+ // up validating and out scoring cellular.
+ || nai.canPossiblyBeat(nri.mSatisfier))) {
return false;
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 207a6aa5b9d0..5db5115b4afe 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -194,8 +194,6 @@ public class LocationManagerService extends ILocationManager.Stub {
// time
private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- private static final String FEATURE_ID = "LocationService";
-
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Object mLock = new Object();
@@ -215,11 +213,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private PackageManager mPackageManager;
private PowerManager mPowerManager;
- // TODO: sharing a location fudger with mock providers can leak information as the mock provider
- // can be used to retrieve offset information. the fudger should likely be reset whenever mock
- // providers are added or removed
- private LocationFudger mLocationFudger;
-
private GeofenceManager mGeofenceManager;
private GeocoderProxy mGeocodeProvider;
@@ -245,7 +238,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private int mBatterySaverMode;
private LocationManagerService(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ mContext = context;
mHandler = FgThread.getHandler();
mLocalService = new LocalService();
@@ -287,8 +280,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mPackageManager = mContext.getPackageManager();
mAppOps = mContext.getSystemService(AppOpsManager.class);
mPowerManager = mContext.getSystemService(PowerManager.class);
-
- mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper);
PowerManagerInternal localPowerManager =
@@ -665,6 +656,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final String mName;
+ private final LocationFudger mLocationFudger;
+
// if the provider is enabled for a given user id - null or not present means unknown
@GuardedBy("mLock")
private final SparseArray<Boolean> mEnabled;
@@ -682,6 +675,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private LocationProviderManager(String name) {
mName = name;
+ mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mEnabled = new SparseArray<>(2);
mLastLocation = new SparseArray<>(2);
mLastCoarseLocation = new SparseArray<>(2);
@@ -706,7 +700,9 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
mProvider.setMockProvider(provider);
- // when removing a mock provider, also clear any mock last locations
+ // when removing a mock provider, also clear any mock last locations and reset the
+ // location fudger. the mock provider could have been used to infer the current
+ // location fudger offsets.
if (provider == null) {
for (int i = 0; i < mLastLocation.size(); i++) {
Location lastLocation = mLastLocation.valueAt(i);
@@ -721,6 +717,8 @@ public class LocationManagerService extends ILocationManager.Stub {
mLastCoarseLocation.setValueAt(i, null);
}
}
+
+ mLocationFudger.resetOffsets();
}
}
}
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca1022c..7f5befabc576 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -16,8 +16,10 @@
package com.android.server;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorAdditionalInfo;
@@ -29,7 +31,9 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
public class SensorNotificationService extends SystemService
@@ -48,8 +52,6 @@ public class SensorNotificationService extends SystemService
private static final long MILLIS_2010_1_1 = 1262358000000l;
- private static final String FEATURE_ID = "SensorNotificationService";
-
private Context mContext;
private SensorManager mSensorManager;
private LocationManager mLocationManager;
@@ -59,8 +61,8 @@ public class SensorNotificationService extends SystemService
private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
public SensorNotificationService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
- mContext = getContext();
+ super(context);
+ mContext = context;
}
public void onStart() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 53dbb9336b36..d86b2230ee7a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2120,8 +2120,6 @@ class StorageManagerService extends IStorageManager.Stub
private void unmount(VolumeInfo vol) {
try {
- mVold.unmount(vol.id);
- mStorageSessionController.onVolumeUnmount(vol);
try {
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
mInstaller.onPrivateVolumeRemoved(vol.getFsUuid());
@@ -2129,6 +2127,8 @@ class StorageManagerService extends IStorageManager.Stub
} catch (Installer.InstallerException e) {
Slog.e(TAG, "Failed unmount mirror data", e);
}
+ mVold.unmount(vol.id);
+ mStorageSessionController.onVolumeUnmount(vol);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -4346,6 +4346,42 @@ class StorageManagerService extends IStorageManager.Stub
mPolicies.add(policy);
}
+ /**
+ * Check if fuse is running in target user, if it's running then setup its obb directories.
+ * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ */
+ @Override
+ public void prepareObbDirs(int userId, Set<String> packageList, String processName) {
+ String fuseRunningUsersList = SystemProperties.get("vold.fuse_running_users", "");
+ String[] fuseRunningUsers = fuseRunningUsersList.split(",");
+ boolean fuseReady = false;
+ String targetUserId = String.valueOf(userId);
+ for (String user : fuseRunningUsers) {
+ if (targetUserId.equals(user)) {
+ fuseReady = true;
+ }
+ }
+ if (fuseReady) {
+ try {
+ final IVold vold = IVold.Stub.asInterface(
+ ServiceManager.getServiceOrThrow("vold"));
+ for (String pkg : packageList) {
+ final String obbDir =
+ String.format("/storage/emulated/%d/Android/obb", userId);
+ final String packageObbDir = String.format("%s/%s/", obbDir, pkg);
+
+ // Create package obb dir if it doesn't exist.
+ File file = new File(packageObbDir);
+ if (!file.exists()) {
+ vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
+ }
+ }
+ } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
+ Slog.e(TAG, "Unable to create obb directories for " + processName, e);
+ }
+ }
+ }
+
@Override
public void onExternalStoragePolicyChanged(int uid, String packageName) {
final int mountMode = getExternalStorageMountMode(uid, packageName);
@@ -4409,6 +4445,25 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ @Override
+ public void prepareAppDataAfterInstall(String packageName, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ final Environment.UserEnvironment userEnv = new Environment.UserEnvironment(userId);
+
+ // The installer may have downloaded OBBs for this newly installed application;
+ // make sure the OBB dir for the application is setup correctly, if it exists.
+ File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName);
+ for (File packageObbDir : packageObbDirs) {
+ try {
+ mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get canonical path for " + packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to fixup app dir for " + packageName);
+ }
+ }
+ }
+
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 430a5b950f14..50f43b5c1bae 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -49,6 +49,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.ServiceStartArgs;
+import android.app.admin.DevicePolicyEventLogger;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
@@ -79,6 +80,7 @@ import android.os.SystemProperties;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1912,6 +1914,8 @@ public final class ActiveServices {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
+ maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);
+
getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
} finally {
@@ -1921,6 +1925,21 @@ public final class ActiveServices {
return 1;
}
+ private void maybeLogBindCrossProfileService(
+ int userId, String callingPackage, int callingUid) {
+ if (UserHandle.isCore(callingUid)) {
+ return;
+ }
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId == userId
+ || !mAm.mUserController.isSameProfileGroup(callingUserId, userId)) {
+ return;
+ }
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.BIND_CROSS_PROFILE_SERVICE)
+ .setStrings(callingPackage)
+ .write();
+ }
+
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ffa7d9202371..0dc44f79d19f 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -80,11 +80,13 @@ import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IVold;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -142,10 +144,14 @@ import java.util.Map;
public final class ProcessList {
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
- // A device config to control the minimum target SDK to enable app data isolation
+ // A system property to control if app data isolation is enabled.
static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
+ // A system property to control if obb app data isolation is enabled in vold.
+ static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
+ "persist.sys.vold_app_data_isolation_enabled";
+
// A device config to control the minimum target SDK to enable app data isolation
static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK = "android_app_data_isolation_min_sdk";
@@ -379,6 +385,8 @@ public final class ProcessList {
private boolean mAppDataIsolationEnabled = false;
+ private boolean mVoldAppDataIsolationEnabled = false;
+
private ArrayList<String> mAppDataIsolationWhitelistedApps;
/**
@@ -691,6 +699,8 @@ public final class ProcessList {
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
@@ -2113,6 +2123,13 @@ public final class ProcessList {
app.info.packageName, app.userId);
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages, uid);
+
+ if (mVoldAppDataIsolationEnabled) {
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid),
+ pkgDataInfoMap.keySet(), app.processName);
+ }
} else {
pkgDataInfoMap = null;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4612cfd0f7cb..3860904a3fed 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -475,24 +477,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- private int getCurrentScore(boolean pretendValidated) {
- // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
- // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
- // score. The NetworkScore class would provide a nice place to centralize score constants
- // so they are not scattered about the transports.
-
+ /** Gets the current score */
+ public int getCurrentScore() {
// If this network is explicitly selected and the user has decided to use it even if it's
- // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
- // selected and we're trying to see what its score could be. This ensures that we don't tear
- // down an explicitly selected network before the user gets a chance to prefer it when
- // a higher-scoring network (e.g., Ethernet) is available.
- if (networkAgentConfig.explicitlySelected
- && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
+ // unvalidated, give it the maximum score.
+ if (networkAgentConfig.explicitlySelected && networkAgentConfig.acceptUnvalidated) {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
int score = mNetworkScore.getLegacyScore();
- if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
+ if (!lastValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
@@ -508,18 +502,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
return isWifi && !avoidBadWifi && everValidated;
}
- // Get the current score for this Network. This may be modified from what the
- // NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScore() {
- return getCurrentScore(false);
- }
-
- // Get the current score for this Network as if it was validated. This may be modified from
- // what the NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScoreAsValidated() {
- return getCurrentScore(true);
- }
-
public void setNetworkScore(@NonNull NetworkScore ns) {
mNetworkScore = ns;
}
@@ -629,6 +611,41 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
mLingering = false;
}
+ /**
+ * Returns whether this NAI has any chance of ever beating this other agent.
+ *
+ * The chief use case of this is the decision to tear down this network. ConnectivityService
+ * tears down networks that don't satisfy any request, unless they have a chance to beat any
+ * existing satisfier.
+ *
+ * @param other the agent to beat
+ * @return whether this should be given more time to try and beat the other agent
+ * TODO : remove this and migrate to a ranker-based approach
+ */
+ public boolean canPossiblyBeat(@NonNull final NetworkAgentInfo other) {
+ // Any explicitly selected network should be held on.
+ if (networkAgentConfig.explicitlySelected) return true;
+ // An outscored exiting network should be torn down.
+ if (mNetworkScore.isExiting()) return false;
+ // If this network is validated it can be torn down as it can't hope to be better than
+ // it already is.
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return false;
+ // If neither network is validated, keep both until at least one does.
+ if (!other.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return true;
+ // If this network is not metered but the other is, it should be preferable if it validates.
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && !other.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ return true;
+ }
+
+ // If the control comes here :
+ // • This network is neither exiting or explicitly selected
+ // • This network is not validated, but the other is
+ // • This network is metered, or both networks are unmetered
+ // Keep it if it's expected to be faster than the other., should it validate.
+ return mNetworkScore.probablyFasterThan(other.mNetworkScore);
+ }
+
public void dumpLingerTimers(PrintWriter pw) {
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index c536ab25e925..80d46e0370b4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -16,6 +16,9 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkScore.POLICY_IGNORE_ON_WIFI;
import static com.android.internal.util.FunctionalUtils.findFirst;
@@ -42,13 +45,20 @@ public class NetworkRanker {
@NonNull final Collection<NetworkAgentInfo> nais) {
final ArrayList<NetworkAgentInfo> candidates = new ArrayList<>(nais);
candidates.removeIf(nai -> !nai.satisfies(request));
- // Enforce policy.
- filterBadWifiAvoidancePolicy(candidates);
+
+ // Enforce policy. The order in which the policy is computed is essential, because each
+ // step may remove some of the candidates. For example, filterValidated drops non-validated
+ // networks in presence of validated networks for INTERNET requests, but the bad wifi
+ // avoidance policy takes priority over this, so it must be done before.
+ filterVpn(candidates);
+ filterExplicitlySelected(candidates);
+ filterBadWifiAvoidance(candidates);
+ filterValidated(request, candidates);
NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : candidates) {
- final int score = nai.getCurrentScore();
+ final int score = nai.getNetworkScore().getLegacyScore();
if (score > bestScore) {
bestNetwork = nai;
bestScore = score;
@@ -57,9 +67,27 @@ public class NetworkRanker {
return bestNetwork;
}
- // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
- private void filterBadWifiAvoidancePolicy(
+ // If a network is a VPN it has priority.
+ private void filterVpn(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ final NetworkAgentInfo vpn = findFirst(candidates,
+ nai -> nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+ if (null == vpn) return; // No VPN : this policy doesn't apply.
+ candidates.removeIf(nai -> !nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+ }
+
+ // If some network is explicitly selected and set to accept unvalidated connectivity, then
+ // drop all networks that are not explicitly selected.
+ private void filterExplicitlySelected(
@NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ final NetworkAgentInfo explicitlySelected = findFirst(candidates,
+ nai -> nai.networkAgentConfig.explicitlySelected
+ && nai.networkAgentConfig.acceptUnvalidated);
+ if (null == explicitlySelected) return; // No explicitly selected network accepting unvalid
+ candidates.removeIf(nai -> !nai.networkAgentConfig.explicitlySelected);
+ }
+
+ // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
+ private void filterBadWifiAvoidance(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
final NetworkAgentInfo wifi = findFirst(candidates,
nai -> nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& nai.everValidated
@@ -71,4 +99,16 @@ public class NetworkRanker {
if (null == wifi) return; // No wifi : this policy doesn't apply
candidates.removeIf(nai -> nai.getNetworkScore().hasPolicy(POLICY_IGNORE_ON_WIFI));
}
+
+ // If some network is validated and the request asks for INTERNET, drop all networks that are
+ // not validated.
+ private void filterValidated(@NonNull final NetworkRequest request,
+ @NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ if (!request.hasCapability(NET_CAPABILITY_INTERNET)) return;
+ final NetworkAgentInfo validated = findFirst(candidates,
+ nai -> nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+ if (null == validated) return; // No validated network
+ candidates.removeIf(nai ->
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ac41434a1b5c..18adc0ba27ee 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayViewport;
import android.util.DisplayMetrics;
import android.view.Display;
@@ -288,6 +289,13 @@ final class DisplayDeviceInfo {
public DisplayAddress address;
/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ */
+ public DeviceProductInfo deviceProductInfo;
+
+ /**
* Display state.
*/
public int state = Display.STATE_ON;
@@ -360,6 +368,7 @@ final class DisplayDeviceInfo {
|| rotation != other.rotation
|| type != other.type
|| !Objects.equals(address, other.address)
+ || !Objects.equals(deviceProductInfo, other.deviceProductInfo)
|| ownerUid != other.ownerUid
|| !Objects.equals(ownerPackageName, other.ownerPackageName)) {
diff |= DIFF_OTHER;
@@ -396,6 +405,7 @@ final class DisplayDeviceInfo {
rotation = other.rotation;
type = other.type;
address = other.address;
+ deviceProductInfo = other.deviceProductInfo;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
@@ -429,6 +439,7 @@ final class DisplayDeviceInfo {
if (address != null) {
sb.append(", address ").append(address);
}
+ sb.append(", deviceProductInfo ").append(deviceProductInfo);
sb.append(", state ").append(Display.stateToString(state));
if (ownerUid != 0 || ownerPackageName != null) {
sb.append(", owner ").append(ownerPackageName);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4ebbddabd6db..e578ac1fcd42 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -513,6 +513,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
mInfo.xDpi = config.xDpi;
mInfo.yDpi = config.yDpi;
+ mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
// Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
@@ -891,8 +892,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mBacklight=" + mBacklight);
pw.println("mAllmSupported=" + mAllmSupported);
pw.println("mAllmRequested=" + mAllmRequested);
- pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
- pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
+ pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
+ pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
pw.println("mDisplayInfo=" + mDisplayInfo);
pw.println("mDisplayConfigs=");
for (int i = 0; i < mDisplayConfigs.length; i++) {
@@ -902,14 +903,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.print("mSupportedColorModes=[");
- for (int i = 0; i < mSupportedColorModes.size(); i++) {
- if (i != 0) {
- pw.print(", ");
- }
- pw.print(mSupportedColorModes.get(i));
- }
- pw.println("]");
+ pw.print("mSupportedColorModes=" + mSupportedColorModes.toString());
}
private int findDisplayConfigIdLocked(int modeId) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0c9445a05551..ac81a6c813f7 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -269,6 +269,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
+ mBaseDisplayInfo.deviceProductInfo = deviceInfo.deviceProductInfo;
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
mBaseDisplayInfo.appWidth = maskedWidth;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 6174e5418caf..b84d3226362b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -77,7 +77,7 @@ final class HdmiCecController {
private static final int NUM_LOGICAL_ADDRESS = 16;
- private static final int MAX_CEC_MESSAGE_HISTORY = 200;
+ private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
// Predicate for whether the given logical address is remote device's one or not.
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -111,9 +111,9 @@ final class HdmiCecController {
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
- // Stores recent CEC messages history for debugging purpose.
- private final ArrayBlockingQueue<MessageHistoryRecord> mMessageHistory =
- new ArrayBlockingQueue<>(MAX_CEC_MESSAGE_HISTORY);
+ // Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
+ private final ArrayBlockingQueue<Dumpable> mMessageHistory =
+ new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
private final NativeWrapper mNativeWrapperImpl;
@@ -618,7 +618,7 @@ final class HdmiCecController {
void sendCommand(final HdmiCecMessage cecMessage,
final HdmiControlService.SendMessageCallback callback) {
assertRunOnServiceThread();
- addMessageToHistory(false /* isReceived */, cecMessage);
+ addCecMessageToHistory(false /* isReceived */, cecMessage);
runOnIoThread(new Runnable() {
@Override
public void run() {
@@ -658,7 +658,7 @@ final class HdmiCecController {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
HdmiLogger.debug("[R]:" + command);
- addMessageToHistory(true /* isReceived */, command);
+ addCecMessageToHistory(true /* isReceived */, command);
onReceiveCommand(command);
}
@@ -669,16 +669,26 @@ final class HdmiCecController {
private void handleHotplug(int port, boolean connected) {
assertRunOnServiceThread();
HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
+ addHotplugEventToHistory(port, connected);
mService.onHotplug(port, connected);
}
@ServiceThreadOnly
- private void addMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+ private void addHotplugEventToHistory(int port, boolean connected) {
assertRunOnServiceThread();
- MessageHistoryRecord record = new MessageHistoryRecord(isReceived, message);
- if (!mMessageHistory.offer(record)) {
+ addEventToHistory(new HotplugHistoryRecord(port, connected));
+ }
+
+ @ServiceThreadOnly
+ private void addCecMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ addEventToHistory(new MessageHistoryRecord(isReceived, message));
+ }
+
+ private void addEventToHistory(Dumpable event) {
+ if (!mMessageHistory.offer(event)) {
mMessageHistory.poll();
- mMessageHistory.offer(record);
+ mMessageHistory.offer(event);
}
}
@@ -689,10 +699,11 @@ final class HdmiCecController {
mLocalDevices.valueAt(i).dump(pw);
pw.decreaseIndent();
}
+
pw.println("CEC message history:");
pw.increaseIndent();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- for (MessageHistoryRecord record : mMessageHistory) {
+ for (Dumpable record : mMessageHistory) {
record.dump(pw, sdf);
}
pw.decreaseIndent();
@@ -792,17 +803,27 @@ final class HdmiCecController {
}
}
- private final class MessageHistoryRecord {
- private final long mTime;
+ private abstract static class Dumpable {
+ protected final long mTime;
+
+ Dumpable() {
+ mTime = System.currentTimeMillis();
+ }
+
+ abstract void dump(IndentingPrintWriter pw, SimpleDateFormat sdf);
+ }
+
+ private static final class MessageHistoryRecord extends Dumpable {
private final boolean mIsReceived; // true if received message and false if sent message
private final HdmiCecMessage mMessage;
- public MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
- mTime = System.currentTimeMillis();
+ MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
+ super();
mIsReceived = isReceived;
mMessage = message;
}
+ @Override
void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
pw.print(mIsReceived ? "[R]" : "[S]");
pw.print(" time=");
@@ -811,4 +832,26 @@ final class HdmiCecController {
pw.println(mMessage);
}
}
+
+ private static final class HotplugHistoryRecord extends Dumpable {
+ private final int mPort;
+ private final boolean mConnected;
+
+ HotplugHistoryRecord(int port, boolean connected) {
+ super();
+ mPort = port;
+ mConnected = connected;
+ }
+
+ @Override
+ void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
+ pw.print("[H]");
+ pw.print(" time=");
+ pw.print(sdf.format(new Date(mTime)));
+ pw.print(" hotplug port=");
+ pw.print(mPort);
+ pw.print(" connected=");
+ pw.println(mConnected);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388281d8..8326ef949858 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,15 +31,13 @@ import android.os.Handler;
* @hide
*/
public abstract class CountryDetectorBase {
- private static final String FEATURE_ID = "CountryDetector";
-
protected final Handler mHandler;
protected final Context mContext;
protected CountryListener mListener;
protected Country mDetectedCountry;
- public CountryDetectorBase(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ public CountryDetectorBase(Context ctx) {
+ mContext = ctx;
mHandler = new Handler();
}
@@ -47,7 +45,7 @@ public abstract class CountryDetectorBase {
* Start detecting the country that the user is in.
*
* @return the country if it is available immediately, otherwise null should
- * be returned.
+ * be returned.
*/
public abstract Country detectCountry();
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index a069e7ace636..1f458ed4e29d 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -87,6 +87,13 @@ public class LocationFudger {
mRandom = random;
mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M);
+ resetOffsets();
+ }
+
+ /**
+ * Resets the random offsets completely.
+ */
+ public void resetOffsets() {
mLatitudeOffsetM = nextRandomOffset();
mLongitudeOffsetM = nextRandomOffset();
mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 05867ba03cf6..f7e1398a2bd3 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -214,6 +214,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
* Updates the mSessionInfo. Returns true if the session info is changed.
*/
boolean updateSessionInfosIfNeededLocked() {
+ // Prevent to execute this method before mBtRouteProvider is created.
+ if (mBtRouteProvider == null) return false;
RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd86f1d563ba..0d402e57d335 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3440,6 +3440,27 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public ParceledListSlice<ConversationChannelWrapper> getConversations(
+ boolean onlyImportant) {
+ enforceSystemOrSystemUI("getConversations");
+ ArrayList<ConversationChannelWrapper> conversations =
+ mPreferencesHelper.getConversations(onlyImportant);
+ for (ConversationChannelWrapper conversation : conversations) {
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+ .setPackage(conversation.getPkg())
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setShortcutIds(Arrays.asList(
+ conversation.getNotificationChannel().getConversationId()));
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+ query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
+ if (shortcuts != null && !shortcuts.isEmpty()) {
+ conversation.setShortcutInfo(shortcuts.get(0));
+ }
+ }
+ return new ParceledListSlice<>(conversations);
+ }
+
+ @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 20c8625d22e4..b8186ed814fe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1186,6 +1186,44 @@ public class PreferencesHelper implements RankingConfig {
return groups;
}
+ public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+ synchronized (mPackagePreferences) {
+ ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+
+ for (PackagePreferences p : mPackagePreferences.values()) {
+ int N = p.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = p.channels.valueAt(i);
+ if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+ && (nc.isImportantConversation() || !onlyImportant)) {
+ ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(p.pkg);
+ conversation.setUid(p.uid);
+ conversation.setNotificationChannel(nc);
+ conversation.setParentChannelLabel(
+ p.channels.get(nc.getParentChannelId()).getName());
+ boolean blockedByGroup = false;
+ if (nc.getGroup() != null) {
+ NotificationChannelGroup group = p.groups.get(nc.getGroup());
+ if (group != null) {
+ if (group.isBlocked()) {
+ blockedByGroup = true;
+ } else {
+ conversation.setGroupLabel(group.getName());
+ }
+ }
+ }
+ if (!blockedByGroup) {
+ conversations.add(conversation);
+ }
+ }
+ }
+ }
+
+ return conversations;
+ }
+ }
+
public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
Objects.requireNonNull(pkg);
synchronized (mPackagePreferences) {
@@ -1199,6 +1237,8 @@ public class PreferencesHelper implements RankingConfig {
final NotificationChannel nc = r.channels.valueAt(i);
if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(r.pkg);
+ conversation.setUid(r.uid);
conversation.setNotificationChannel(nc);
conversation.setParentChannelLabel(
r.channels.get(nc.getParentChannelId()).getName());
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3a162173a59f..09e3febbb050 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -307,8 +306,8 @@ public class LauncherAppsService extends SystemService {
final int callingUserId = injectCallingUserId();
if (targetUserId == callingUserId) return true;
- if (mContext.checkCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED) {
+ if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
+ injectBinderCallingUid())) {
return true;
}
@@ -684,6 +683,15 @@ public class LauncherAppsService extends SystemService {
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
}
+ /**
+ * Returns true if the caller has the "INTERACT_ACROSS_USERS_FULL" permission.
+ */
+ @VisibleForTesting
+ boolean injectHasInteractAcrossUsersFullPermission(int callingPid, int callingUid) {
+ return mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
String packageName, List shortcutIds, List<LocusId> locusIds,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f6eb76bdaf97..2a3f7ed62845 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15158,10 +15158,13 @@ public class PackageManagerService extends IPackageManager.Stub
// We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
// this task was only focused on moving data on internal storage.
+ // We don't want ART profiles cleared, because they don't move,
+ // so we would be deleting the only copy (b/149200535).
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
for (int userId : userIds) {
try {
- mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+ mInstaller.destroyAppData(volumeUuid, move.packageName, userId, flags, 0);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
@@ -21733,6 +21736,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+ StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
@@ -21746,6 +21750,13 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
prepareAppDataLIF(pkg, user.id, flags);
+
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ // Prepare app data on external storage; currently this is used to
+ // setup any OBB dirs that were created by the installer correctly.
+ int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
+ smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 377fd16d4e19..12f7d5c27459 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2773,6 +2773,13 @@ public class ShortcutService extends IShortcutService.Stub {
userId, /* doCache= */ false);
}
+ @Override
+ public List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+ @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) {
+ return ShortcutService.this.getShareTargets(
+ callingPackage, intentFilter, userId).getList();
+ }
+
private void updateCachedShortcutsInternal(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId, boolean doCache) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index cb755f9cf52f..df3c83aec85a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -437,7 +437,7 @@ public class UserManagerService extends IUserManager.Stub {
/**
* Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
*
- * @see {@link #requestQuietModeEnabled(String, boolean, int, IntentSender)}
+ * @see #requestQuietModeEnabled(String, boolean, int, IntentSender, int)
*/
private class DisableQuietModeUserUnlockedCallback extends IProgressListener.Stub {
private final IntentSender mTarget;
@@ -967,7 +967,16 @@ public class UserManagerService extends IUserManager.Stub {
"target should only be specified when we are disabling quiet mode.");
}
- ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), userId, target != null);
+ final boolean dontAskCredential =
+ (flags & UserManager.QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL) != 0;
+ final boolean onlyIfCredentialNotRequired =
+ (flags & UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED) != 0;
+ if (dontAskCredential && onlyIfCredentialNotRequired) {
+ throw new IllegalArgumentException("invalid flags: " + flags);
+ }
+
+ ensureCanModifyQuietMode(
+ callingPackage, Binder.getCallingUid(), userId, target != null, dontAskCredential);
final long identity = Binder.clearCallingIdentity();
try {
if (enableQuietMode) {
@@ -976,11 +985,11 @@ public class UserManagerService extends IUserManager.Stub {
return true;
}
mLockPatternUtils.tryUnlockWithCachedUnifiedChallenge(userId);
- boolean needToShowConfirmCredential =
- mLockPatternUtils.isSecure(userId)
- && !StorageManager.isUserKeyUnlocked(userId);
+ final boolean needToShowConfirmCredential = !dontAskCredential
+ && mLockPatternUtils.isSecure(userId)
+ && !StorageManager.isUserKeyUnlocked(userId);
if (needToShowConfirmCredential) {
- if ((flags & UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED) != 0) {
+ if (onlyIfCredentialNotRequired) {
return false;
}
showConfirmCredentialToDisableQuietMode(userId, target);
@@ -1007,7 +1016,7 @@ public class UserManagerService extends IUserManager.Stub {
* {@link Manifest.permission#MANAGE_USERS}.
*/
private void ensureCanModifyQuietMode(String callingPackage, int callingUid,
- @UserIdInt int targetUserId, boolean startIntent) {
+ @UserIdInt int targetUserId, boolean startIntent, boolean dontAskCredential) {
if (hasManageUsersPermission()) {
return;
}
@@ -1015,6 +1024,10 @@ public class UserManagerService extends IUserManager.Stub {
throw new SecurityException("MANAGE_USERS permission is required to start intent "
+ "after disabling quiet mode.");
}
+ if (dontAskCredential) {
+ throw new SecurityException("MANAGE_USERS permission is required to disable quiet "
+ + "mode without credentials.");
+ }
if (!isSameProfileGroupNoChecks(UserHandle.getUserId(callingUid), targetUserId)) {
throw new SecurityException("MANAGE_USERS permission is required to modify quiet mode "
+ "for a different profile group.");
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e6eaf211a86a..9c945d5a7ea8 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -150,6 +150,12 @@ public final class DefaultPermissionGrantPolicy {
ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
+ private static final Set<String> FOREGROUND_LOCATION_PERMISSIONS = new ArraySet<>();
+ static {
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
private static final Set<String> COARSE_BACKGROUND_LOCATION_PERMISSIONS = new ArraySet<>();
static {
COARSE_BACKGROUND_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
@@ -587,11 +593,6 @@ public final class DefaultPermissionGrantPolicy {
DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
userId, CONTACTS_PERMISSIONS);
- // Maps
- grantPermissionsToSystemPackage(
- getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
- userId, ALWAYS_LOCATION_PERMISSIONS);
-
// Email
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackageForCategory(
@@ -609,7 +610,7 @@ public final class DefaultPermissionGrantPolicy {
}
}
grantPermissionsToPackage(browserPackage, userId, false /* ignoreSystemPackage */,
- true /*whitelistRestrictedPermissions*/, ALWAYS_LOCATION_PERMISSIONS);
+ true /*whitelistRestrictedPermissions*/, FOREGROUND_LOCATION_PERMISSIONS);
// Voice interaction
if (voiceInteractPackageNames != null) {
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 491c5ab2ac03..da3cbf9d03b4 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,15 +16,18 @@
package com.android.server.power;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.thermal.V1_0.ThermalStatus;
import android.hardware.thermal.V1_0.ThermalStatusCode;
import android.hardware.thermal.V1_1.IThermalCallback;
import android.hardware.thermal.V2_0.IThermalChangedCallback;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
import android.hardware.thermal.V2_0.ThrottlingSeverity;
import android.os.Binder;
import android.os.CoolingDevice;
+import android.os.Handler;
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
@@ -36,6 +39,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.Temperature;
import android.util.ArrayMap;
import android.util.EventLog;
@@ -43,6 +47,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
@@ -54,6 +59,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -100,6 +106,9 @@ public class ThermalManagerService extends SystemService {
/** Hal ready. */
private final AtomicBoolean mHalReady = new AtomicBoolean();
+ /** Watches temperatures to forecast when throttling will occur */
+ private final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher();
+
/** Invalid throttling status */
private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
@@ -154,6 +163,7 @@ public class ThermalManagerService extends SystemService {
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
+ mTemperatureWatcher.updateSevereThresholds();
mHalReady.set(true);
}
}
@@ -462,6 +472,15 @@ public class ThermalManagerService extends SystemService {
}
}
+ @Override
+ public float getThermalHeadroom(int forecastSeconds) {
+ if (!mHalReady.get()) {
+ return Float.NaN;
+ }
+
+ return mTemperatureWatcher.getForecast(forecastSeconds);
+ }
+
private void dumpItemsLocked(PrintWriter pw, String prefix,
Collection<?> items) {
for (Iterator iterator = items.iterator(); iterator.hasNext();) {
@@ -616,6 +635,10 @@ public class ThermalManagerService extends SystemService {
protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type);
+ @NonNull
+ protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type);
+
protected abstract boolean connectToHal();
protected abstract void dump(PrintWriter pw, String prefix);
@@ -728,6 +751,12 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -857,6 +886,12 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -975,6 +1010,32 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<TemperatureThreshold> ret = new ArrayList<>();
+ if (mThermalHal20 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal20.getTemperatureThresholds(shouldFilter, type,
+ (status, thresholds) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ ret.addAll(thresholds);
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperature thresholds because of HAL "
+ + "error: " + status.debugMessage);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
+ }
+ return ret;
+ }
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -1001,4 +1062,190 @@ public class ThermalManagerService extends SystemService {
}
}
+ private class TemperatureWatcher {
+ private final Handler mHandler = BackgroundThread.getHandler();
+
+ /** Map of skin temperature sensor name to a corresponding list of samples */
+ @GuardedBy("mSamples")
+ private final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
+
+ /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
+ @GuardedBy("mSamples")
+ private ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
+
+ @GuardedBy("mSamples")
+ private long mLastForecastCallTimeMillis = 0;
+
+ void updateSevereThresholds() {
+ synchronized (mSamples) {
+ List<TemperatureThreshold> thresholds =
+ mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+ for (int t = 0; t < thresholds.size(); ++t) {
+ TemperatureThreshold threshold = thresholds.get(t);
+ if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
+ continue;
+ }
+ float temperature =
+ threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
+ if (!Float.isNaN(temperature)) {
+ mSevereThresholds.put(threshold.name,
+ threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]);
+ }
+ }
+ }
+ }
+
+ private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
+ private static final int RING_BUFFER_SIZE = 30;
+
+ private void updateTemperature() {
+ synchronized (mSamples) {
+ if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis
+ < INACTIVITY_THRESHOLD_MILLIS) {
+ // Trigger this again after a second as long as forecast has been called more
+ // recently than the inactivity timeout
+ mHandler.postDelayed(this::updateTemperature, 1000);
+ } else {
+ // Otherwise, we've been idle for at least 10 seconds, so we should
+ // shut down
+ mSamples.clear();
+ return;
+ }
+
+ long now = SystemClock.elapsedRealtime();
+ List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
+ Temperature.TYPE_SKIN);
+
+ for (int t = 0; t < temperatures.size(); ++t) {
+ Temperature temperature = temperatures.get(t);
+
+ // Filter out invalid temperatures. If this results in no values being stored at
+ // all, the mSamples.empty() check in getForecast() will catch it.
+ if (Float.isNaN(temperature.getValue())) {
+ continue;
+ }
+
+ ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
+ k -> new ArrayList<>(RING_BUFFER_SIZE));
+ if (samples.size() == RING_BUFFER_SIZE) {
+ samples.remove(0);
+ }
+ samples.add(new Sample(now, temperature.getValue()));
+ }
+ }
+ }
+
+ /**
+ * Calculates the trend using a linear regression. As the samples are degrees Celsius with
+ * associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond.
+ */
+ private float getSlopeOf(List<Sample> samples) {
+ long sumTimes = 0L;
+ float sumTemperatures = 0.0f;
+ for (int s = 0; s < samples.size(); ++s) {
+ Sample sample = samples.get(s);
+ sumTimes += sample.time;
+ sumTemperatures += sample.temperature;
+ }
+ long meanTime = sumTimes / samples.size();
+ float meanTemperature = sumTemperatures / samples.size();
+
+ long sampleVariance = 0L;
+ float sampleCovariance = 0.0f;
+ for (int s = 0; s < samples.size(); ++s) {
+ Sample sample = samples.get(s);
+ long timeDelta = sample.time - meanTime;
+ float temperatureDelta = sample.temperature - meanTemperature;
+ sampleVariance += timeDelta * timeDelta;
+ sampleCovariance += timeDelta * temperatureDelta;
+ }
+
+ return sampleCovariance / sampleVariance;
+ }
+
+ /**
+ * Used to determine the temperature corresponding to 0.0. Given that 1.0 is pinned at the
+ * temperature corresponding to the SEVERE threshold, we set 0.0 to be that temperature
+ * minus DEGREES_BETWEEN_ZERO_AND_ONE.
+ */
+ private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
+
+ private float normalizeTemperature(float temperature, float severeThreshold) {
+ synchronized (mSamples) {
+ float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
+ if (temperature <= zeroNormalized) {
+ return 0.0f;
+ }
+ float delta = temperature - zeroNormalized;
+ return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
+ }
+ }
+
+ private static final int MINIMUM_SAMPLE_COUNT = 3;
+
+ float getForecast(int forecastSeconds) {
+ synchronized (mSamples) {
+ mLastForecastCallTimeMillis = System.currentTimeMillis();
+ if (mSamples.isEmpty()) {
+ updateTemperature();
+ }
+
+ // If somehow things take much longer than expected or there are no temperatures
+ // to sample, return early
+ if (mSamples.isEmpty()) {
+ Slog.e(TAG, "No temperature samples found");
+ return Float.NaN;
+ }
+
+ // If we don't have any thresholds, we can't normalize the temperatures,
+ // so return early
+ if (mSevereThresholds.isEmpty()) {
+ Slog.e(TAG, "No temperature thresholds found");
+ return Float.NaN;
+ }
+
+ float maxNormalized = Float.NaN;
+ for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
+ String name = entry.getKey();
+ ArrayList<Sample> samples = entry.getValue();
+
+ Float threshold = mSevereThresholds.get(name);
+ if (threshold == null) {
+ Slog.e(TAG, "No threshold found for " + name);
+ continue;
+ }
+
+ float currentTemperature = samples.get(0).temperature;
+
+ if (samples.size() < MINIMUM_SAMPLE_COUNT) {
+ // Don't try to forecast, just use the latest one we have
+ float normalized = normalizeTemperature(currentTemperature, threshold);
+ if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+ maxNormalized = normalized;
+ }
+ continue;
+ }
+
+ float slope = getSlopeOf(samples);
+ float normalized = normalizeTemperature(
+ currentTemperature + slope * forecastSeconds * 1000, threshold);
+ if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+ maxNormalized = normalized;
+ }
+ }
+
+ return maxNormalized;
+ }
+ }
+
+ private class Sample {
+ public long time;
+ public float temperature;
+
+ Sample(long time, float temperature) {
+ this.time = time;
+ this.temperature = temperature;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8a0622..e72ba8d9f01b 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,6 @@ public final class TwilightService extends SystemService
implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
private static final String TAG = "TwilightService";
- private static final String FEATURE_ID = "TwilightService";
private static final boolean DEBUG = false;
private static final int MSG_START_LISTENING = 1;
@@ -74,7 +73,7 @@ public final class TwilightService extends SystemService
protected TwilightState mLastTwilightState;
public TwilightService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
+ super(context);
mHandler = new Handler(Looper.getMainLooper(), this);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 46596e357de2..688f47475d8b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -111,6 +111,7 @@ import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -731,53 +732,14 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
newBounds);
hasNewOverrideBounds = true;
}
-
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- // If entering split screen or if something about the available split area changes,
- // recalculate the split windows to match the new configuration.
- if (rotationChanged || windowingModeChanged
- || prevDensity != getConfiguration().densityDpi
- || prevScreenW != getConfiguration().screenWidthDp
- || prevScreenH != getConfiguration().screenHeightDp) {
- calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
- hasNewOverrideBounds = true;
- }
- }
}
if (windowingModeChanged) {
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- // immediately resize so docked bounds are available in onSplitScreenModeActivated
- setTaskDisplayedBounds(null);
- setTaskBounds(newBounds);
- setBounds(newBounds);
- newBounds.set(newBounds);
- } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- Rect dockedBounds = display.getRootSplitScreenPrimaryTask().getBounds();
- final boolean isMinimizedDock =
- display.mDisplayContent.getDockedDividerController().isMinimizedDock();
- if (isMinimizedDock) {
- Task topTask = display.getRootSplitScreenPrimaryTask().getTopMostTask();
- if (topTask != null) {
- dockedBounds = topTask.getBounds();
- }
- }
- getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- hasNewOverrideBounds = true;
- }
+ display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- if (inSplitScreenPrimaryWindowingMode()) {
- mStackSupervisor.resizeDockedStackLocked(new Rect(newBounds),
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ if (inSplitScreenWindowingMode()) {
+ setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
resize(new Rect(newBounds), null /* tempTaskBounds */,
@@ -920,11 +882,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
// warning toast about it.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- final ActivityStack primarySplitStack = display.getRootSplitScreenPrimaryTask();
- primarySplitStack.setWindowingModeInSurfaceTransaction(WINDOWING_MODE_UNDEFINED,
- false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- primarySplitStack == this ? creating : false);
+ display.onSplitScreenModeDismissed();
}
}
@@ -1218,7 +1176,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenStack != null) {
final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
- if (display.getIndexOf(topFullScreenStack)
+ if (primarySplitScreenStack != null && display.getIndexOf(topFullScreenStack)
> display.getIndexOf(primarySplitScreenStack)) {
primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
}
@@ -3999,17 +3957,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
? ((WindowContainer) oldParent).getDisplayContent() : null;
super.onParentChanged(newParent, oldParent);
- if (display != null && inSplitScreenPrimaryWindowingMode()
- // only do this for the base stack
- && !newParent.inSplitScreenPrimaryWindowingMode()) {
- // If we created a docked stack we want to resize it so it resizes all other stacks
- // in the system.
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
- mTmpRect2, null, null, PRESERVE_WINDOWS);
- }
-
// Resume next focusable stack after reparenting to another display if we aren't removing
// the prevous display.
if (oldDisplay != null && oldDisplay.isRemoving()) {
@@ -4955,6 +4902,12 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
}
@Override
+ public SurfaceControl getParentSurfaceControl() {
+ // Tile is a "virtual" parent, so we need to intercept the parent surface here
+ return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
+ }
+
+ @Override
void removeImmediately() {
// TODO(task-hierarchy): remove this override when tiles are in hierarchy
if (mTile != null) {
@@ -5007,6 +4960,10 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
if (!matchParentBounds()) {
final Rect bounds = getRequestedOverrideBounds();
bounds.dumpDebug(proto, BOUNDS);
+ } else if (getStack().getTile() != null) {
+ // use tile's bounds here for cts.
+ final Rect bounds = getStack().getTile().getRequestedOverrideBounds();
+ bounds.dumpDebug(proto, BOUNDS);
}
getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
@@ -5026,6 +4983,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
}
+ proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 7720f7f41092..70cd01b44f05 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2457,7 +2457,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+ dockedStack.getDisplay().onSplitScreenModeDismissed();
+ dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
}
return;
}
@@ -2819,7 +2821,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final DisplayContent display = task.getStack().getDisplay();
final ActivityStack topSecondaryStack =
display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (topSecondaryStack.isActivityTypeHome()) {
+ if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
// If the home activity is the top split-screen secondary stack, then the
// primary split-screen stack is in the minimized mode which means it can't
// receive input keys, so we should move the focused app to the home app so that
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 132e48656df3..882d5c70de25 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,7 +37,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -229,6 +228,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
import android.view.WindowManager;
import com.android.internal.R;
@@ -2269,6 +2269,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
+ if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
+ }
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
@@ -2286,10 +2289,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ActivityStack stack = task.getStack();
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.getTile() != null) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+ }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2719,36 +2728,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- if (isInLockTaskMode()) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
- + getLockTaskModeState());
- return false;
- }
-
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
- if (task == null) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
- return false;
- }
- if (!task.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
- + " non-standard task " + taskId + " to split-screen windowing mode");
- }
-
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
- + " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
- final int windowingMode = task.getWindowingMode();
- final ActivityStack stack = task.getStack();
- if (toTop) {
- stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
- }
- stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- false /* creating */);
- return windowingMode != task.getWindowingMode();
+ return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ toTop);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2756,6 +2737,49 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
+ * Moves the specified task into a split-screen tile.
+ */
+ private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+ + "split-screen mode: " + windowingMode);
+ }
+ if (isInLockTaskMode()) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
+ + getLockTaskModeState());
+ return false;
+ }
+
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+ return false;
+ }
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to split-screen windowing mode");
+ }
+
+ final int prevMode = task.getWindowingMode();
+ final ActivityStack stack = task.getStack();
+ TaskTile tile = null;
+ for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
+ tile = stack.getDisplay().getStackAt(i).asTile();
+ if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ break;
+ }
+ }
+ if (tile == null) {
+ throw new IllegalStateException("Can't enter split without associated tile");
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ mTaskOrganizerController.applyContainerTransaction(wct, null);
+ return prevMode != task.getWindowingMode();
+ }
+
+ /**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@@ -3963,46 +3987,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
- * Dismisses split-screen multi-window mode.
- * @param toTop If true the current primary split-screen stack will be placed or left on top.
- */
- @Override
- public void dismissSplitScreenMode(boolean toTop) {
- enforceCallerIsRecentsOrHasPermission(
- MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
- Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
- return;
- }
-
- if (toTop) {
- // Caller wants the current split-screen primary stack to be the top stack after
- // it goes fullscreen, so move it to the front.
- stack.moveToFront("dismissSplitScreenMode");
- } else {
- // In this case the current split-screen primary stack shouldn't be the top
- // stack after it goes fullscreen, so we move the focus to the top-most
- // split-screen secondary stack next to it.
- final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (otherStack != null) {
- otherStack.moveToFront("dismissSplitScreenMode_other");
- }
- }
-
- stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
* Dismisses Pip
* @param animate True if the dismissal should be animated.
* @param animationDuration The duration of the resize animation in milliseconds or -1 if the
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 840abb12bb13..e60ab3fdc31c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,9 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -32,6 +30,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -56,9 +55,6 @@ import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -84,9 +80,6 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
-import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -105,12 +98,15 @@ import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.DisplayContentProto.TASKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1553,6 +1549,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
+ * If the provided {@link ActivityRecord} can be displayed in an orientation different from the
+ * display's, it will be rotated to match its requested orientation.
+ *
+ * @see #rotationForActivityInDifferentOrientation(ActivityRecord).
+ * @see WindowToken#applyFixedRotationTransform(DisplayInfo, DisplayFrames, Configuration)
+ */
+ void rotateInDifferentOrientationIfNeeded(ActivityRecord activityRecord) {
+ int rotation = rotationForActivityInDifferentOrientation(activityRecord);
+ if (rotation != NO_ROTATION) {
+ startFixedRotationTransform(activityRecord, rotation);
+ }
+ }
+
+ /**
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
@@ -2794,54 +2804,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void adjustForImeIfNeeded() {
final WindowState imeWin = mInputMethodWindow;
- final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
- && !mDividerControllerLocked.isImeHideRequested();
- final ActivityStack dockedStack = getRootSplitScreenPrimaryTask();
- final boolean dockVisible = dockedStack != null;
- final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
- final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
- final int imeDockSide = (dockVisible && imeTargetStack != null) ?
- imeTargetStack.getDockSide() : DOCKED_INVALID;
- final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
- final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
+ final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
+ && imeWin.isDisplayedLw();
final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
- final boolean imeHeightChanged = imeVisible &&
- imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
-
- // This includes a case where the docked stack is unminimizing and IME is visible for the
- // bottom side stack. The condition prevents adjusting the override task bounds for IME to
- // the minimized docked stack bounds.
- final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
- || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
- && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
-
- // The divider could be adjusted for IME position, or be thinner than usual,
- // or both. There are three possible cases:
- // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
- // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
- // - If IME is not visible, divider is not moved and is normal width.
-
- if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
- if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
- && stack.inSplitScreenWindowingMode()) {
- stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
- } else {
- stack.resetAdjustedForIme(false);
- }
- }
- mDividerControllerLocked.setAdjustedForIme(
- imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
- } else {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- stack.resetAdjustedForIme(!dockVisible);
- }
- mDividerControllerLocked.setAdjustedForIme(
- false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
- }
mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
}
@@ -3625,20 +3590,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
- mInputMethodControlTarget = computeImeControlTarget();
- mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
+ updateImeControlTarget(mInputMethodTarget);
updateImeParent();
}
/**
* IME control target is the window that controls the IME visibility and animation.
* This window is same as the window on which startInput is called.
- * @param target the window that receives IME control.
+ * @param target the window that receives IME control. This is ignored if we aren't attaching
+ * the IME to an app (eg. when in multi-window mode).
*
* @see #getImeControlTarget()
*/
- void updateImeControlTarget(WindowState target) {
- mInputMethodControlTarget = target;
+ void updateImeControlTarget(InsetsControlTarget target) {
+ if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
+ mInputMethodControlTarget = mRemoteInsetsControlTarget;
+ } else {
+ // Otherwise, we just use the ime target
+ mInputMethodControlTarget = target;
+ }
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
}
@@ -3671,19 +3641,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mWindowContainers.getSurfaceControl();
}
- /**
- * Computes which control-target the IME should be attached to.
- */
- @VisibleForTesting
- InsetsControlTarget computeImeControlTarget() {
- if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
- return mRemoteInsetsControlTarget;
- }
-
- // Otherwise, we just use the ime target
- return mInputMethodTarget;
- }
-
void setLayoutNeeded() {
if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
mLayoutNeeded = true;
@@ -4069,7 +4026,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+ /** @return the orientation of the display when it's rotation is ROTATION_0. */
int getNaturalOrientation() {
return mBaseDisplayWidth < mBaseDisplayHeight
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
@@ -4510,8 +4467,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
} else {
mRootSplitScreenPrimaryTask = stack;
- mDisplayContent.onSplitScreenModeActivated();
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
}
@@ -4523,11 +4478,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mRootPinnedTask = null;
} else if (stack == mRootSplitScreenPrimaryTask) {
mRootSplitScreenPrimaryTask = null;
- mDisplayContent.onSplitScreenModeDismissed();
- // Re-set the split-screen create mode whenever the split-screen stack is removed.
- mWmService.setDockedStackCreateStateLocked(
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
}
@@ -5943,6 +5893,33 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
}
+ /** @return the tile to create the next stack in. */
+ private TaskTile updateLaunchTile(int windowingMode) {
+ if (!isSplitScreenWindowingMode(windowingMode)) {
+ // Only split-screen windowing modes interact with tiles.
+ return null;
+ }
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final TaskTile t = getStackAt(i).asTile();
+ if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ continue;
+ }
+ // If not already set, pick a launch tile which is not the one we are launching
+ // into.
+ if (mLaunchTile == null) {
+ for (int j = 0, n = getStackCount(); j < n; ++j) {
+ TaskTile tt = getStackAt(j).asTile();
+ if (tt != t) {
+ mLaunchTile = tt;
+ break;
+ }
+ }
+ }
+ return t;
+ }
+ return mLaunchTile;
+ }
+
@VisibleForTesting
ActivityStack createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop, ActivityInfo info, Intent intent) {
@@ -5955,13 +5932,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
info.applicationInfo = new ApplicationInfo();
}
+ TaskTile tile = updateLaunchTile(windowingMode);
+ if (tile != null) {
+ // Since this stack will be put into a tile, its windowingMode will be inherited.
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
final ActivityStack stack = new ActivityStack(this, stackId,
mRootWindowContainer.mStackSupervisor, activityType, info, intent);
addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
true /* creating */);
-
+ if (tile != null) {
+ tile.addChild(stack, 0 /* index */);
+ }
return stack;
}
@@ -6181,16 +6165,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void onSplitScreenModeDismissed() {
mAtmService.deferWindowLayout();
try {
- // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+ mLaunchTile = null;
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
- continue;
+ final TaskTile t = getStackAt(i).asTile();
+ if (t != null) {
+ t.removeAllChildren();
}
- otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
- false /* showRecents */, false /* enteringSplitScreenMode */,
- true /* deferEnsuringVisibility */, false /* creating */);
}
+ mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
+ false /* animate */);
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6208,27 +6191,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- void onSplitScreenModeActivated() {
- mAtmService.deferWindowLayout();
- try {
- // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
- final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (otherStack == splitScreenPrimaryStack
- || !otherStack.affectedBySplitScreenResize()) {
- continue;
- }
- otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- false /* animate */, false /* showRecents */,
- true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- false /* creating */);
- }
- } finally {
- mAtmService.continueWindowLayout();
- }
- }
-
/**
* Returns true if the {@param windowingMode} is supported based on other parameters passed in.
* @param windowingMode The windowing mode we are checking support for.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 1a0dcb9e1218..64c5faa24454 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -527,9 +527,15 @@ public class DisplayRotation {
}
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
- mDisplayContent.sendNewConfiguration();
- if (t != null) {
- mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ mService.mAtmService.deferWindowLayout();
+ try {
+ mDisplayContent.sendNewConfiguration();
+ if (t != null) {
+ mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t,
+ null /* organizer */);
+ }
+ } finally {
+ mService.mAtmService.continueWindowLayout();
}
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 872379efa389..6431e117c4e6 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -745,7 +745,7 @@ public class DockedStackDividerController {
* @param minimizedDock Whether the docked stack is currently minimized.
* @param animate Whether to animate the change.
*/
- private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
+ void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
final boolean wasMinimized = mMinimizedDock;
mMinimizedDock = minimizedDock;
if (minimizedDock == wasMinimized) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 00947d766e89..44034edaa4bf 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -31,14 +31,14 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.os.IBinder;
import android.os.RemoteException;
@@ -411,8 +411,7 @@ class KeyguardController {
if (stack == null) {
return;
}
- mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
- stack.isFocusedStackOnDisplay());
+ mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 251d0f1105d5..e923e6413546 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -34,6 +33,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.ActivityManager.TaskSnapshot;
import android.app.WindowConfiguration;
import android.graphics.Point;
@@ -414,15 +414,15 @@ public class RecentsAnimationController implements DeathRecipient {
}
// Save the minimized home height
- final ActivityStack dockedStack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
- mDisplayContent.getConfiguration(),
- dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
- mMinimizedHomeBounds);
+ mMinimizedHomeBounds = mDisplayContent.getRootHomeTask().getBounds();
mService.mWindowPlacerLocked.performSurfacePlacement();
+ // If the target activity has a fixed orientation which is different from the current top
+ // activity, it will be rotated before being shown so we avoid a screen rotation
+ // animation when showing the Recents view.
+ mDisplayContent.rotateInDifferentOrientationIfNeeded(mTargetActivityRecord);
+
// Notify that the animation has started
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(true /* running */);
@@ -695,6 +695,9 @@ public class RecentsAnimationController implements DeathRecipient {
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
mTargetActivityRecord.token);
}
+ if (mTargetActivityRecord.hasFixedRotationTransform()) {
+ mTargetActivityRecord.clearFixedRotationTransform();
+ }
}
// Notify that the animation has ended
@@ -828,6 +831,19 @@ public class RecentsAnimationController implements DeathRecipient {
return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
}
+ /**
+ * If the animation target ActivityRecord has a fixed rotation ({@link
+ * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly.
+ *
+ * This avoids any screen rotation animation when animating to the Recents view.
+ */
+ void applyFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
+ if (mTargetActivityRecord == null) {
+ return;
+ }
+ wallpaper.applyFixedRotationTransform(mTargetActivityRecord);
+ }
+
@VisibleForTesting
class TaskAnimationAdapter implements AnimationAdapter {
@@ -844,8 +860,8 @@ public class RecentsAnimationController implements DeathRecipient {
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
final WindowContainer container = mTask.getParent();
- container.getRelativeDisplayedPosition(mPosition);
mBounds.set(container.getDisplayedBounds());
+ mPosition.set(mBounds.left, mBounds.top);
}
RemoteAnimationTarget createRemoteAnimationTarget() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 34b5c117d118..c7f2cc7f3692 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2145,12 +2145,6 @@ class Task extends WindowContainer<WindowContainer> {
// For floating tasks, calculate the smallest width from the bounds of the task
inOutConfig.smallestScreenWidthDp = (int) (
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
- // Iterating across all screen orientations, and return the minimum of the task
- // width taking into account that the bounds might change because the snap
- // algorithm snaps to a different value
- inOutConfig.smallestScreenWidthDp =
- getSmallestScreenWidthDpForDockedBounds(mTmpFullBounds);
}
// otherwise, it will just inherit
}
@@ -3257,6 +3251,10 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
+ TaskTile asTile() {
+ return null;
+ }
+
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4b13a0c1f75d..4d5621cd5b32 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -492,6 +492,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
if (!(container instanceof Task)) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
+ if (container.getDisplayContent() == null) {
+ Slog.w(TAG, "Container is no longer attached: " + container);
+ return 0;
+ }
if (hop.isReparent()) {
// special case for tiles since they are "virtual" parents
if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 369db05452f7..74d5c338a68d 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -31,7 +31,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
-import android.view.SurfaceControl;
import java.util.ArrayList;
import java.util.Comparator;
@@ -78,30 +77,9 @@ public class TaskTile extends ActivityStack {
// Virtual parent, so don't notify children.
}
- /**
- * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
- * leash by moving known children to a new surfacecontrol and then removing the old one.
- */
- void cleanupSurfaces() {
- if (mSurfaceControl == null) {
- return;
- }
- SurfaceControl oldSurface = mSurfaceControl;
- WindowContainer parentWin = getParent();
- if (parentWin == null) {
- return;
- }
- mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
- + getRequestedOverrideWindowingMode()).setContainerLayer().build();
- SurfaceControl.Transaction t = parentWin.getPendingTransaction();
- t.show(mSurfaceControl);
- for (int i = 0; i < mChildren.size(); ++i) {
- if (mChildren.get(i).getSurfaceControl() == null) {
- continue;
- }
- mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
- }
- t.remove(oldSurface);
+ @Override
+ TaskTile asTile() {
+ return this;
}
@Override
@@ -215,6 +193,12 @@ public class TaskTile extends ActivityStack {
super.removeImmediately();
}
+ @Override
+ void taskOrganizerDied() {
+ super.taskOrganizerDied();
+ removeImmediately();
+ }
+
static TaskTile forToken(IBinder token) {
try {
return (TaskTile) ((TaskToken) token).getContainer();
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf978cbab..1e22141f232a 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -122,10 +122,37 @@ class WallpaperWindowToken extends WindowToken {
mDisplayContent.setLayoutNeeded();
}
- final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
+ if (visible) {
+ final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
+ if (wallpaperTarget != null
+ && recentsAnimationController != null
+ && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
+ // If the Recents animation is running, and the wallpaper target is the animating
+ // task we want the wallpaper to be rotated in the same orientation as the
+ // RecentsAnimation's target (e.g the launcher)
+ recentsAnimationController.applyFixedRotationTransformIfNeeded(this);
+ } else if (wallpaperTarget != null
+ && wallpaperTarget.mToken.hasFixedRotationTransform()) {
+ // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its
+ // rotation
+ applyFixedRotationTransform(wallpaperTarget.mToken);
+ } else if (hasFixedRotationTransform()) {
+ clearFixedRotationTransform();
+ }
+ }
+
+ DisplayInfo displayInfo = getFixedRotationTransformDisplayInfo();
+ if (displayInfo == null) {
+ displayInfo = mDisplayContent.getDisplayInfo();
+ }
+
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2a7d551c1b37..68b8348e706f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7492,15 +7492,14 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null) {
- WindowState imeTarget = dc.getImeControlTarget();
- if (imeTarget == null) {
+ InsetsControlTarget imeControlTarget = dc.mInputMethodControlTarget;
+ if (imeControlTarget == null) {
return;
}
// If there was a pending IME show(), reset it as IME has been
// requested to be hidden.
- imeTarget.getDisplayContent().getInsetsStateController().getImeSourceProvider()
- .abortShowImePostLayout();
- imeTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
+ imeControlTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1cfd0d49d432..b25008373a7b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2686,18 +2686,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
- if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
- // The owner of the docked divider died :( We reset the docked stack,
- // just in case they have the divider at an unstable position. Better
- // also reset drag resizing state, because the owner can't do it
- // anymore.
- final ActivityStack stack =
- dc.getRootSplitScreenPrimaryTask();
- if (stack != null) {
- stack.resetDockedStackToMiddle();
- }
- resetSplitScreenResizing = true;
- }
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 118056631cb1..48c7812afec0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -377,6 +377,19 @@ class WindowToken extends WindowContainer<WindowState> {
onConfigurationChanged(getParent().getConfiguration());
}
+ /**
+ * Copies the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
+ * one.
+ */
+ void applyFixedRotationTransform(WindowToken other) {
+ final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState;
+ if (fixedRotationState != null) {
+ applyFixedRotationTransform(fixedRotationState.mDisplayInfo,
+ fixedRotationState.mDisplayFrames,
+ fixedRotationState.mRotatedOverrideConfiguration);
+ }
+ }
+
/** Clears the transformation and continue updating the orientation change of display. */
void clearFixedRotationTransform() {
if (mFixedRotationTransformState == null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 9b85a7b55c94..eff222a2051a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -82,4 +82,8 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public long getManagedProfileMaximumTimeOff(ComponentName admin) {
return 0;
}
+
+ public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 731cd1e3a503..b239b6849d91 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -48,6 +48,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_
import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
+import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
@@ -2290,6 +2291,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
context, requestCode, intent, flags, options, user);
}
+ PendingIntent pendingIntentGetBroadcast(
+ Context context, int requestCode, Intent intent, int flags) {
+ return PendingIntent.getBroadcast(context, requestCode, intent, flags);
+ }
+
void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, int userHandle) {
mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents,
@@ -7036,9 +7042,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
saveSettingsLocked(userId);
}
- mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
- new Intent(DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
- UserHandle.getUserHandleForUid(frpManagementAgentUid)));
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED).addFlags(
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
+
+ mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(intent,
+ UserHandle.getUserHandleForUid(frpManagementAgentUid),
+ android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION)
@@ -10036,35 +10046,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean checkCallerIsCurrentUserOrProfile() {
- final int callingUserId = UserHandle.getCallingUserId();
- final long token = mInjector.binderClearCallingIdentity();
- try {
- UserInfo currentUser;
- UserInfo callingUser = getUserInfo(callingUserId);
- try {
- currentUser = mInjector.getIActivityManager().getCurrentUser();
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
- return false;
- }
-
- if (callingUser.isManagedProfile() && callingUser.profileGroupId != currentUser.id) {
- Slog.e(LOG_TAG, "Cannot set permitted input methods for managed profile "
- + "of a user that isn't the foreground user.");
- return false;
- }
- if (!callingUser.isManagedProfile() && callingUserId != currentUser.id ) {
- Slog.e(LOG_TAG, "Cannot set permitted input methods "
- + "of a user that isn't the foreground user.");
- return false;
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- return true;
- }
-
@Override
public boolean setPermittedInputMethods(ComponentName who, List packageList) {
if (!mHasFeature) {
@@ -14513,12 +14494,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userHandle = mInjector.userHandleGetCallingUserId();
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- DevicePolicyData policy = getUserData(userHandle);
- if (policy.mPasswordTokenHandle != 0) {
- return mInjector.binderWithCleanCallingIdentity(
- () -> mLockPatternUtils.isEscrowTokenActive(policy.mPasswordTokenHandle,
- userHandle));
- }
+ return isResetPasswordTokenActiveForUserLocked(userHandle);
+ }
+ }
+
+ private boolean isResetPasswordTokenActiveForUserLocked(int userHandle) {
+ DevicePolicyData policy = getUserData(userHandle);
+ if (policy.mPasswordTokenHandle != 0) {
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ mLockPatternUtils.isEscrowTokenActive(policy.mPasswordTokenHandle, userHandle));
}
return false;
}
@@ -15666,8 +15650,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void updateProfileOffAlarm(long profileOffDeadline) {
final AlarmManager am = mInjector.getAlarmManager();
- final PendingIntent pi = PendingIntent.getBroadcast(mContext, REQUEST_PROFILE_OFF_DEADLINE,
- new Intent(ACTION_PROFILE_OFF_DEADLINE),
+ final PendingIntent pi = mInjector.pendingIntentGetBroadcast(
+ mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pi);
if (profileOffDeadline != 0) {
@@ -15802,4 +15786,34 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return admin.mProfileMaximumTimeOff;
}
}
+
+ @Override
+ public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
+ enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
+ synchronized (getLockObject()) {
+ final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
+ if (poAdmin == null
+ || getEncryptionStatus() != ENCRYPTION_STATUS_ACTIVE_PER_USER
+ || !isResetPasswordTokenActiveForUserLocked(userId)) {
+ return false;
+ }
+ final ApplicationInfo poAppInfo;
+ try {
+ poAppInfo = mIPackageManager.getApplicationInfo(
+ poAdmin.info.getPackageName(), 0 /* flags */, userId);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Failed to query PO app info", e);
+ return false;
+ }
+ if (poAppInfo == null) {
+ Slog.wtf(LOG_TAG, "Cannot find AppInfo for profile owner");
+ return false;
+ }
+ if (!poAppInfo.isEncryptionAware()) {
+ return false;
+ }
+ Slog.d(LOG_TAG, "PO should be able to reset password from direct boot");
+ return true;
+ }
+ }
}
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 663bf4f30708..2499614a3738 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -88,7 +88,7 @@ public class PeopleService extends SystemService {
@Override
public void onCreatePredictionSession(AppPredictionContext context,
AppPredictionSessionId sessionId) {
- mSessions.put(sessionId, new SessionInfo(context, mDataManager));
+ mSessions.put(sessionId, new SessionInfo(context, mDataManager, sessionId.getUserId()));
}
@Override
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index eaa0781f12ef..28612f1dd49b 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -16,6 +16,7 @@
package com.android.server.people;
+import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.IPredictionCallback;
@@ -38,9 +39,10 @@ class SessionInfo {
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<>();
- SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
+ SessionInfo(AppPredictionContext predictionContext, DataManager dataManager,
+ @UserIdInt int callingUserId) {
mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
- this::updatePredictions, dataManager);
+ this::updatePredictions, dataManager, callingUserId);
}
void addCallback(IPredictionCallback callback) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index dd9cbd00f6a2..6b97c98b0029 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -34,13 +34,11 @@ import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Process;
@@ -83,7 +81,6 @@ import java.util.function.Function;
*/
public class DataManager {
- private static final String PLATFORM_PACKAGE_NAME = "android";
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
@@ -106,7 +103,6 @@ public class DataManager {
private ShortcutServiceInternal mShortcutServiceInternal;
private PackageManagerInternal mPackageManagerInternal;
- private ShortcutManager mShortcutManager;
private UserManager mUserManager;
public DataManager(Context context) {
@@ -125,7 +121,6 @@ public class DataManager {
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mShortcutManager = mContext.getSystemService(ShortcutManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
mShortcutServiceInternal.addListener(new ShortcutServiceListener());
@@ -171,8 +166,7 @@ public class DataManager {
mNotificationListeners.put(userId, notificationListener);
try {
notificationListener.registerAsSystemService(mContext,
- new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getCanonicalName()),
- userId);
+ new ComponentName(mContext, getClass()), userId);
} catch (RemoteException e) {
// Should never occur for local calls.
}
@@ -242,8 +236,8 @@ public class DataManager {
* Iterates through all the {@link PackageData}s owned by the unlocked users who are in the
* same profile group as the calling user.
*/
- public void forAllPackages(Consumer<PackageData> consumer) {
- List<UserInfo> users = mUserManager.getEnabledProfiles(mInjector.getCallingUserId());
+ void forPackagesInProfile(@UserIdInt int callingUserId, Consumer<PackageData> consumer) {
+ List<UserInfo> users = mUserManager.getEnabledProfiles(callingUserId);
for (UserInfo userInfo : users) {
UserData userData = getUnlockedUserData(userInfo.id);
if (userData != null) {
@@ -275,8 +269,10 @@ public class DataManager {
* Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
* the specified {@link IntentFilter}.
*/
- public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
- return mShortcutManager.getShareTargets(intentFilter);
+ public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter,
+ @UserIdInt int callingUserId) {
+ return mShortcutServiceInternal.getShareTargets(
+ mContext.getPackageName(), intentFilter, callingUserId);
}
/** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -361,7 +357,7 @@ public class DataManager {
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
return mShortcutServiceInternal.getShortcuts(
- mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME,
+ UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
/*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID);
}
@@ -775,7 +771,7 @@ public class DataManager {
@Override
public void onReceive(Context context, Intent intent) {
- forAllPackages(PackageData::saveToDisk);
+ forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
}
}
@@ -809,9 +805,5 @@ public class DataManager {
Function<String, PackageData> packageDataGetter) {
return new UsageStatsQueryHelper(userId, packageDataGetter);
}
-
- int getCallingUserId() {
- return Binder.getCallingUserHandle().getIdentifier();
- }
}
}
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 44f3e35833d9..19cf8af5d66b 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -18,6 +18,7 @@ package com.android.server.people.prediction;
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
@@ -42,25 +43,28 @@ public class AppTargetPredictor {
/** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
return new ShareTargetPredictor(
- predictionContext, updatePredictionsMethod, dataManager);
+ predictionContext, updatePredictionsMethod, dataManager, callingUserId);
}
- return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
+ return new AppTargetPredictor(
+ predictionContext, updatePredictionsMethod, dataManager, callingUserId);
}
private final AppPredictionContext mPredictionContext;
private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
private final DataManager mDataManager;
+ final int mCallingUserId;
private final ExecutorService mCallbackExecutor;
AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
mPredictionContext = predictionContext;
mUpdatePredictionsMethod = updatePredictionsMethod;
mDataManager = dataManager;
+ mCallingUserId = callingUserId;
mCallbackExecutor = Executors.newSingleThreadExecutor();
}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 280ced3a07c5..90d821641149 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -19,6 +19,7 @@ package com.android.server.people.prediction;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
@@ -45,8 +46,8 @@ class ShareTargetPredictor extends AppTargetPredictor {
ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
- super(predictionContext, updatePredictionsMethod, dataManager);
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+ super(predictionContext, updatePredictionsMethod, dataManager, callingUserId);
mIntentFilter = predictionContext.getExtras().getParcelable(
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
}
@@ -84,7 +85,7 @@ class ShareTargetPredictor extends AppTargetPredictor {
List<ShareTarget> getShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
- getDataManager().getShareShortcuts(mIntentFilter);
+ getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 2a78b6f6ca24..2c84f2603b5c 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -52,13 +53,14 @@ import org.mockito.MockitoAnnotations;
public class CachedDeviceStateServiceTest {
@Mock private BatteryManagerInternal mBatteryManager;
@Mock private IPowerManager mPowerManager;
+ @Mock private IThermalService mThermalService;
private BroadcastInterceptingContext mContext;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
- PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+ PowerManager powerManager = new PowerManager(context, mPowerManager, mThermalService, null);
mContext = new BroadcastInterceptingContext(context) {
@Override
public Object getSystemService(String name) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 69ca643f1abf..ae8d5545e069 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -74,6 +74,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallback;
@@ -147,6 +148,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private Context mMockContext;
@Mock private IPowerManager mMockIPowerManager;
+ @Mock private IThermalService mMockIThermalService;
@Mock private PackageManager mMockPackageManager;
@Spy private AccessibilityServiceInfo mSpyServiceInfo = new AccessibilityServiceInfo();
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@@ -174,7 +176,7 @@ public class AbstractAccessibilityServiceConnectionTest {
.thenReturn(mMockMagnificationController);
PowerManager powerManager =
- new PowerManager(mMockContext, mMockIPowerManager, mHandler);
+ new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 41235560dc91..85b8fcbbcc61 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -34,6 +34,7 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.content.Context;
import android.os.Handler;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -73,6 +74,7 @@ public class KeyEventDispatcherTest {
private KeyEventFilter mKeyEventFilter1;
private KeyEventFilter mKeyEventFilter2;
private IPowerManager mMockPowerManagerService;
+ private IThermalService mMockThermalService;
private MessageCapturingHandler mMessageCapturingHandler;
private ArgumentCaptor<Integer> mFilter1SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
private ArgumentCaptor<Integer> mFilter2SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -82,10 +84,12 @@ public class KeyEventDispatcherTest {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mInputEventsHandler = new MessageCapturingHandler(looper, null);
mMockPowerManagerService = mock(IPowerManager.class);
+ mMockThermalService = mock(IThermalService.class);
// TODO: It would be better to mock PowerManager rather than its binder, but the class is
// final.
PowerManager powerManager =
- new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler(looper));
+ new PowerManager(mock(Context.class), mMockPowerManagerService, mMockThermalService,
+ new Handler(looper));
mMessageCapturingHandler = new MessageCapturingHandler(looper, null);
mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHandler, SEND_FRAMEWORK_KEY_EVENT,
mLock, powerManager, mMessageCapturingHandler);
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index e90cb4641752..ac0cac14be7b 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -37,6 +37,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.DeviceConfig;
@@ -74,6 +75,8 @@ public class AttentionManagerServiceTest {
@Mock
private IPowerManager mMockIPowerManager;
@Mock
+ private IThermalService mMockIThermalService;
+ @Mock
Context mContext;
@Before
@@ -84,7 +87,7 @@ public class AttentionManagerServiceTest {
// setup power manager mock
PowerManager mPowerManager;
doReturn(true).when(mMockIPowerManager).isInteractive();
- mPowerManager = new PowerManager(mContext, mMockIPowerManager, null);
+ mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
Object mLock = new Object();
// setup a spy on attention manager
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 9574a086c74b..40b0e7114cc9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -31,8 +31,11 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import androidx.test.filters.SmallTest;
+
import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -43,7 +46,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-// TODO (b/143516163): Fix old test cases and put into presubmit.
+// TODO (b/149818286): Fix old test cases and put the whole test into presubmit.
public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
private static final String USER_TYPE_EMPTY = "";
@@ -343,6 +346,8 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING));
}
+ @Presubmit
+ @SmallTest
public void testCompMigrationUnAffiliated_skipped() throws Exception {
prepareAdmin1AsDo();
prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID);
@@ -354,6 +359,8 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
assertTrue(dpms.mOwners.hasDeviceOwner());
}
+ @Presubmit
+ @SmallTest
public void testCompMigrationAffiliated() throws Exception {
prepareAdmin1AsDo();
prepareAdmin1AsPo(COPE_PROFILE_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 3a8258be5f01..853151f8a0de 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -369,6 +369,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ PendingIntent pendingIntentGetBroadcast(Context context, int requestCode,
+ Intent intent, int flags) {
+ return null;
+ }
+
+ @Override
void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, int userHandle) {
mContentObservers.put(new Pair<Uri, Integer>(uri, userHandle), observer);
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 37ce5104991c..dbf2f14146fb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -26,6 +26,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPassword;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -2097,7 +2098,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
- MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+ eq(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
}
public void testSetFactoryResetProtectionPolicyFailWithPO() throws Exception {
@@ -2144,7 +2146,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
- MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+ eq(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
}
public void testGetFactoryResetProtectionPolicyWithFrpManagementAgent()
@@ -2171,7 +2174,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
- MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+ eq(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
}
private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
@@ -6045,6 +6049,86 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
}
+ public void testCanProfileOwnerResetPasswordWhenLocked_nonDirectBootAwarePo()
+ throws Exception {
+ setDeviceEncryptionPerUser();
+ setupProfileOwner();
+ setupPasswordResetToken();
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertFalse("po is not direct boot aware",
+ dpm.canProfileOwnerResetPasswordWhenLocked(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testCanProfileOwnerResetPasswordWhenLocked_noActiveToken() throws Exception {
+ setDeviceEncryptionPerUser();
+ setupProfileOwner();
+ makeAdmin1DirectBootAware();
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertFalse("po doesn't have an active password reset token",
+ dpm.canProfileOwnerResetPasswordWhenLocked(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testCanProfileOwnerResetPasswordWhenLocked_nonFbeDevice() throws Exception {
+ setupProfileOwner();
+ makeAdmin1DirectBootAware();
+ setupPasswordResetToken();
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertFalse("device is not FBE",
+ dpm.canProfileOwnerResetPasswordWhenLocked(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception {
+ setDeviceEncryptionPerUser();
+ setupProfileOwner();
+ makeAdmin1DirectBootAware();
+ setupPasswordResetToken();
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue("direct boot aware po with active password reset token",
+ dpm.canProfileOwnerResetPasswordWhenLocked(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ private void setupPasswordResetToken() {
+ final byte[] token = new byte[32];
+ final long handle = 123456;
+
+ when(getServices().lockPatternUtils
+ .addEscrowToken(eq(token), eq(DpmMockContext.CALLER_USER_HANDLE),
+ nullable(EscrowTokenStateChangeCallback.class)))
+ .thenReturn(handle);
+
+ dpm.setResetPasswordToken(admin1, token);
+
+ when(getServices().lockPatternUtils
+ .isEscrowTokenActive(eq(handle), eq(DpmMockContext.CALLER_USER_HANDLE)))
+ .thenReturn(true);
+
+ assertTrue("failed to activate token", dpm.isResetPasswordTokenActive(admin1));
+ }
+
+ private void makeAdmin1DirectBootAware()
+ throws PackageManager.NameNotFoundException, android.os.RemoteException {
+ Mockito.reset(getServices().ipackageManager);
+
+ final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getApplicationInfo(
+ admin1.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
+ ai.privateFlags = PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+
+ doReturn(ai).when(getServices().ipackageManager).getApplicationInfo(
+ eq(admin1.getPackageName()),
+ anyInt(),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ private void setDeviceEncryptionPerUser() {
+ when(getServices().storageManager.isFileBasedEncryptionEnabled()).thenReturn(true);
+ }
+
private void setCrossProfileAppsList(String... packages) {
when(mContext.getResources()
.getStringArray(eq(R.array.cross_profile_apps)))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 9e98427db709..fa19814f401f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -34,6 +34,7 @@ import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -123,6 +124,7 @@ public class HdmiControlServiceTest {
private HdmiPortInfo[] mHdmiPortInfo;
@Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -130,7 +132,8 @@ public class HdmiControlServiceTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null);
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null);
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 3ecd3193058a..5e104a5210d7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -54,7 +54,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.pm.parsing.AndroidPackage;
@@ -87,6 +86,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -110,7 +110,6 @@ public final class DataManagerTest {
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock private PackageManagerInternal mPackageManagerInternal;
- @Mock private ShortcutManager mShortcutManager;
@Mock private UserManager mUserManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private TelecomManager mTelecomManager;
@@ -123,7 +122,6 @@ public final class DataManagerTest {
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
- private int mCallingUserId;
private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@@ -145,14 +143,11 @@ public final class DataManagerTest {
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getPackageName()).thenReturn("android");
Context originalContext = getInstrumentation().getTargetContext();
when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
- when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
- when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
- Context.SHORTCUT_SERVICE);
-
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
Context.USER_SERVICE);
@@ -191,8 +186,6 @@ public final class DataManagerTest {
NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationChannel.setConversationId("test", TEST_SHORTCUT_ID);
- mCallingUserId = USER_ID_PRIMARY;
-
mCancellationSignal = new CancellationSignal();
mInjector = new TestInjector();
@@ -222,9 +215,7 @@ public final class DataManagerTest {
mDataManager.onShortcutAddedOrUpdated(
buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
assertEquals(2, conversations.size());
@@ -250,18 +241,14 @@ public final class DataManagerTest {
mDataManager.onShortcutAddedOrUpdated(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
assertEquals(1, conversations.size());
assertEquals("sc_1", conversations.get(0).getShortcutId());
mDataManager.onUserStopped(USER_ID_PRIMARY);
- conversations.clear();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ conversations = getConversationsInPrimary();
assertTrue(conversations.isEmpty());
}
@@ -289,12 +276,8 @@ public final class DataManagerTest {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
- List<Range<Long>> activeShareTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeShareTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_SHARE_IMAGE)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
assertEquals(1, activeShareTimeSlots.size());
}
@@ -315,9 +298,7 @@ public final class DataManagerTest {
USER_ID_PRIMARY);
contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
@@ -338,12 +319,8 @@ public final class DataManagerTest {
listenerService.onNotificationPosted(mStatusBarNotification);
- List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeNotificationOpenTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_NOTIFICATION_POSTED)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+ Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@@ -361,12 +338,8 @@ public final class DataManagerTest {
listenerService.onNotificationRemoved(mStatusBarNotification, null,
NotificationListenerService.REASON_CLICK);
- List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeNotificationOpenTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_NOTIFICATION_OPENED)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+ Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@@ -469,12 +442,7 @@ public final class DataManagerTest {
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_CALL_MISSED));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(3, activeTimeSlots.size());
}
@@ -498,12 +466,7 @@ public final class DataManagerTest {
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, outgoingSmsEvent);
mInjector.mSmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, incomingSmsEvent);
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(2, activeTimeSlots.size());
}
@@ -551,22 +514,12 @@ public final class DataManagerTest {
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
- activeTimeSlots.clear();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@@ -583,22 +536,12 @@ public final class DataManagerTest {
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
- activeTimeSlots.clear();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@@ -607,6 +550,24 @@ public final class DataManagerTest {
LocalServices.addService(clazz, mock);
}
+ private List<ConversationInfo> getConversationsInPrimary() {
+ List<ConversationInfo> conversations = new ArrayList<>();
+ mDataManager.forPackagesInProfile(USER_ID_PRIMARY,
+ packageData -> packageData.forAllConversations(conversations::add));
+ return conversations;
+ }
+
+ private List<Range<Long>> getActiveSlotsForTestShortcut(
+ Set<Integer> eventTypes) {
+ List<Range<Long>> activeSlots = new ArrayList<>();
+ mDataManager.forPackagesInProfile(USER_ID_PRIMARY, packageData ->
+ activeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(eventTypes)
+ .getActiveTimeSlots()));
+ return activeSlots;
+ }
+
private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
@Nullable Person person) {
Context mockContext = mock(Context.class);
@@ -778,10 +739,5 @@ public final class DataManagerTest {
mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
return mSmsQueryHelper;
}
-
- @Override
- int getCallingUserId() {
- return mCallingUserId;
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 808906e3a06a..f498a9450c9e 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@ public final class ShareTargetPredictorTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
+ when(mDataManager.getShareShortcuts(any(), anyInt())).thenReturn(mShareShortcuts);
when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
@@ -82,7 +83,8 @@ public final class ShareTargetPredictorTest {
.setPredictedTargetCount(NUM_PREDICTED_TARGETS)
.setExtras(new Bundle())
.build();
- mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
+ mPredictor = new ShareTargetPredictor(
+ predictionContext, targets -> { }, mDataManager, USER_ID);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index df2b3efeb94c..6c769485f6ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -562,6 +562,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
return true;
}
+
+ @Override
+ boolean injectHasInteractAcrossUsersFullPermission(int callingPid, int callingUid) {
+ return false;
+ }
}
protected class LauncherAppsTestable extends LauncherApps {
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index ccf7ca9d3cf0..624cb83f9e19 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -28,10 +28,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
import android.os.CoolingDevice;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -72,6 +74,8 @@ public class ThermalManagerServiceTest {
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
+ private IThermalService mIThermalServiceMock;
+ @Mock
private IThermalEventListener mEventListener1;
@Mock
private IThermalEventListener mEventListener2;
@@ -133,6 +137,12 @@ public class ThermalManagerServiceTest {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
return true;
}
@@ -153,7 +163,7 @@ public class ThermalManagerServiceTest {
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mFakeHal = new ThermalHalFake();
- mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+ mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, null);
when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
resetListenerMock();
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index d5cdbeb121b0..035a2f11112c 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -36,6 +36,7 @@ import android.content.IntentSender;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
@@ -62,6 +63,7 @@ public class RecoverySystemServiceTest {
private RecoverySystemService.UncryptSocket mUncryptSocket;
private Context mContext;
private IPowerManager mIPowerManager;
+ private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
@@ -77,8 +79,9 @@ public class RecoverySystemServiceTest {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mIPowerManager = mock(IPowerManager.class);
+ mIThermalService = mock(IThermalService.class);
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
- new Handler(looper));
+ mIThermalService, new Handler(looper));
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 604fcd38fc44..ccce0436ea59 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -359,8 +359,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
+ // Shell permisssions will override permissions of our app, so add all necessary permissions
+ // fo this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- "android.permission.WRITE_DEVICE_CONFIG", "android.permission.READ_DEVICE_CONFIG");
+ "android.permission.WRITE_DEVICE_CONFIG",
+ "android.permission.READ_DEVICE_CONFIG",
+ "android.permission.READ_CONTACTS");
MockitoAnnotations.initMocks(this);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0adf15c8b637..7f9732bb350a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
private static final int UID_N_MR1 = 0;
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
+ private static final int UID_P = 2222;
private static final String SYSTEM_PKG = "android";
private static final int SYSTEM_UID = 1000;
private static final UserHandle USER2 = UserHandle.of(10);
@@ -141,9 +142,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_P), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+ when(mPm.getPackageUidAsUser(eq(PKG_P), anyInt())).thenReturn(UID_P);
when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
PackageInfo info = mock(PackageInfo.class);
info.signatures = new Signature[] {mock(Signature.class)};
@@ -2945,6 +2948,92 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testGetConversations_all() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ channel2.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+
+ assertEquals(3, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertTrue(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ @Test
+ public void testGetConversations_onlyImportant() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ channel.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ diffConvo.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+
+ assertEquals(2, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertFalse(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
+ NotificationChannel expected) {
+ for (ConversationChannelWrapper ccw : list) {
+ if (ccw.getNotificationChannel().equals(expected)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Test
public void testGetConversations_invalidPkg() {
assertThat(mHelper.getConversations("bad", 1)).isEmpty();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c110a0c4d92c..b917e1b92fb9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -29,6 +29,7 @@ import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -64,8 +65,10 @@ import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -80,6 +83,9 @@ import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -999,7 +1005,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
// Move activity to split-screen-primary stack and make sure it has the focus.
- top.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
+ splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
// Activity must landed on split-screen-secondary when launch adjacent.
@@ -1022,4 +1029,58 @@ public class ActivityStarterTests extends ActivityTestsBase {
verify(recentTasks, times(1)).add(any());
}
+
+ static class TestSplitOrganizer extends ITaskOrganizer.Stub {
+ final ActivityTaskManagerService mService;
+ TaskTile mPrimary;
+ TaskTile mSecondary;
+ boolean mInSplit = false;
+ int mDisplayId;
+ TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
+ mService = service;
+ mDisplayId = displayId;
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
+ mPrimary = TaskTile.forToken(primary.asBinder());
+ IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
+ mSecondary = TaskTile.forToken(secondary.asBinder());
+ }
+ @Override
+ public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ }
+ @Override
+ public void taskVanished(IWindowContainer wc) {
+ }
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ if (mInSplit) {
+ return;
+ }
+ if (info.topActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ if (info.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ mInSplit = true;
+ mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
+ mSecondary.mRemoteToken);
+ // move everything to secondary because test expects this but usually sysui
+ // does it.
+ DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(
+ dc.getStackAt(i).getWindowingMode())) {
+ mSecondary.addChild(dc.getStackAt(i), 0);
+ }
+ }
+ }
+ }
+ }
+ };
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index a672a95dcc12..444906977f47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1098,7 +1098,6 @@ public class RecentTasksTest extends ActivityTestsBase {
assertSecurityException(expectCallable,
() -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
() -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7753a32d8ca0..6a8917cda9fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -20,7 +20,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
@@ -53,6 +55,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
@@ -65,12 +69,16 @@ import androidx.test.filters.SmallTest;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.google.common.truth.Truth;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
/**
* Build/Install/Run:
* atest WmTests:RecentsAnimationControllerTest
@@ -330,6 +338,107 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
}
+ @Test
+ public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+ mWm.mIsFixedRotationTransformEnabled = true;
+ mWm.setRecentsAnimationController(mController);
+
+ final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final ActivityRecord homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ final ActivityRecord appWindow = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win0);
+
+ final ActivityRecord landActivity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ landActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, landActivity, "win1");
+ landActivity.addWindow(win1);
+
+ assertEquals(landActivity.getTask().getTopVisibleActivity(), landActivity);
+ assertEquals(landActivity.findMainWindow(), win1);
+
+ // Ensure that the display is in Landscape
+ landActivity.onDescendantOrientationChanged(landActivity.token, landActivity);
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE,
+ mDisplayContent.getConfiguration().orientation);
+
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeAppWindow);
+
+ // Check that the home app is in portrait
+ assertEquals(Configuration.ORIENTATION_PORTRAIT,
+ homeAppWindow.getConfiguration().orientation);
+ }
+
+ @Test
+ public void testWallpaperHasFixedRotationApplied() {
+ mWm.mIsFixedRotationTransformEnabled = true;
+ mWm.setRecentsAnimationController(mController);
+
+ // Create a portrait home stack, a wallpaper and a landscape application displayed on top.
+
+ // Home stack
+ final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final ActivityRecord homeActivity =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ homeActivity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ final WindowState homeWindow = createWindow(null, TYPE_BASE_APPLICATION, homeActivity,
+ "homeWindow");
+ homeActivity.addWindow(homeWindow);
+ homeWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+
+ // Landscape application
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState applicationWindow = createWindow(null, TYPE_BASE_APPLICATION, activity,
+ "applicationWindow");
+ activity.addWindow(applicationWindow);
+ activity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Wallpaper
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+
+ // Make sure the landscape activity is on top and the display is in landscape
+ activity.moveFocusableActivityToTop("test");
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+
+
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ // Start the recents animation
+ mController
+ .initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
+
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+
+ // Check preconditions
+ ArrayList<WallpaperWindowToken> wallpapers = new ArrayList<>(1);
+ mDisplayContent.forAllWallpaperWindows(wallpapers::add);
+
+ Truth.assertThat(wallpapers).hasSize(1);
+ Truth.assertThat(wallpapers.get(0).getTopChild()).isEqualTo(wallpaperWindow);
+
+ // Actual check
+ assertEquals(Configuration.ORIENTATION_PORTRAIT,
+ wallpapers.get(0).getConfiguration().orientation);
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b5663bd7e26b..851b0523b058 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -260,4 +260,9 @@ public class StubTransaction extends SurfaceControl.Transaction {
public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
+ return this;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 0312df6d34fc..cd53eced948f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,19 +28,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -49,6 +47,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.PictureInPictureParams;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -56,7 +55,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
-import android.content.pm.ActivityInfo;
import android.util.Rational;
import android.view.Display;
import android.view.ITaskOrganizer;
@@ -458,9 +456,9 @@ public class TaskOrganizerTests extends WindowTestsBase {
private List<TaskTile> getTaskTiles(DisplayContent dc) {
ArrayList<TaskTile> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final Task t = dc.getStackAt(i);
- if (t instanceof TaskTile) {
- out.add((TaskTile) t);
+ final TaskTile t = dc.getStackAt(i).asTile();
+ if (t != null) {
+ out.add(t);
}
}
return out;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 795de57e834f..9924839c2d93 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.Manifest;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,7 +27,6 @@ import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
-import android.net.ipsec.ike.SaProposal;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.service.carrier.CarrierService;
@@ -3499,369 +3497,6 @@ public class CarrierConfigManager {
public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
"show_forwarded_number_bool";
- /**
- * Configs used for epdg tunnel bring up.
- *
- * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange
- * Protocol Version 2 (IKEv2)</a>
- */
- public static final class Iwlan {
- /** Prefix of all Epdg.KEY_* constants. */
- public static final String KEY_PREFIX = "iwlan.";
-
- /**
- * Time in seconds after which the child security association session is terminated if
- * rekey procedure is not successful. If not set or set to <= 0, the default value is
- * 3600 seconds.
- */
- public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT =
- KEY_PREFIX + "child_sa_rekey_hard_timer_sec_int";
-
- /**
- * Time in seconds after which the child session rekey procedure is started. If not set or
- * set to <= 0, default value is 3000 seconds.
- */
- public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT =
- KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int";
-
- /** Supported DH groups for IKE negotiation.
- * Possible values are {@link #DH_GROUP_NONE}, {@link #DH_GROUP_1024_BIT_MODP},
- * {@link #DH_GROUP_2048_BIT_MODP}
- */
- public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
- KEY_PREFIX + "diffie_hellman_groups_int_array";
-
- /**
- * Time in seconds after which a dead peer detection (DPD) request is sent.
- * If not set or set to <= 0, default value is 120 seconds.
- */
- public static final String KEY_DPD_TIMER_SEC_INT = KEY_PREFIX + "dpd_timer_sec_int";
-
- /**
- * Method used to authenticate epdg server.
- * Possible values are {@link #AUTHENTICATION_METHOD_EAP_ONLY},
- * {@link #AUTHENTICATION_METHOD_CERT}
- */
- public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT =
- KEY_PREFIX + "epdg_authentication_method_int";
-
- /**
- * A priority list of ePDG addresses to be used.
- * Possible values are {@link #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN},
- * {@link #EPDG_ADDRESS_PCO}
- */
- public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
- KEY_PREFIX + "epdg_address_priority_int_array";
-
- /** Epdg static IP address or FQDN */
- public static final String KEY_EPDG_STATIC_ADDRESS_STRING =
- KEY_PREFIX + "epdg_static_address_string";
-
- /** Epdg static IP address or FQDN for roaming */
- public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING =
- KEY_PREFIX + "epdg_static_address_roaming_string";
-
- /**
- * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child
- * session.
- * Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
- * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
- */
- public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
- KEY_PREFIX + "child_session_aes_cbc_key_size_int_array";
-
- /**
- * List of supported key sizes for AES counter (CTR) encryption mode of child session.
- * Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
- * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
- */
- public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
- KEY_PREFIX + "child_encryption_aes_ctr_key_size_int_array";
-
- /**
- * List of supported encryption algorithms for child session.
- * Possible values are {@link #ENCRYPTION_ALGORITHM_3DES},
- * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_8},
- * {@link #ENCRYPTION_ALGORITHM_AES_GCM_12}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_16}
- */
- public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
- KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
-
- /** Controls if IKE message fragmentation is enabled. */
- public static final String KEY_IKE_FRAGMENTATION_ENABLED_BOOL =
- KEY_PREFIX + "ike_fragmentation_enabled_bool";
-
- /**
- * Time in seconds after which the IKE session is terminated if rekey procedure is not
- * successful. If not set or set to <= 0, default value is 3600 seconds.
- */
- public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT =
- KEY_PREFIX + "ike_rekey_hard_timer_in_sec";
-
- /**
- * Time in seconds after which the IKE session rekey procedure is started. If not set or
- * set to <= 0, default value is 3000 seconds.
- */
- public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT =
- KEY_PREFIX + "ike_rekey_soft_timer_sec_int";
-
- /**
- * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE
- * session.
- * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
- * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
- */
- public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
- KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
-
- /**
- * List of supported key sizes for AES counter (CTR) encryption mode of IKE session.
- * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
- * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
- */
- public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
- KEY_PREFIX + "ike_session_aes_ctr_key_size_int_array";
-
- /**
- * List of supported encryption algorithms for IKE session.
- * Possible values are {@link #ENCRYPTION_ALGORITHM_3DES},
- * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_8},
- * {@link #ENCRYPTION_ALGORITHM_AES_GCM_12}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_16}
- */
- public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
- KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
-
- /**
- * List of supported integrity algorithms for IKE session
- * Possible values are {@link #INTEGRITY_ALGORITHM_NONE},
- * {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link #INTEGRITY_ALGORITHM_AES_XCBC_96},
- * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128},
- * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192},
- * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
- */
- public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY =
- KEY_PREFIX + "supported_integrity_algorithms_int_array";
-
- /** Maximum number of retries for tunnel establishment. */
- public static final String KEY_MAX_RETRIES_INT = KEY_PREFIX + "max_retries_int";
-
- /** Controls if nat traversal should be enabled. */
- public static final String KEY_NATT_ENABLED_BOOL = KEY_PREFIX + "natt_enabled_bool";
-
- /**
- * Time in seconds after which a NATT keep alive message is sent. If not set or set to <= 0,
- * default value is 20 seconds.
- */
- public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT =
- KEY_PREFIX + "natt_keep_alive_timer_sec_int";
-
- /** List of comma separated MCC/MNCs used to create ePDG FQDN as per 3GPP TS 23.003 */
- public static final String KEY_MCC_MNCS_STRING_ARRAY = KEY_PREFIX + "mcc_mncs_string_array";
-
- /**
- * List of supported pseudo random function algorithms for IKE session
- * Possible values are {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1},
- * {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC}
- */
- public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = KEY_PREFIX +
- "supported_prf_algorithms_int_array";
-
- /**
- * Time in seconds after which IKE message is retransmitted. If not set or set to <= 0,
- * default value is 2 seconds.
- */
- public static final String KEY_RETRANSMIT_TIMER_SEC_INT =
- KEY_PREFIX + "retransmit_timer_sec_int";
-
- /** @hide */
- @IntDef({
- AUTHENTICATION_METHOD_EAP_ONLY,
- AUTHENTICATION_METHOD_CERT
- })
- public @interface AuthenticationMethodType {}
-
- /**
- * Certificate sent from the server is ignored. Only Extensible Authentication Protocol
- * (EAP) is used to authenticate the server.
- * EAP_ONLY_AUTH payload is added to IKE_AUTH request if supported.
- * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998</a>
- */
- public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0;
- /** Server is authenticated using its certificate. */
- public static final int AUTHENTICATION_METHOD_CERT = 1;
-
- /** @hide */
- @IntDef({
- EPDG_ADDRESS_STATIC,
- EPDG_ADDRESS_PLMN,
- EPDG_ADDRESS_PCO
- })
- public @interface EpdgAddressType {}
-
- /** Use static epdg address. */
- public static final int EPDG_ADDRESS_STATIC = 0;
- /** Construct the epdg address using plmn. */
- public static final int EPDG_ADDRESS_PLMN = 1;
- /**
- * Use the epdg address received in protocol configuration options (PCO) from the
- * network.
- */
- public static final int EPDG_ADDRESS_PCO = 2;
-
- /** @hide */
- @IntDef({
- KEY_LEN_UNUSED,
- KEY_LEN_AES_128,
- KEY_LEN_AES_192,
- KEY_LEN_AES_256
- })
- public @interface EncrpytionKeyLengthType {}
-
- public static final int KEY_LEN_UNUSED = SaProposal.KEY_LEN_UNUSED;
- /** AES Encryption/Ciphering Algorithm key length 128 bits. */
- public static final int KEY_LEN_AES_128 = SaProposal.KEY_LEN_AES_128;
- /** AES Encryption/Ciphering Algorithm key length 192 bits. */
- public static final int KEY_LEN_AES_192 = SaProposal.KEY_LEN_AES_192;
- /** AES Encryption/Ciphering Algorithm key length 256 bits. */
- public static final int KEY_LEN_AES_256 = SaProposal.KEY_LEN_AES_256;
-
- /** @hide */
- @IntDef({
- DH_GROUP_NONE,
- DH_GROUP_1024_BIT_MODP,
- DH_GROUP_2048_BIT_MODP
- })
- public @interface DhGroup {}
-
- /** None Diffie-Hellman Group. */
- public static final int DH_GROUP_NONE = SaProposal.DH_GROUP_NONE;
- /** 1024-bit MODP Diffie-Hellman Group. */
- public static final int DH_GROUP_1024_BIT_MODP = SaProposal.DH_GROUP_1024_BIT_MODP;
- /** 2048-bit MODP Diffie-Hellman Group. */
- public static final int DH_GROUP_2048_BIT_MODP = SaProposal.DH_GROUP_2048_BIT_MODP;
-
- /** @hide */
- @IntDef({
- ENCRYPTION_ALGORITHM_3DES,
- ENCRYPTION_ALGORITHM_AES_CBC,
- ENCRYPTION_ALGORITHM_AES_GCM_8,
- ENCRYPTION_ALGORITHM_AES_GCM_12,
- ENCRYPTION_ALGORITHM_AES_GCM_16
- })
- public @interface EncryptionAlgorithm {}
-
- /** 3DES Encryption/Ciphering Algorithm. */
- public static final int ENCRYPTION_ALGORITHM_3DES = SaProposal.ENCRYPTION_ALGORITHM_3DES;
- /** AES-CBC Encryption/Ciphering Algorithm. */
- public static final int ENCRYPTION_ALGORITHM_AES_CBC =
- SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
-
- /**
- * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 8-octet ICV
- * (truncation).
- */
- public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 =
- SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
- /**
- * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 12-octet ICV
- * (truncation).
- */
- public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 =
- SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
- /**
- * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV
- * (truncation).
- */
- public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 =
- SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
-
- /** @hide */
- @IntDef({
- INTEGRITY_ALGORITHM_NONE,
- INTEGRITY_ALGORITHM_HMAC_SHA1_96,
- INTEGRITY_ALGORITHM_AES_XCBC_96,
- INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
- INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
- INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
- })
- public @interface IntegrityAlgorithm {}
-
- /** None Authentication/Integrity Algorithm. */
- public static final int INTEGRITY_ALGORITHM_NONE = SaProposal.INTEGRITY_ALGORITHM_NONE;
- /** HMAC-SHA1 Authentication/Integrity Algorithm. */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 =
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
- /** AES-XCBC-96 Authentication/Integrity Algorithm. */
- public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 =
- SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
- /** HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 =
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
- /** HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 =
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
- /** HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. */
- public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 =
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
-
- /** @hide */
- @IntDef({
- PSEUDORANDOM_FUNCTION_HMAC_SHA1,
- PSEUDORANDOM_FUNCTION_AES128_XCBC
- })
- public @interface PseudorandomFunction {}
-
- /** HMAC-SHA1 Pseudorandom Function. */
- public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 =
- SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
- /** AES128-XCBC Pseudorandom Function. */
- public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC =
- SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
-
- private Iwlan() {}
-
- private static PersistableBundle getDefaults() {
- PersistableBundle defaults = new PersistableBundle();
- defaults.putInt(KEY_IKE_REKEY_SOFT_TIMER_SEC_INT, 3000);
- defaults.putInt(KEY_IKE_REKEY_HARD_TIMER_SEC_INT, 3600);
- defaults.putInt(KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT, 3000);
- defaults.putInt(KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT, 3600);
- defaults.putInt(KEY_RETRANSMIT_TIMER_SEC_INT, 2);
- defaults.putInt(KEY_DPD_TIMER_SEC_INT, 120);
- defaults.putInt(KEY_MAX_RETRIES_INT, 3);
- defaults.putIntArray(KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY,
- new int[]{DH_GROUP_1024_BIT_MODP, DH_GROUP_2048_BIT_MODP});
- defaults.putIntArray(KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
- new int[]{ENCRYPTION_ALGORITHM_3DES, ENCRYPTION_ALGORITHM_AES_CBC});
- defaults.putIntArray(KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
- new int[]{ENCRYPTION_ALGORITHM_3DES, ENCRYPTION_ALGORITHM_AES_CBC});
- defaults.putIntArray(KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
- new int[]{INTEGRITY_ALGORITHM_AES_XCBC_96, INTEGRITY_ALGORITHM_HMAC_SHA1_96,
- INTEGRITY_ALGORITHM_HMAC_SHA2_256_128});
- defaults.putIntArray(KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY,
- new int[]{PSEUDORANDOM_FUNCTION_HMAC_SHA1, PSEUDORANDOM_FUNCTION_AES128_XCBC});
- defaults.putBoolean(KEY_NATT_ENABLED_BOOL, true);
- defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_CERT);
- defaults.putString(KEY_EPDG_STATIC_ADDRESS_STRING, "");
- defaults.putString(KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING, "");
- defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20);
- defaults.putIntArray(KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
- new int[]{KEY_LEN_AES_128, KEY_LEN_AES_256});
- defaults.putIntArray(KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
- new int[]{KEY_LEN_AES_128});
- defaults.putIntArray(KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
- new int[]{KEY_LEN_AES_128, KEY_LEN_AES_256});
- defaults.putIntArray(KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
- new int[]{KEY_LEN_AES_128});
- defaults.putBoolean(KEY_IKE_FRAGMENTATION_ENABLED_BOOL, false);
- defaults.putIntArray(KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[]{EPDG_ADDRESS_PLMN,
- EPDG_ADDRESS_STATIC});
- defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[]{});
-
- return defaults;
- }
- }
-
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4359,7 +3994,6 @@ public class CarrierConfigManager {
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
- sDefaults.putAll(Iwlan.getDefaults());
}
/**
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 08869751b93e..220cdce0d178 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2054,14 +2054,15 @@ public class ConnectivityServiceTest {
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- // Bring up wifi with a score of 70.
+ // Bring up validated wifi.
// Cell is lingered because it would not satisfy any request, even if it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.adjustScore(50);
- mWiFiNetworkAgent.connect(false); // Score: 70
+ mWiFiNetworkAgent.connect(true); // Score: 60
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5845,7 +5846,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ trustedCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
reset(mNetworkManagementService);
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
index a6b371a23b58..d2532c2ce3d3 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -16,14 +16,18 @@
package com.android.server.connectivity
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.LinkProperties
+import android.net.Network
+import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
+import android.net.NetworkInfo
import android.net.NetworkRequest
+import android.net.NetworkScore
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import kotlin.test.assertEquals
import kotlin.test.assertNull
@@ -33,10 +37,24 @@ import kotlin.test.assertNull
class NetworkRankerTest {
private val ranker = NetworkRanker()
- private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
- doReturn(satisfy).`when`(it).satisfies(any())
- doReturn(score).`when`(it).currentScore
- it.networkCapabilities = NetworkCapabilities()
+ private fun makeNai(satisfy: Boolean, score: Int) = object : NetworkAgentInfo(
+ null /* messenger */,
+ null /* asyncChannel*/,
+ Network(100),
+ NetworkInfo(TYPE_WIFI, 0 /* subtype */, "" /* typename */, "" /* subtypename */),
+ LinkProperties(),
+ NetworkCapabilities(),
+ NetworkScore.Builder().setLegacyScore(score).build(),
+ null /* context */,
+ null /* handler */,
+ NetworkAgentConfig(),
+ null /* connectivityService */,
+ null /* netd */,
+ null /* dnsResolver */,
+ null /* networkManagementService */,
+ 0 /* factorySerialNumber */) {
+ override fun satisfies(request: NetworkRequest?): Boolean = satisfy
+ override fun getCurrentScore(): Int = score
}
@Test
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c330e263d7e..3025caf74000 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -125,6 +125,8 @@ interface IWifiManager
DhcpInfo getDhcpInfo();
+ void setScanAlwaysAvailable(boolean isAvailable);
+
boolean isScanAlwaysAvailable();
boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 0db3313ae137..ae5bf7d5ae84 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -202,6 +203,11 @@ public final class SoftApConfiguration implements Parcelable {
private final List<MacAddress> mAllowedClientList;
/**
+ * Whether auto shutdown of soft AP is enabled or not.
+ */
+ private final boolean mAutoShutdownEnabled;
+
+ /**
* Delay in milliseconds before shutting down soft AP when
* there are no connected devices.
*/
@@ -240,9 +246,9 @@ public final class SoftApConfiguration implements Parcelable {
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
- @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis,
- boolean clientControlByUser, @NonNull List<MacAddress> blockedList,
- @NonNull List<MacAddress> allowedList) {
+ @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
+ int shutdownTimeoutMillis, boolean clientControlByUser,
+ @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
mSsid = ssid;
mBssid = bssid;
mPassphrase = passphrase;
@@ -251,6 +257,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
+ mAutoShutdownEnabled = shutdownTimeoutEnabled;
mShutdownTimeoutMillis = shutdownTimeoutMillis;
mClientControlByUser = clientControlByUser;
mBlockedClientList = new ArrayList<>(blockedList);
@@ -274,6 +281,7 @@ public final class SoftApConfiguration implements Parcelable {
&& mChannel == other.mChannel
&& mSecurityType == other.mSecurityType
&& mMaxNumberOfClients == other.mMaxNumberOfClients
+ && mAutoShutdownEnabled == other.mAutoShutdownEnabled
&& mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
&& mClientControlByUser == other.mClientControlByUser
&& Objects.equals(mBlockedClientList, other.mBlockedClientList)
@@ -283,8 +291,9 @@ public final class SoftApConfiguration implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
- mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis,
- mClientControlByUser, mBlockedClientList, mAllowedClientList);
+ mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
+ mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
+ mAllowedClientList);
}
@Override
@@ -299,6 +308,7 @@ public final class SoftApConfiguration implements Parcelable {
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
+ sbuf.append(" \n AutoShutdownEnabled=").append(mAutoShutdownEnabled);
sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
@@ -316,6 +326,7 @@ public final class SoftApConfiguration implements Parcelable {
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
+ dest.writeBoolean(mAutoShutdownEnabled);
dest.writeInt(mShutdownTimeoutMillis);
dest.writeBoolean(mClientControlByUser);
dest.writeTypedList(mBlockedClientList);
@@ -335,7 +346,7 @@ public final class SoftApConfiguration implements Parcelable {
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
- in.readInt(), in.readInt(), in.readBoolean(),
+ in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(),
in.createTypedArrayList(MacAddress.CREATOR),
in.createTypedArrayList(MacAddress.CREATOR));
}
@@ -429,6 +440,18 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
+ * Returns whether auto shutdown is enabled or not.
+ * The Soft AP will shutdown when there are no devices associated to it for
+ * the timeout duration. See {@link Builder#setAutoShutdownEnabled(boolean)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isAutoShutdownEnabled() {
+ return mAutoShutdownEnabled;
+ }
+
+ /**
* Returns the shutdown timeout in milliseconds.
* The Soft AP will shutdown when there are no devices associated to it for
* the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
@@ -551,6 +574,7 @@ public final class SoftApConfiguration implements Parcelable {
private int mChannel;
private int mMaxNumberOfClients;
private int mSecurityType;
+ private boolean mAutoShutdownEnabled;
private int mShutdownTimeoutMillis;
private boolean mClientControlByUser;
private List<MacAddress> mBlockedClientList;
@@ -568,6 +592,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mAutoShutdownEnabled = true; // enabled by default.
mShutdownTimeoutMillis = 0;
mClientControlByUser = false;
mBlockedClientList = new ArrayList<>();
@@ -588,6 +613,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
+ mAutoShutdownEnabled = other.mAutoShutdownEnabled;
mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
mClientControlByUser = other.mClientControlByUser;
mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
@@ -603,8 +629,8 @@ public final class SoftApConfiguration implements Parcelable {
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
- mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
- mAllowedClientList);
+ mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
+ mBlockedClientList, mAllowedClientList);
}
/**
@@ -789,7 +815,7 @@ public final class SoftApConfiguration implements Parcelable {
* @return Builder for chaining.
*/
@NonNull
- public Builder setMaxNumberOfClients(int maxNumberOfClients) {
+ public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
if (maxNumberOfClients < 0) {
throw new IllegalArgumentException("maxNumberOfClients should be not negative");
}
@@ -798,6 +824,25 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
+ * Specifies whether auto shutdown is enabled or not.
+ * The Soft AP will shut down when there are no devices connected to it for
+ * the timeout duration.
+ *
+ * <p>
+ * <li>If not set, defaults to true</li>
+ *
+ * @param enable true to enable, false to disable.
+ * @return Builder for chaining.
+ *
+ * @see #setShutdownTimeoutMillis(int)
+ */
+ @NonNull
+ public Builder setAutoShutdownEnabled(boolean enable) {
+ mAutoShutdownEnabled = enable;
+ return this;
+ }
+
+ /**
* Specifies the shutdown timeout in milliseconds.
* The Soft AP will shut down when there are no devices connected to it for
* the timeout duration.
@@ -807,14 +852,16 @@ public final class SoftApConfiguration implements Parcelable {
*
* <p>
* <li>If not set, defaults to 0</li>
- * <li>The shut down timout will apply when
- * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li>
+ * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
+ * set to true</li>
*
* @param timeoutMillis milliseconds of the timeout delay.
* @return Builder for chaining.
+ *
+ * @see #setAutoShutdownEnabled(boolean)
*/
@NonNull
- public Builder setShutdownTimeoutMillis(int timeoutMillis) {
+ public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("Invalid timeout value");
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f693315c6cff..af2f2461ac94 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2755,6 +2755,26 @@ public class WifiManager {
}
/**
+ * Set if scanning is always available.
+ *
+ * If set to {@code true}, apps can issue {@link #startScan} and fetch scan results
+ * even when Wi-Fi is turned off.
+ *
+ * @param isAvailable true to enable, false to disable.
+ * @hide
+ * @see #isScanAlwaysAvailable()
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setScanAlwaysAvailable(boolean isAvailable) {
+ try {
+ mService.setScanAlwaysAvailable(isAvailable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check if scanning is always available.
*
* If this return {@code true}, apps can issue {@link #startScan} and fetch scan results
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 22d778637101..44dbb98a8ab8 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -21,8 +21,10 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.Settings;
import java.util.List;
@@ -33,6 +35,9 @@ import java.util.List;
*/
@SystemApi
public final class WifiOemMigrationHook {
+
+ private WifiOemMigrationHook() { }
+
/**
* Container for all the wifi config data to migrate.
*/
@@ -152,8 +157,6 @@ public final class WifiOemMigrationHook {
}
}
- private WifiOemMigrationHook() { }
-
/**
* Load data from OEM's config store.
* <p>
@@ -178,4 +181,263 @@ public final class WifiOemMigrationHook {
// Note: OEM's should add code to parse data from their config store format here!
return null;
}
+
+ /**
+ * Container for all the wifi settings data to migrate.
+ */
+ public static final class SettingsMigrationData implements Parcelable {
+ private final boolean mScanAlwaysAvailable;
+ private final boolean mP2pFactoryResetPending;
+ private final String mP2pDeviceName;
+ private final boolean mSoftApTimeoutEnabled;
+ private final boolean mWakeupEnabled;
+ private final boolean mScanThrottleEnabled;
+ private final boolean mVerboseLoggingEnabled;
+
+ private SettingsMigrationData(boolean scanAlwaysAvailable, boolean p2pFactoryResetPending,
+ @Nullable String p2pDeviceName, boolean softApTimeoutEnabled, boolean wakeupEnabled,
+ boolean scanThrottleEnabled, boolean verboseLoggingEnabled) {
+ mScanAlwaysAvailable = scanAlwaysAvailable;
+ mP2pFactoryResetPending = p2pFactoryResetPending;
+ mP2pDeviceName = p2pDeviceName;
+ mSoftApTimeoutEnabled = softApTimeoutEnabled;
+ mWakeupEnabled = wakeupEnabled;
+ mScanThrottleEnabled = scanThrottleEnabled;
+ mVerboseLoggingEnabled = verboseLoggingEnabled;
+ }
+
+ public static final @NonNull Parcelable.Creator<SettingsMigrationData> CREATOR =
+ new Parcelable.Creator<SettingsMigrationData>() {
+ @Override
+ public SettingsMigrationData createFromParcel(Parcel in) {
+ boolean scanAlwaysAvailable = in.readBoolean();
+ boolean p2pFactoryResetPending = in.readBoolean();
+ String p2pDeviceName = in.readString();
+ boolean softApTimeoutEnabled = in.readBoolean();
+ boolean wakeupEnabled = in.readBoolean();
+ boolean scanThrottleEnabled = in.readBoolean();
+ boolean verboseLoggingEnabled = in.readBoolean();
+ return new SettingsMigrationData(
+ scanAlwaysAvailable, p2pFactoryResetPending,
+ p2pDeviceName, softApTimeoutEnabled, wakeupEnabled,
+ scanThrottleEnabled, verboseLoggingEnabled);
+ }
+
+ @Override
+ public SettingsMigrationData[] newArray(int size) {
+ return new SettingsMigrationData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mScanAlwaysAvailable);
+ dest.writeBoolean(mP2pFactoryResetPending);
+ dest.writeString(mP2pDeviceName);
+ dest.writeBoolean(mSoftApTimeoutEnabled);
+ dest.writeBoolean(mWakeupEnabled);
+ dest.writeBoolean(mScanThrottleEnabled);
+ dest.writeBoolean(mVerboseLoggingEnabled);
+ }
+
+ /**
+ * @return True if scans are allowed even when wifi is toggled off, false otherwise.
+ */
+ public boolean isScanAlwaysAvailable() {
+ return mScanAlwaysAvailable;
+ }
+
+ /**
+ * @return indicate whether factory reset request is pending.
+ */
+ public boolean isP2pFactoryResetPending() {
+ return mP2pFactoryResetPending;
+ }
+
+ /**
+ * @return the Wi-Fi peer-to-peer device name
+ */
+ public @Nullable String getP2pDeviceName() {
+ return mP2pDeviceName;
+ }
+
+ /**
+ * @return Whether soft AP will shut down after a timeout period when no devices are
+ * connected.
+ */
+ public boolean isSoftApTimeoutEnabled() {
+ return mSoftApTimeoutEnabled;
+ }
+
+ /**
+ * @return whether Wi-Fi Wakeup feature is enabled.
+ */
+ public boolean isWakeUpEnabled() {
+ return mWakeupEnabled;
+ }
+
+ /**
+ * @return Whether wifi scan throttle is enabled or not.
+ */
+ public boolean isScanThrottleEnabled() {
+ return mScanThrottleEnabled;
+ }
+
+ /**
+ * @return Whether to enable verbose logging in Wi-Fi.
+ */
+ public boolean isVerboseLoggingEnabled() {
+ return mVerboseLoggingEnabled;
+ }
+
+ /**
+ * Builder to create instance of {@link SettingsMigrationData}.
+ */
+ public static final class Builder {
+ private boolean mScanAlwaysAvailable;
+ private boolean mP2pFactoryResetPending;
+ private String mP2pDeviceName;
+ private boolean mSoftApTimeoutEnabled;
+ private boolean mWakeupEnabled;
+ private boolean mScanThrottleEnabled;
+ private boolean mVerboseLoggingEnabled;
+
+ public Builder() {
+ }
+
+ /**
+ * Setting to allow scans even when wifi is toggled off.
+ *
+ * @param available true if available, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setScanAlwaysAvailable(boolean available) {
+ mScanAlwaysAvailable = available;
+ return this;
+ }
+
+ /**
+ * Indicate whether factory reset request is pending.
+ *
+ * @param pending true if pending, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setP2pFactoryResetPending(boolean pending) {
+ mP2pFactoryResetPending = pending;
+ return this;
+ }
+
+ /**
+ * The Wi-Fi peer-to-peer device name
+ *
+ * @param name Name if set, null otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setP2pDeviceName(@Nullable String name) {
+ mP2pDeviceName = name;
+ return this;
+ }
+
+ /**
+ * Whether soft AP will shut down after a timeout period when no devices are connected.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setSoftApTimeoutEnabled(boolean enabled) {
+ mSoftApTimeoutEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Value to specify if Wi-Fi Wakeup feature is enabled.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setWakeUpEnabled(boolean enabled) {
+ mWakeupEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Whether wifi scan throttle is enabled or not.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setScanThrottleEnabled(boolean enabled) {
+ mScanThrottleEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Setting to enable verbose logging in Wi-Fi.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setVerboseLoggingEnabled(boolean enabled) {
+ mVerboseLoggingEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Build an instance of {@link SettingsMigrationData}.
+ *
+ * @return Instance of {@link SettingsMigrationData}.
+ */
+ public @NonNull SettingsMigrationData build() {
+ return new SettingsMigrationData(mScanAlwaysAvailable, mP2pFactoryResetPending,
+ mP2pDeviceName, mSoftApTimeoutEnabled, mWakeupEnabled, mScanThrottleEnabled,
+ mVerboseLoggingEnabled);
+ }
+ }
+ }
+
+ /**
+ * Load data from Settings.Global values.
+ *
+ * <p>
+ * Note:
+ * <li> This is method is invoked once on the first bootup. OEM can safely delete these settings
+ * once the migration is complete. The first & only relevant invocation of
+ * {@link #loadFromSettings(Context)} ()} occurs when a previously released
+ * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * </li>
+ *
+ * @param context Context to use for loading the settings provider.
+ * @return Instance of {@link SettingsMigrationData} for migrating data.
+ */
+ @NonNull
+ public static SettingsMigrationData loadFromSettings(@NonNull Context context) {
+ return new SettingsMigrationData.Builder()
+ .setScanAlwaysAvailable(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1)
+ .setP2pFactoryResetPending(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET, 0) == 1)
+ .setP2pDeviceName(
+ Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.WIFI_P2P_DEVICE_NAME))
+ .setSoftApTimeoutEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1)
+ .setWakeUpEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1)
+ .setScanThrottleEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1)
+ .setVerboseLoggingEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 1) == 1)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index d9584885a045..060ddf05b8d7 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -125,6 +125,7 @@ public class SoftApConfigurationTest {
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
+ .setAutoShutdownEnabled(true)
.setShutdownTimeoutMillis(500000)
.enableClientControlByUser(true)
.setClientList(testBlockedClientList, testAllowedClientList)
@@ -136,6 +137,7 @@ public class SoftApConfigurationTest {
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
+ assertThat(original.isAutoShutdownEnabled()).isEqualTo(true);
assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 853212aafcdf..234d929fc05a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2401,4 +2401,15 @@ public class WifiManagerTest {
assertFalse(mWifiManager.isAutoWakeupEnabled());
verify(mWifiService).isAutoWakeupEnabled();
}
+
+
+ @Test
+ public void testScanAvailable() throws Exception {
+ mWifiManager.setScanAlwaysAvailable(true);
+ verify(mWifiService).setScanAlwaysAvailable(true);
+
+ when(mWifiService.isScanAlwaysAvailable()).thenReturn(false);
+ assertFalse(mWifiManager.isScanAlwaysAvailable());
+ verify(mWifiService).isScanAlwaysAvailable();
+ }
}