Merge "Fix Kotlin warnings"
diff --git a/Android.bp b/Android.bp
index b3c9e2a..add492f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1327,7 +1327,6 @@
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 d4ceabd..7b3764a 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 @@
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 ab669d4..5533ed8 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -48,17 +48,8 @@
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 @@
":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 841483b..20933e4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9,6 +9,7 @@
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 @@
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();
@@ -43402,11 +43404,12 @@
public abstract class ControlsProviderService extends android.app.Service {
ctor public ControlsProviderService();
- method public abstract void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
- method public void loadSuggestedControls(int, @NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
+ method @Deprecated public void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
+ method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable();
+ method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested();
field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
field @NonNull public static final String TAG = "ControlsProviderService";
}
@@ -46812,59 +46815,6 @@
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();
@@ -53239,11 +53189,13 @@
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
@@ -53283,7 +53235,7 @@
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);
@@ -53891,6 +53843,7 @@
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);
diff --git a/api/system-current.txt b/api/system-current.txt
index a7dbf38..98c1f0d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -118,6 +118,7 @@
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 @@
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 @@
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";
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9ed4798..8f02f15 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1035,10 +1035,12 @@
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 @@
@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 @@
OP_MANAGE_EXTERNAL_STORAGE,
OP_INTERACT_ACROSS_PROFILES,
OP_LOADER_USAGE_STATS,
+ OP_ACCESS_CALL_AUDIO,
};
/**
@@ -1525,6 +1531,7 @@
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 @@
OPSTR_INTERACT_ACROSS_PROFILES,
OPSTR_ACTIVATE_PLATFORM_VPN,
OPSTR_LOADER_USAGE_STATS,
+ OPSTR_ACCESS_CALL_AUDIO,
};
/**
@@ -1730,6 +1738,7 @@
"INTERACT_ACROSS_PROFILES",
"ACTIVATE_PLATFORM_VPN",
"LOADER_USAGE_STATS",
+ "ACCESS_CALL_AUDIO",
};
/**
@@ -1834,6 +1843,7 @@
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 @@
null, // INTERACT_ACROSS_PROFILES
null, // ACTIVATE_PLATFORM_VPN
null, // LOADER_USAGE_STATS
+ null, // ACCESS_CALL_AUDIO
};
/**
@@ -2041,6 +2052,7 @@
false, // INTERACT_ACROSS_PROFILES
false, // ACTIVATE_PLATFORM_VPN
false, // LOADER_USAGE_STATS
+ false, // ACCESS_CALL_AUDIO
};
/**
@@ -2143,6 +2155,7 @@
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 @@
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 130e2ec..6b1afda 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
};
@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 180507c..3c475c1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -315,11 +315,6 @@
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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 655dd9b4..f1559f7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -148,6 +148,7 @@
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 @@
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 d08dbc6..139b179 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1416,6 +1416,7 @@
* @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 @@
}
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 84332ca..25c1e12 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -478,4 +478,5 @@
long getManagedProfileMaximumTimeOff(in ComponentName admin);
void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
+ boolean canProfileOwnerResetPasswordWhenLocked(in int userId);
}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8c98960..ad00233 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -103,4 +103,11 @@
* {@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 a8fa6db..199b5d5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -18,7 +18,9 @@
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 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 @@
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 @@
* 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 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 @@
@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);
- }
+ 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");
- }
- } 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 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");
}
- 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();
- }
+ } 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 6d1f646..84fd580 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -33,8 +33,8 @@
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 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 @@
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 4bd31f8..a2def7f 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,6 +20,8 @@
import android.annotation.Nullable;
import android.os.IVold;
+import java.util.Set;
+
/**
* Mount service local interface.
*
@@ -98,6 +100,12 @@
}
/**
+ * 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.
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index de4c056..cb20db9 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -16,6 +16,7 @@
package android.service.controls;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
@@ -34,7 +35,6 @@
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Flow.Publisher;
@@ -70,25 +70,48 @@
* Retrieve all available controls, using the stateless builder
* {@link Control.StatelessBuilder} to build each Control, then use the
* provided consumer to callback to the call originator.
+ *
+ * @deprecated Removing consumer-based load apis. Use publisherForAllAvailable() instead
*/
- public abstract void loadAvailableControls(@NonNull Consumer<List<Control>> consumer);
-
- /**
- * (Optional) The service may be asked to provide a small number of recommended controls, in
- * order to suggest some controls to the user for favoriting. The controls shall be built using
- * the stateless builder {@link Control.StatelessBuilder}, followed by an invocation to the
- * provided consumer to callback to the call originator. If the number of controls
- * is greater than maxNumber, the list will be truncated.
- */
- public void loadSuggestedControls(int maxNumber, @NonNull Consumer<List<Control>> consumer) {
- // Override to change the default behavior
+ @Deprecated
+ public void loadAvailableControls(@NonNull Consumer<List<Control>> consumer) {
+ // pending removal
consumer.accept(Collections.emptyList());
}
/**
- * Return a valid Publisher for the given controlIds. This publisher will be asked
- * to provide updates for the given list of controlIds as long as the Subscription
- * is valid.
+ * Publisher for all available controls
+ *
+ * Retrieve all available controls. Use the stateless builder {@link Control.StatelessBuilder}
+ * to build each Control. Call {@link Subscriber#onComplete} when done loading all unique
+ * controls, or {@link Subscriber#onError} for error scenarios. Duplicate Controls will
+ * replace the original.
+ */
+ @Nullable
+ public Publisher<Control> publisherForAllAvailable() {
+ // will be abstract and @nonnull when consumers are removed
+ return null;
+ }
+
+ /**
+ * (Optional) Publisher for suggested controls
+ *
+ * The service may be asked to provide a small number of recommended controls, in
+ * order to suggest some controls to the user for favoriting. The controls shall be built using
+ * the stateless builder {@link Control.StatelessBuilder}. The number of controls requested
+ * through {@link Subscription#request} will be limited. Call {@link Subscriber#onComplete}
+ * when done, or {@link Subscriber#onError} for error scenarios.
+ */
+ @Nullable
+ public Publisher<Control> publisherForSuggested() {
+ return null;
+ }
+
+ /**
+ * Return a valid Publisher for the given controlIds. This publisher will be asked to provide
+ * updates for the given list of controlIds as long as the {@link Subscription} is valid.
+ * Calls to {@link Subscriber#onComplete} will not be expected. Instead, wait for the call from
+ * {@link Subscription#cancel} to indicate that updates are no longer required.
*/
@NonNull
public abstract Publisher<Control> publisherFor(@NonNull List<String> controlIds);
@@ -113,13 +136,13 @@
mToken = bundle.getBinder(CALLBACK_TOKEN);
return new IControlsProvider.Stub() {
- public void load(IControlsLoadCallback cb) {
- mHandler.obtainMessage(RequestHandler.MSG_LOAD, cb).sendToTarget();
+ public void load(IControlsSubscriber subscriber) {
+ mHandler.obtainMessage(RequestHandler.MSG_LOAD, subscriber).sendToTarget();
}
- public void loadSuggested(int maxNumber, IControlsLoadCallback cb) {
- LoadMessage msg = new LoadMessage(maxNumber, cb);
- mHandler.obtainMessage(RequestHandler.MSG_LOAD_SUGGESTED, msg).sendToTarget();
+ public void loadSuggested(IControlsSubscriber subscriber) {
+ mHandler.obtainMessage(RequestHandler.MSG_LOAD_SUGGESTED, subscriber)
+ .sendToTarget();
}
public void subscribe(List<String> controlIds,
@@ -148,73 +171,56 @@
private static final int MSG_ACTION = 3;
private static final int MSG_LOAD_SUGGESTED = 4;
- /**
- * This the maximum number of controls that can be loaded via
- * {@link ControlsProviderService#loadAvailablecontrols}. Anything over this number
- * will be truncated.
- */
- private static final int MAX_NUMBER_OF_CONTROLS_ALLOWED = 1000;
-
RequestHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_LOAD:
- final IControlsLoadCallback cb = (IControlsLoadCallback) msg.obj;
- ControlsProviderService.this.loadAvailableControls(consumerFor(
- MAX_NUMBER_OF_CONTROLS_ALLOWED, cb));
- break;
+ case MSG_LOAD: {
+ final IControlsSubscriber cs = (IControlsSubscriber) msg.obj;
+ final SubscriberProxy proxy = new SubscriberProxy(true, mToken, cs);
- case MSG_LOAD_SUGGESTED:
- final LoadMessage lMsg = (LoadMessage) msg.obj;
- ControlsProviderService.this.loadSuggestedControls(lMsg.mMaxNumber,
- consumerFor(lMsg.mMaxNumber, lMsg.mCb));
+ Publisher<Control> publisher =
+ ControlsProviderService.this.publisherForAllAvailable();
+ if (publisher == null) {
+ ControlsProviderService.this.loadAvailableControls(consumerFor(proxy));
+ } else {
+ publisher.subscribe(proxy);
+ }
break;
+ }
- case MSG_SUBSCRIBE:
+ case MSG_LOAD_SUGGESTED: {
+ final IControlsSubscriber cs = (IControlsSubscriber) msg.obj;
+ final SubscriberProxy proxy = new SubscriberProxy(true, mToken, cs);
+
+ Publisher<Control> publisher =
+ ControlsProviderService.this.publisherForSuggested();
+ if (publisher == null) {
+ Log.i(TAG, "No publisher provided for suggested controls");
+ proxy.onComplete();
+ } else {
+ publisher.subscribe(proxy);
+ }
+ break;
+ }
+
+ case MSG_SUBSCRIBE: {
final SubscribeMessage sMsg = (SubscribeMessage) msg.obj;
- final IControlsSubscriber cs = sMsg.mSubscriber;
- Subscriber<Control> s = new Subscriber<Control>() {
- public void onSubscribe(Subscription subscription) {
- try {
- cs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
- public void onNext(@NonNull Control statefulControl) {
- Preconditions.checkNotNull(statefulControl);
- try {
- cs.onNext(mToken, statefulControl);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
- public void onError(Throwable t) {
- try {
- cs.onError(mToken, t.toString());
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
- public void onComplete() {
- try {
- cs.onComplete(mToken);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
- }
- };
- ControlsProviderService.this.publisherFor(sMsg.mControlIds).subscribe(s);
- break;
+ final SubscriberProxy proxy = new SubscriberProxy(false, mToken,
+ sMsg.mSubscriber);
- case MSG_ACTION:
+ ControlsProviderService.this.publisherFor(sMsg.mControlIds).subscribe(proxy);
+ break;
+ }
+
+ case MSG_ACTION: {
final ActionMessage aMsg = (ActionMessage) msg.obj;
ControlsProviderService.this.performControlAction(aMsg.mControlId,
aMsg.mAction, consumerFor(aMsg.mControlId, aMsg.mCb));
break;
+ }
}
}
@@ -234,39 +240,88 @@
};
}
- private Consumer<List<Control>> consumerFor(int maxNumber, IControlsLoadCallback cb) {
- return (@NonNull List<Control> controls) -> {
+ /**
+ * Method will be removed during migration to publisher
+ */
+ private Consumer<List<Control>> consumerFor(final Subscriber<Control> subscriber) {
+ return (@NonNull final List<Control> controls) -> {
Preconditions.checkNotNull(controls);
- if (controls.size() > maxNumber) {
- Log.w(TAG, "Too many controls. Provided: " + controls.size() + ", Max allowed: "
- + maxNumber + ". Truncating the list.");
- controls = controls.subList(0, maxNumber);
- }
- List<Control> list = new ArrayList<>();
- for (Control control: controls) {
- if (control == null) {
- Log.e(TAG, "onLoad: null control.");
- }
- if (isStatelessControl(control)) {
- list.add(control);
- } else {
- Log.w(TAG, "onLoad: control is not stateless.");
- list.add(new Control.StatelessBuilder(control).build());
- }
- }
- try {
- cb.accept(mToken, list);
- } catch (RemoteException ex) {
- ex.rethrowAsRuntimeException();
- }
+ subscriber.onSubscribe(new Subscription() {
+ public void request(long n) {
+ for (Control control: controls) {
+ Control c;
+ if (control == null) {
+ Log.e(TAG, "onLoad: null control.");
+ }
+ if (isStatelessControl(control)) {
+ c = control;
+ } else {
+ Log.w(TAG, "onLoad: control is not stateless.");
+ c = new Control.StatelessBuilder(control).build();
+ }
+
+ subscriber.onNext(c);
+ }
+ subscriber.onComplete();
+ }
+
+ public void cancel() {}
+ });
};
}
+ }
- private boolean isStatelessControl(Control control) {
- return (control.getStatus() == Control.STATUS_UNKNOWN
- && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
- && TextUtils.isEmpty(control.getStatusText()));
+ private static boolean isStatelessControl(Control control) {
+ return (control.getStatus() == Control.STATUS_UNKNOWN
+ && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
+ && TextUtils.isEmpty(control.getStatusText()));
+ }
+
+ private static class SubscriberProxy implements Subscriber<Control> {
+ private IBinder mToken;
+ private IControlsSubscriber mCs;
+ private boolean mEnforceStateless;
+
+ SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) {
+ mEnforceStateless = enforceStateless;
+ mToken = token;
+ mCs = cs;
+ }
+
+ public void onSubscribe(Subscription subscription) {
+ try {
+ mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onNext(@NonNull Control control) {
+ Preconditions.checkNotNull(control);
+ try {
+ if (mEnforceStateless && !isStatelessControl(control)) {
+ Log.w(TAG, "onNext(): control is not stateless. Use the "
+ + "Control.StatelessBuilder() to build the control.");
+ control = new Control.StatelessBuilder(control).build();
+ }
+ mCs.onNext(mToken, control);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onError(Throwable t) {
+ try {
+ mCs.onError(mToken, t.toString());
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ public void onComplete() {
+ try {
+ mCs.onComplete(mToken);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
@@ -307,14 +362,4 @@
this.mSubscriber = subscriber;
}
}
-
- private static class LoadMessage {
- final int mMaxNumber;
- final IControlsLoadCallback mCb;
-
- LoadMessage(int maxNumber, IControlsLoadCallback cb) {
- this.mMaxNumber = maxNumber;
- this.mCb = cb;
- }
- }
}
diff --git a/core/java/android/service/controls/IControlsLoadCallback.aidl b/core/java/android/service/controls/IControlsLoadCallback.aidl
deleted file mode 100644
index bfc61cd..0000000
--- a/core/java/android/service/controls/IControlsLoadCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.service.controls;
-
-import android.service.controls.Control;
-
-/**
- * @hide
- */
-oneway interface IControlsLoadCallback {
- void accept(in IBinder token, in List<Control> controls);
-}
\ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl
index 4375fbb..0cb06b4 100644
--- a/core/java/android/service/controls/IControlsProvider.aidl
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -17,7 +17,6 @@
package android.service.controls;
import android.service.controls.IControlsActionCallback;
-import android.service.controls.IControlsLoadCallback;
import android.service.controls.IControlsSubscriber;
import android.service.controls.actions.ControlActionWrapper;
@@ -25,9 +24,9 @@
* @hide
*/
oneway interface IControlsProvider {
- void load(IControlsLoadCallback cb);
+ void load(IControlsSubscriber subscriber);
- void loadSuggested(int maxNumber, IControlsLoadCallback cb);
+ void loadSuggested(IControlsSubscriber subscriber);
void subscribe(in List<String> controlIds,
IControlsSubscriber subscriber);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 360dedd..58e5b2d 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.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 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 4ac6a66..13d6dd6 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 @@
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 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 @@
* 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 29371b0..0816e84 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -214,7 +214,7 @@
@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;
@@ -2738,13 +2738,17 @@
* 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 5566e0e..deff79d5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -788,9 +788,16 @@
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 @@
mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
}
+ } else if ((layoutSizeChanged || positionChanged) &&
+ WindowManagerGlobal.useBLAST()) {
+ viewRoot.setUseBLASTSyncTransaction();
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if (sizeChanged && !creating) {
@@ -1058,11 +1068,6 @@
} 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 @@
}
/**
- * @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 b9be33c..4c7307e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@
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 @@
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 @@
mAttachInfo.mKeyDispatchState.reset(this);
}
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
@@ -15820,12 +15833,17 @@
* 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 @@
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 56f933c..b6c46be 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -9029,4 +9029,30 @@
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 f25206d..775c15e 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -675,4 +675,18 @@
*/
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 043e5be..204d2c8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,6 +18,7 @@
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.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 @@
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
boolean mUnbufferedInputDispatch;
+ boolean mUnbufferedInputDispatchBySource;
+ @InputSourceClass
+ int mUnbufferedInputSource = SOURCE_CLASS_NONE;
+
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -1849,7 +1855,7 @@
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- if (!mUnbufferedInputDispatch) {
+ if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
@@ -7505,6 +7511,9 @@
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 @@
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ processUnbufferedRequest(event);
List<InputEvent> processedEvents;
try {
processedEvents =
@@ -8134,7 +8144,7 @@
@Override
public void onBatchedInputEventPending() {
- if (mUnbufferedInputDispatch) {
+ if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
@@ -8151,6 +8161,17 @@
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 @@
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 62f3fa4..87dcba0 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -163,6 +163,15 @@
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/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index c68e506..b61b9de 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 @@
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 @@
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 b01083b..8a53bd0 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -413,10 +413,15 @@
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 @@
(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 741ce82..a9ef257 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -613,11 +613,14 @@
}
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) {
@@ -1467,7 +1470,7 @@
(void*)nativeSetLayerStack },
{"nativeSetShadowRadius", "(JJF)V",
(void*)nativeSetShadowRadius },
- {"nativeSetFrameRate", "(JJF)V",
+ {"nativeSetFrameRate", "(JJFI)V",
(void*)nativeSetFrameRate },
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d91911c..e4141e0 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -105,10 +105,12 @@
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 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 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 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 @@
}
}
+// 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 @@
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 b0b9ce6f..08db454 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -275,6 +275,7 @@
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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6530036..62e57e4 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 -->
<!-- ================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e6a93e5..d513e2b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5364,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/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index ea0a0fd..37e8d59 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -244,6 +244,28 @@
}
@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/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index a24b4e0..78c88d7 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -62,8 +63,6 @@
@Mock
private IControlsActionCallback.Stub mActionCallback;
@Mock
- private IControlsLoadCallback.Stub mLoadCallback;
- @Mock
private IControlsSubscriber.Stub mSubscriber;
@Mock
private IIntentSender mIIntentSender;
@@ -79,8 +78,6 @@
when(mActionCallback.asBinder()).thenCallRealMethod();
when(mActionCallback.queryLocalInterface(any())).thenReturn(mActionCallback);
- when(mLoadCallback.asBinder()).thenCallRealMethod();
- when(mLoadCallback.queryLocalInterface(any())).thenReturn(mLoadCallback);
when(mSubscriber.asBinder()).thenCallRealMethod();
when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
@@ -102,22 +99,28 @@
Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent)
.setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build();
- @SuppressWarnings("unchecked")
- ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
+ ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+ ArgumentCaptor<Control> controlCaptor =
+ ArgumentCaptor.forClass(Control.class);
ArrayList<Control> list = new ArrayList<>();
list.add(control1);
list.add(control2);
mControlsProviderService.setControls(list);
- mControlsProvider.load(mLoadCallback);
+ mControlsProvider.load(mSubscriber);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- verify(mLoadCallback).accept(eq(mToken), captor.capture());
- List<Control> l = captor.getValue();
- assertEquals(2, l.size());
- assertTrue(equals(control1, l.get(0)));
- assertTrue(equals(control2, l.get(1)));
+ verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
+ subscriptionCaptor.getValue().request(1000);
+
+ verify(mSubscriber, times(2)).onNext(eq(mToken), controlCaptor.capture());
+ List<Control> values = controlCaptor.getAllValues();
+ assertTrue(equals(values.get(0), list.get(0)));
+ assertTrue(equals(values.get(1), list.get(1)));
+
+ verify(mSubscriber).onComplete(eq(mToken));
}
@Test
@@ -128,50 +131,57 @@
.build();
Control statelessControl = new Control.StatelessBuilder(control).build();
- @SuppressWarnings("unchecked")
- ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
+ ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+ ArgumentCaptor<Control> controlCaptor =
+ ArgumentCaptor.forClass(Control.class);
ArrayList<Control> list = new ArrayList<>();
list.add(control);
mControlsProviderService.setControls(list);
- mControlsProvider.load(mLoadCallback);
+ mControlsProvider.load(mSubscriber);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- verify(mLoadCallback).accept(eq(mToken), captor.capture());
- List<Control> l = captor.getValue();
- assertEquals(1, l.size());
- assertFalse(equals(control, l.get(0)));
- assertTrue(equals(statelessControl, l.get(0)));
- assertEquals(Control.STATUS_UNKNOWN, l.get(0).getStatus());
+ verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
+ subscriptionCaptor.getValue().request(1000);
+
+ verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
+ Control c = controlCaptor.getValue();
+ assertFalse(equals(control, c));
+ assertTrue(equals(statelessControl, c));
+ assertEquals(Control.STATUS_UNKNOWN, c.getStatus());
+
+ verify(mSubscriber).onComplete(eq(mToken));
}
@Test
- public void testLoadSuggested_withMaxNumber() throws RemoteException {
+ public void testOnLoadSuggested_allStateless() throws RemoteException {
Control control1 = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent)
.setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build();
- @SuppressWarnings("unchecked")
- ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
+ ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+ ArgumentCaptor<Control> controlCaptor =
+ ArgumentCaptor.forClass(Control.class);
ArrayList<Control> list = new ArrayList<>();
list.add(control1);
list.add(control2);
- final int maxSuggested = 1;
-
mControlsProviderService.setControls(list);
- mControlsProvider.loadSuggested(maxSuggested, mLoadCallback);
+ mControlsProvider.loadSuggested(mSubscriber);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- verify(mLoadCallback).accept(eq(mToken), captor.capture());
- List<Control> l = captor.getValue();
- assertEquals(maxSuggested, l.size());
+ verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
+ subscriptionCaptor.getValue().request(1);
- for (int i = 0; i < maxSuggested; ++i) {
- assertTrue(equals(list.get(i), l.get(i)));
- }
+ verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
+ Control c = controlCaptor.getValue();
+ assertTrue(equals(c, list.get(0)));
+
+ verify(mSubscriber).onComplete(eq(mToken));
}
@Test
@@ -244,22 +254,19 @@
}
@Override
- public void loadSuggestedControls(int maxNumber, Consumer<List<Control>> cb) {
- cb.accept(mControls);
- }
-
- @Override
public Publisher<Control> publisherFor(List<String> ids) {
return new Publisher<Control>() {
public void subscribe(final Subscriber s) {
- s.onSubscribe(new Subscription() {
- public void request(long n) {
- for (Control c : mControls) {
- s.onNext(c);
- }
- }
- public void cancel() {}
- });
+ s.onSubscribe(createSubscription(s, mControls));
+ }
+ };
+ }
+
+ @Override
+ public Publisher<Control> publisherForSuggested() {
+ return new Publisher<Control>() {
+ public void subscribe(final Subscriber s) {
+ s.onSubscribe(createSubscription(s, mControls));
}
};
}
@@ -269,7 +276,19 @@
Consumer<Integer> cb) {
cb.accept(ControlAction.RESPONSE_OK);
}
+
+ private Subscription createSubscription(Subscriber s, List<Control> controls) {
+ return new Subscription() {
+ public void request(long n) {
+ int i = 0;
+ for (Control c : mControls) {
+ if (i++ < n) s.onNext(c);
+ else break;
+ }
+ s.onComplete();
+ }
+ public void cancel() {}
+ };
+ }
}
}
-
-
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ba793e8..0af6cbf 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -547,16 +547,11 @@
}
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 123559a..a428cb3 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 fb3c065..4436aab 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
similarity index 98%
rename from packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
rename to packages/OsuLogin/src/com/android/hotspot2/osulogin/OsuLoginActivity.java
index 3a994d7..d554745 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 androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import com.android.hotspot2.R;
-
import java.net.MalformedURLException;
import java.net.URL;
@@ -194,7 +192,7 @@
// 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 @@
mPageError = true;
Log.e(TAG, "onReceived Error for MainFrame: " + error.getErrorCode());
}
- }
+ }
}
}
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 2d288ff..b1d39f5 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 @@
*/
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 d317b7e..69bc259 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.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 @@
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
+ @Inject Lazy<Divider> mDivider;
@Inject
public Dependency() {
@@ -527,6 +529,7 @@
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 5c0df17..0000000
--- 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 0000000..801cf8a
--- /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 7f45cc8..0329183 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 @@
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 957ea8d..a796f3c 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 @@
// 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/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 87bdfa8..6ff1bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -22,7 +22,6 @@
import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.IControlsActionCallback
-import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
@@ -63,12 +62,6 @@
private val componentMap: MutableMap<Key, ControlsProviderLifecycleManager> =
ArrayMap<Key, ControlsProviderLifecycleManager>()
- private val loadCallbackService = object : IControlsLoadCallback.Stub() {
- override fun accept(token: IBinder, controls: MutableList<Control>) {
- backgroundExecutor.execute(OnLoadRunnable(token, controls))
- }
- }
-
private val actionCallbackService = object : IControlsActionCallback.Stub() {
override fun accept(
token: IBinder,
@@ -106,7 +99,6 @@
return ControlsProviderLifecycleManager(
context,
backgroundExecutor,
- loadCallbackService,
actionCallbackService,
subscriberService,
currentUser,
@@ -130,7 +122,7 @@
callback: ControlsBindingController.LoadCallback
) {
val provider = retrieveLifecycleManager(component)
- provider.maybeBindAndLoad(callback)
+ provider.maybeBindAndLoad(LoadSubscriber(callback))
}
override fun subscribe(controls: List<ControlInfo>) {
@@ -216,7 +208,8 @@
private inner class OnLoadRunnable(
token: IBinder,
- val list: List<Control>
+ val list: List<Control>,
+ val callback: ControlsBindingController.LoadCallback
) : CallbackRunnable(token) {
override fun run() {
if (provider == null) {
@@ -233,9 +226,7 @@
return
}
}
- provider.lastLoadCallback?.accept(list) ?: run {
- Log.w(TAG, "Null callback")
- }
+ callback.accept(list)
provider.unbindService()
}
}
@@ -277,7 +268,7 @@
) : CallbackRunnable(token) {
override fun run() {
provider?.let {
- Log.i(TAG, "onComplete receive from '${provider.componentName}'")
+ Log.i(TAG, "onComplete receive from '${it.componentName}'")
}
}
}
@@ -288,7 +279,7 @@
) : CallbackRunnable(token) {
override fun run() {
provider?.let {
- Log.e(TAG, "onError receive from '${provider.componentName}': $error")
+ Log.e(TAG, "onError receive from '${it.componentName}': $error")
}
}
}
@@ -308,6 +299,44 @@
}
}
}
+
+ private inner class OnLoadErrorRunnable(
+ token: IBinder,
+ val error: String,
+ val callback: ControlsBindingController.LoadCallback
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ callback.error(error)
+ provider?.let {
+ Log.e(TAG, "onError receive from '${it.componentName}': $error")
+ }
+ }
+ }
+
+ private inner class LoadSubscriber(
+ val callback: ControlsBindingController.LoadCallback
+ ) : IControlsSubscriber.Stub() {
+ val loadedControls = ArrayList<Control>()
+ var hasError = false
+
+ override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ }
+
+ override fun onNext(token: IBinder, c: Control) {
+ backgroundExecutor.execute { loadedControls.add(c) }
+ }
+ override fun onError(token: IBinder, s: String) {
+ hasError = true
+ backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback))
+ }
+
+ override fun onComplete(token: IBinder) {
+ if (!hasError) {
+ backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback))
+ }
+ }
+ }
}
-private data class Key(val component: ComponentName, val user: UserHandle)
\ No newline at end of file
+private data class Key(val component: ComponentName, val user: UserHandle)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 9ec71c7..a53fcd4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -29,7 +29,6 @@
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
import android.service.controls.ControlsProviderService.CALLBACK_TOKEN
import android.service.controls.IControlsActionCallback
-import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
@@ -48,8 +47,6 @@
*
* @property context A SystemUI context for binding to the services
* @property executor A delayable executor for posting timeouts
- * @property loadCallbackService a callback interface to hand the remote service for loading
- * controls
* @property actionCallbackService a callback interface to hand the remote service for sending
* action responses
* @property subscriberService an "subscriber" interface for requesting and accepting updates for
@@ -60,15 +57,12 @@
class ControlsProviderLifecycleManager(
private val context: Context,
private val executor: DelayableExecutor,
- private val loadCallbackService: IControlsLoadCallback.Stub,
private val actionCallbackService: IControlsActionCallback.Stub,
private val subscriberService: IControlsSubscriber.Stub,
val user: UserHandle,
val componentName: ComponentName
) : IBinder.DeathRecipient {
- var lastLoadCallback: ControlsBindingController.LoadCallback? = null
- private set
val token: IBinder = Binder()
@GuardedBy("subscriptions")
private val subscriptions = mutableListOf<IControlsSubscription>()
@@ -156,9 +150,12 @@
bindService(false)
return
}
- if (Message.Load in queue) {
- load()
+
+ queue.filter { it is Message.Load }.forEach {
+ val msg = it as Message.Load
+ load(msg.subscriber)
}
+
queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run {
if (this.isNotEmpty()) {
subscribe(this)
@@ -193,12 +190,12 @@
}
}
- private fun load() {
+ private fun load(subscriber: IControlsSubscriber.Stub) {
if (DEBUG) {
Log.d(TAG, "load $componentName")
}
- if (!(wrapper?.load(loadCallbackService) ?: false)) {
- queueMessage(Message.Load)
+ if (!(wrapper?.load(subscriber) ?: false)) {
+ queueMessage(Message.Load(subscriber))
binderDied()
}
}
@@ -213,27 +210,23 @@
}
/**
- * Request a call to [ControlsProviderService.loadAvailableControls].
+ * Request a call to [IControlsProvider.load].
*
* If the service is not bound, the call will be queued and the service will be bound first.
- * The service will be bound after the controls are returned or the call times out.
+ * The service will be unbound after the controls are returned or the call times out.
*
- * @param callback a callback in which to return the result back. If the call times out
- * [ControlsBindingController.LoadCallback.error] will be called instead.
+ * @param subscriber the subscriber that manages coordination for loading controls
*/
- fun maybeBindAndLoad(callback: ControlsBindingController.LoadCallback) {
+ fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
unqueueMessage(Message.Unbind)
- lastLoadCallback = callback
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back error
Log.d(TAG, "Timeout waiting onLoad for $componentName")
- callback.error("Timeout waiting onLoad")
- // Don't accept load callbacks after this
- lastLoadCallback = null
+ subscriber.onError(token, "Timeout waiting onLoad")
unbindService()
}, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- invokeOrQueue(::load, Message.Load)
+ invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
}
/**
@@ -324,7 +317,6 @@
* Request unbind from the service.
*/
fun unbindService() {
- lastLoadCallback = null
onLoadCanceller?.run()
onLoadCanceller = null
@@ -344,7 +336,7 @@
*/
sealed class Message {
abstract val type: Int
- object Load : Message() {
+ class Load(val subscriber: IControlsSubscriber.Stub) : Message() {
override val type = MSG_LOAD
}
object Unbind : Message() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index b90f892..b2afd3c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -18,7 +18,6 @@
import android.service.controls.actions.ControlAction
import android.service.controls.IControlsActionCallback
-import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
@@ -45,9 +44,9 @@
}
}
- fun load(cb: IControlsLoadCallback): Boolean {
+ fun load(subscriber: IControlsSubscriber): Boolean {
return callThroughService {
- service.load(cb)
+ service.load(subscriber)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 019cb14..17aaff1 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.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.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 @@
}
}
+ @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/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ad49364..34cad51 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 @@
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 5ae0954..4f20492 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 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 @@
}
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 90cc0e57..2daefbd 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;
- public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+ // 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,
+ 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();
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- mDockDividerVisibilityListener);
- } catch (Exception e) {
- Log.e(TAG, "Failed to register docked stack listener", e);
- }
- mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+ 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
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
+ 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 {
+ 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) {
+ Slog.e(TAG, "Failed to register docked stack listener", e);
+ }
+ update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
+ }
+
+ @Override
+ 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 @@
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,65 +337,86 @@
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);
+ void updateVisibility(final boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- // Update state because animations won't finish.
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- }
+ 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();
+ }
+
+ /** 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 notifyDockedStackExistsChanged(final boolean exists) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mForcedResizableController.notifyDockedStackExistsChanged(exists);
- }
- });
+ void setAdjustedForIme(boolean adjustedForIme) {
+ if (mAdjustedForIme == adjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = adjustedForIme;
+ updateTouchable();
}
private void updateTouchable() {
mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
- public void onRecentsActivityStarting() {
- if (mView != null) {
- mView.onRecentsActivityStarting();
- }
- }
-
/**
* Workaround for b/62528361, at the time recents has drawn, it may happen before a
* configuration change to the Divider, and internally, the event will be posted to the
@@ -206,6 +448,9 @@
}
public void onAppTransitionFinished() {
+ if (mView == null) {
+ return;
+ }
mForcedResizableController.onAppTransitionFinished();
}
@@ -231,46 +476,66 @@
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);
+ }
+
+ 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 onDockedStackMinimizedChanged(boolean minimized, long animDuration,
- boolean isHomeStackResizable) throws RemoteException {
- mHomeStackResizable = isHomeStackResizable;
- updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
+ 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 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);
- }
- }
- }
- });
- }
-
- @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 49f4d5e..3b7f315 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 @@
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 9fe6e84..375d9bb 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.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 @@
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
+ private static final String TAG = "DividerView";
public interface DividerCallbacks {
void onDraggingStart();
@@ -123,14 +121,11 @@
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 @@
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 @@
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 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 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 @@
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 @@
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 @@
&& !mIsInMinimizeInteraction) {
saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
}
+ mFirstLayout = true;
}
void onDividerRemoved() {
@@ -341,17 +340,17 @@
|| 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 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 @@
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 @@
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 @@
}
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 @@
}
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 @@
: 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 @@
// 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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
.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 @@
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 @@
@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 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 @@
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);
}
- public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
+ resizeSplitSurfaces(t, dockedRect, null, otherRect, null);
+ }
+
+ 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 @@
mBackground.invalidate();
}
+ Transaction t = mTiles.getTransaction();
mLastResizeRect.set(mDockedRect);
if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1037,8 +1106,10 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
void onDockedFirstAnimationFrame() {
- saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onDockedTopTask() {
@@ -1307,8 +1375,9 @@
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 @@
}
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 @@
}
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 2486d653..3020a25 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.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 @@
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 @@
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 @@
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
@@ -95,7 +97,7 @@
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 c6ac309..db7996e 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.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 @@
}
};
+ 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 ForcedResizableInfoActivityController(Context context) {
+ public ForcedResizableInfoActivityController(Context context, Divider divider) {
mContext = context;
ActivityManagerWrapper.getInstance().registerTaskStackListener(
new TaskStackChangeListener() {
@@ -87,12 +95,7 @@
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 0000000..b19f560
--- /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 0000000..5cc8799
--- /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 228aab5..7685733 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 @@
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 @@
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 int getDockSide() {
- try {
- return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get dock side: " + e);
- }
- return DOCKED_INVALID;
- }
-
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- mDimLayerVisible = visible;
- mDimLayerTargetWindowingMode = targetWindowingMode;
- mDimLayerAlpha = alpha;
- mExecutor.execute(mDimLayerRunnable);
- }
-
+ /** 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 {
+ 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) {
+ }
+ return resizable;
+ }
+
+ static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
+ applyHomeTasksMinimized(layout, parent, null /* transaction */);
+ }
+
+ /**
+ * 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);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d9b4e91..f8db922 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.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;
@@ -53,6 +51,7 @@
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;
@@ -74,6 +73,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -111,7 +111,7 @@
@Nullable
private LockscreenWallpaper mLockscreenWallpaper;
- private final Handler mHandler = Dependency.get(MAIN_HANDLER);
+ private final Executor mMainExecutor;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
@@ -182,7 +182,8 @@
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController) {
+ KeyguardBypassController keyguardBypassController,
+ @Main Executor mainExecutor) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -195,6 +196,7 @@
mStatusBarLazy = statusBarLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mEntryManager = notificationEntryManager;
+ mMainExecutor = mainExecutor;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
@@ -624,7 +626,7 @@
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 0b37c22..cd5bb77 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.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
+import java.util.concurrent.Executor;
+
import javax.inject.Singleton;
import dagger.Lazy;
@@ -88,14 +90,16 @@
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 93f5805..55a20fa 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 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 @@
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 @@
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/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 55173182..234ab93 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 @@
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/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index fa4bc2a..52f7c2c 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 @@
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 @@
/**
* 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 @@
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 @@
if (mImageCache != null) {
mImageCache.setImageResolver(this);
}
+
+ updateMaxImageSizes();
}
/**
@@ -66,14 +84,49 @@
return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
}
+ private boolean isLowRam() {
+ return ActivityManager.isLowRamDeviceStatic();
+ }
+
/**
- * To resolve image from specified uri directly.
+ * 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. 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 d790cbc..84aecd4 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 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.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 @@
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 d8d96c1..0d3b09a 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.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 @@
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 @@
: 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 f6e1681..cebcf76 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.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 @@
// 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 @@
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 @@
| 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 @@
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 @@
}
}
- 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 @@
}
/**
- * 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 @@
}
}
- // 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 @@
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 @@
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 @@
|| 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 @@
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 @@
+ " 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,35 +634,12 @@
+ " type=" + networkType);
}
mDataState = state;
- updateDataNetType(networkType);
+ if (networkType != mDisplayInfo.getNetworkType()) {
+ mDisplayInfo = new DisplayInfo(networkType, DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+ }
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();
- }
- }
- return false;
- }
-
@Override
public void onDataActivity(int direction) {
if (DEBUG) {
@@ -820,7 +654,6 @@
Log.d(mTag, "onCarrierNetworkChange: active=" + active);
}
mCurrentState.carrierNetworkChangeMode = active;
-
updateTelephony();
}
@@ -830,7 +663,16 @@
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 4f382e7..9003de1 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.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.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.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 @@
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 @@
@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 @@
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 @@
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 749b56c..3a45691 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 @@
boolean enabled;
boolean activityIn;
boolean activityOut;
- public boolean activityDormant;
int level;
IconGroup iconGroup;
int inetCondition;
@@ -278,7 +277,6 @@
inetCondition = state.inetCondition;
activityIn = state.activityIn;
activityOut = state.activityOut;
- activityDormant = state.activityDormant;
rssi = state.rssi;
time = state.time;
}
@@ -302,7 +300,6 @@
.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 @@
&& 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 bc24ad0..c66f07dd 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 @@
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 7dad05d..1b62cbf 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.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 @@
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 @@
SystemWindows mSystemWindows;
final Handler mHandler;
+ final TransactionPool mTransactionPool;
final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
@@ -64,9 +66,10 @@
@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 @@
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 @@
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 @@
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 64b0b66..4652abf 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.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 @@
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 044a2a6..23df991 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 @@
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 @@
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 44454d9..e5ab9be 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 @@
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/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 40075c8..02bfc19e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -45,7 +45,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class ControlsBindingControllerTest : SysuiTestCase() {
+class ControlsBindingControllerImplTest : SysuiTestCase() {
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -95,7 +95,7 @@
assertEquals(1, providers.size)
val provider = providers.first()
- verify(provider).maybeBindAndLoad(callback)
+ verify(provider).maybeBindAndLoad(any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index ddd6b12..a3e59e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -19,7 +19,6 @@
import android.content.ComponentName
import android.os.UserHandle
import android.service.controls.IControlsActionCallback
-import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
import android.service.controls.IControlsSubscriber
import android.service.controls.actions.ControlAction
@@ -32,13 +31,13 @@
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
-import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
@@ -54,8 +53,6 @@
@Mock
private lateinit var actionCallbackService: IControlsActionCallback.Stub
@Mock
- private lateinit var loadCallbackService: IControlsLoadCallback.Stub
- @Mock
private lateinit var subscriberService: IControlsSubscriber.Stub
@Mock
private lateinit var service: IControlsProvider.Stub
@@ -85,7 +82,6 @@
manager = ControlsProviderLifecycleManager(
context,
executor,
- loadCallbackService,
actionCallbackService,
subscriberService,
UserHandle.of(0),
@@ -113,31 +109,29 @@
@Test
fun testMaybeBindAndLoad() {
- manager.maybeBindAndLoad(loadCallback)
+ manager.maybeBindAndLoad(subscriberService)
- verify(service).load(loadCallbackService)
+ verify(service).load(subscriberService)
assertTrue(mContext.isBound(componentName))
- assertEquals(loadCallback, manager.lastLoadCallback)
}
@Test
fun testMaybeUnbind_bindingAndCallback() {
- manager.maybeBindAndLoad(loadCallback)
+ manager.maybeBindAndLoad(subscriberService)
manager.unbindService()
assertFalse(mContext.isBound(componentName))
- assertNull(manager.lastLoadCallback)
}
@Test
fun testMaybeBindAndLoad_timeout() {
- manager.maybeBindAndLoad(loadCallback)
+ manager.maybeBindAndLoad(subscriberService)
executor.advanceClockToLast()
executor.runAllReady()
- verify(loadCallback).error(anyString())
+ verify(subscriberService).onError(any(), anyString())
}
@Test
@@ -160,4 +154,4 @@
eq(actionCallbackService))
assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index 9e7ce06..cd82844 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -18,7 +18,6 @@
import android.os.RemoteException
import android.service.controls.IControlsActionCallback
-import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
@@ -56,9 +55,6 @@
private lateinit var subscriber: IControlsSubscriber
@Mock
- private lateinit var loadCallback: IControlsLoadCallback
-
- @Mock
private lateinit var actionCallback: IControlsActionCallback
@Captor
@@ -81,16 +77,16 @@
@Test
fun testLoad_happyPath() {
- val result = wrapper.load(loadCallback)
+ val result = wrapper.load(subscriber)
assertTrue(result)
- verify(service).load(loadCallback)
+ verify(service).load(subscriber)
}
@Test
fun testLoad_error() {
`when`(service.load(any())).thenThrow(exception)
- val result = wrapper.load(loadCallback)
+ val result = wrapper.load(subscriber)
assertFalse(result)
}
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 0000000..7f48cd1
--- /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 e5ee439..d81b8c2 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.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 @@
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 9a0e97a..a0d551c 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.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 @@
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 @@
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
+ mDisplayInfo = mock(DisplayInfo.class);
mFakeRegInfo = new NetworkRegistrationInfo.Builder()
.setTransportType(TRANSPORT_TYPE_WWAN)
@@ -167,6 +170,9 @@
.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 @@
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 @@
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 @@
.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 a906d9f..3eb0c44 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 @@
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 org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -180,254 +175,6 @@
}
@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 @@
.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 @@
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/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3b0a1a3..d86b223 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2120,8 +2120,6 @@
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 @@
} 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 @@
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);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ffa7d92..0dc44f7 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.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 @@
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 @@
private boolean mAppDataIsolationEnabled = false;
+ private boolean mVoldAppDataIsolationEnabled = false;
+
private ArrayList<String> mAppDataIsolationWhitelistedApps;
/**
@@ -691,6 +699,8 @@
// 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 @@
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/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3a16217..09e3feb 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 @@
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 @@
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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index cb755f9..df3c83a 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 @@
/**
* 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 @@
"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 @@
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 @@
* {@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 @@
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 e6eaf21..9c945d5 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 @@
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 @@
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 @@
}
}
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 491c5ab..da3cbf9 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.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 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.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 @@
/** 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 @@
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
+ mTemperatureWatcher.updateSevereThresholds();
mHalReady.set(true);
}
}
@@ -462,6 +472,15 @@
}
}
+ @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 @@
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 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -857,6 +886,12 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -975,6 +1010,32 @@
}
@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 @@
}
}
+ 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/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 46596e35..688f474 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_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 @@
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 @@
// 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 @@
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 @@
? ((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 @@
}
@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 @@
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 @@
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 7720f7f..70cd01b 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 @@
// 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 @@
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 132e486..882d5c7 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_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.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 @@
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 @@
}
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 @@
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 @@
}
/**
+ * 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 @@
}
/**
- * 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 840abb1..e60ab3f 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_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.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_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.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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
}
}
- /** @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 @@
}
} else {
mRootSplitScreenPrimaryTask = stack;
- mDisplayContent.onSplitScreenModeActivated();
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
}
@@ -4523,11 +4478,6 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 1a0dcb9..64c5faa 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 @@
}
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 872379e..6431e11 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 @@
* @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 00947d7..44034ed 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_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 @@
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 251d0f1..e923e64 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_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.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 @@
}
// 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 @@
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
mTargetActivityRecord.token);
}
+ if (mTargetActivityRecord.hasFixedRotationTransform()) {
+ mTargetActivityRecord.clearFixedRotationTransform();
+ }
}
// Notify that the animation has ended
@@ -828,6 +831,19 @@
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 @@
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 34b5c11..c7f2cc7 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 @@
// 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 @@
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 4b13a0c..4d5621c 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 @@
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 369db05..74d5c33 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.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 @@
// 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 @@
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 d23bf97..1e22141 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 @@
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 2a7d551..68b8348 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 @@
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 1cfd0d4..b250083 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 @@
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 1180566..48c7812 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 @@
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 9b85a7b..eff222a 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 @@
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 731cd1e..b239b684 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_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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 2a78b6f..2c84f26 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.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 @@
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 69ca643..ae8d554 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.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 @@
@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 @@
.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 4123556..85b8fcb 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 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 @@
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 @@
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 e90cb46..ac0cac1 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.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 @@
@Mock
private IPowerManager mMockIPowerManager;
@Mock
+ private IThermalService mMockIThermalService;
+ @Mock
Context mContext;
@Before
@@ -84,7 +87,7 @@
// 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 9574a08..40b0e71 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.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.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 @@
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 @@
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 3a8258b..853151f 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 @@
}
@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 37ce510..dbf2f14 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_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 @@
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 @@
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 @@
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 @@
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 9e98427..fa19814 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.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 @@
private HdmiPortInfo[] mHdmiPortInfo;
@Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -130,7 +132,8 @@
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/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index df2b3ef..6c769485 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 @@
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 ccf7ca9..624cb83 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.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 @@
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
+ private IThermalService mIThermalServiceMock;
+ @Mock
private IThermalEventListener mEventListener1;
@Mock
private IThermalEventListener mEventListener2;
@@ -133,6 +137,12 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
return true;
}
@@ -153,7 +163,7 @@
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 d5cdbeb..035a2f1 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.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 @@
private RecoverySystemService.UncryptSocket mUncryptSocket;
private Context mContext;
private IPowerManager mIPowerManager;
+ private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
@@ -77,8 +79,9 @@
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/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c110a0c..b917e1b 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_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.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.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 @@
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 @@
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 a672a95..4449069 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 @@
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 7753a32..6a8917c 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_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.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 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 @@
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/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 0312df6..cd53ece 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_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 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.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 @@
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 795de57..9924839c 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.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 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 @@
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());
}
/**