diff options
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(); + } } |