Merge "Return the previous value when adding a new one."
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index 9d92e0f..b8857ec 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -1,5 +1,9 @@
# OWNERS of Multiuser related files
+annabauza@google.com
bookatz@google.com
+nykkumar@google.com
olilan@google.com
omakoto@google.com
+tetianameronyk@google.com
+tyk@google.com
yamasani@google.com
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index c3fc7b1..be0e025 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -235,6 +235,15 @@
public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
// TODO: Add JobScheduler modifier keys
/** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_INSTANT =
+ "js_reward_app_install_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_ONGOING =
+ "js_reward_app_install_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_MAX =
+ "js_reward_app_install_max";
+ /** @hide */
public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
"js_reward_top_activity_instant";
/** @hide */
@@ -463,6 +472,12 @@
public static final long DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000);
// TODO: add JobScheduler modifier default values
/** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES = arcToCake(408);
+ /** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES = arcToCake(0);
+ /** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES = arcToCake(4000);
+ /** @hide */
public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0);
/** @hide */
public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES = CAKE_IN_ARC / 2;
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 40d1b4c..bd475e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -37,6 +37,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -993,69 +994,69 @@
"pre_idle_factor_short";
private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
- private static final long DEFAULT_FLEX_TIME_SHORT =
+ private long mDefaultFlexTimeShort =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ private long mDefaultLightIdleAfterInactiveTimeout =
!COMPRESS_TIME ? 4 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
+ private long mDefaultLightIdleTimeout =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
- private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
- private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
+ private float mDefaultLightIdleFactor = 2f;
+ private long mDefaultLightMaxIdleTimeout =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+ private long mDefaultLightIdleMaintenanceMinBudget =
!COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+ private long mDefaultLightIdleMaintenanceMaxBudget =
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_MIN_LIGHT_MAINTENANCE_TIME =
+ private long mDefaultMinLightMaintenanceTime =
!COMPRESS_TIME ? 5 * 1000L : 1 * 1000L;
- private static final long DEFAULT_MIN_DEEP_MAINTENANCE_TIME =
+ private long mDefaultMinDeepMaintenanceTime =
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L;
- private static final long DEFAULT_INACTIVE_TIMEOUT =
+ private long mDefaultInactiveTimeout =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY =
(15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
- private static final long DEFAULT_SENSING_TIMEOUT =
+ private long mDefaultSensingTimeout =
!COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_LOCATING_TIMEOUT =
+ private long mDefaultLocatingTimeout =
!COMPRESS_TIME ? 30 * 1000L : 15 * 1000L;
- private static final float DEFAULT_LOCATION_ACCURACY = 20f;
- private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
+ private float mDefaultLocationAccuracy = 20f;
+ private long mDefaultMotionInactiveTimeout =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+ private long mDefaultMotionInactiveTimeoutFlex =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ private long mDefaultIdleAfterInactiveTimeout =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
(15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
- private static final long DEFAULT_IDLE_PENDING_TIMEOUT =
+ private long mDefaultIdlePendingTimeout =
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT =
+ private long mDefaultMaxIdlePendingTimeout =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
- private static final float DEFAULT_IDLE_PENDING_FACTOR = 2f;
- private static final long DEFAULT_QUICK_DOZE_DELAY_TIMEOUT =
+ private float mDefaultIdlePendingFactor = 2f;
+ private long mDefaultQuickDozeDelayTimeout =
!COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_IDLE_TIMEOUT =
+ private long mDefaultIdleTimeout =
!COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L;
- private static final long DEFAULT_MAX_IDLE_TIMEOUT =
+ private long mDefaultMaxIdleTimeout =
!COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L;
- private static final float DEFAULT_IDLE_FACTOR = 2f;
- private static final long DEFAULT_MIN_TIME_TO_ALARM =
+ private float mDefaultIdleFactor = 2f;
+ private long mDefaultMinTimeToAlarm =
!COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L;
- private static final long DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = 5 * 60 * 1000L;
- private static final long DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = 60 * 1000L;
- private static final long DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = 20 * 1000L;
- private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L;
- private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
- private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
- private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
- private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+ private long mDefaultMaxTempAppAllowlistDurationMs = 5 * 60 * 1000L;
+ private long mDefaultMmsTempAppAllowlistDurationMs = 60 * 1000L;
+ private long mDefaultSmsTempAppAllowlistDurationMs = 20 * 1000L;
+ private long mDefaultNotificationAllowlistDurationMs = 30 * 1000L;
+ private boolean mDefaultWaitForUnlock = true;
+ private float mDefaultPreIdleFactorLong = 1.67f;
+ private float mDefaultPreIdleFactorShort = .33f;
+ private boolean mDefaultUseWindowAlarms = true;
/**
* A somewhat short alarm window size that we will tolerate for various alarm timings.
*
* @see #KEY_FLEX_TIME_SHORT
*/
- public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
+ public long FLEX_TIME_SHORT = mDefaultFlexTimeShort;
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -1063,28 +1064,28 @@
*
* @see #KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
*/
- public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
/**
* This is the initial time that we will run in light idle maintenance mode.
*
* @see #KEY_LIGHT_IDLE_TIMEOUT
*/
- public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
+ public long LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
/**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
* @see #KEY_LIGHT_IDLE_FACTOR
*/
- public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
+ public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
/**
* This is the maximum time we will stay in light idle mode.
*
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
- public long LIGHT_MAX_IDLE_TIMEOUT = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT;
+ public long LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
/**
* This is the minimum amount of time we want to make available for maintenance mode
@@ -1093,7 +1094,7 @@
*
* @see #KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
*/
- public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
/**
* This is the maximum amount of time we want to make available for maintenance mode
@@ -1104,7 +1105,7 @@
*
* @see #KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
*/
- public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+ public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
/**
* This is the minimum amount of time that we will stay in maintenance mode after
@@ -1115,7 +1116,7 @@
*
* @see #KEY_MIN_LIGHT_MAINTENANCE_TIME
*/
- public long MIN_LIGHT_MAINTENANCE_TIME = DEFAULT_MIN_LIGHT_MAINTENANCE_TIME;
+ public long MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
/**
* This is the minimum amount of time that we will stay in maintenance mode after
@@ -1125,7 +1126,7 @@
* mode immediately.
* @see #KEY_MIN_DEEP_MAINTENANCE_TIME
*/
- public long MIN_DEEP_MAINTENANCE_TIME = DEFAULT_MIN_DEEP_MAINTENANCE_TIME;
+ public long MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
/**
* This is the time, after becoming inactive, at which we start looking at the
@@ -1134,7 +1135,7 @@
* the motion sensor whenever the screen is off.
* @see #KEY_INACTIVE_TIMEOUT
*/
- public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
+ public long INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
/**
* If we don't receive a callback from AnyMotion in this amount of time +
@@ -1143,14 +1144,14 @@
* will be ignored.
* @see #KEY_SENSING_TIMEOUT
*/
- public long SENSING_TIMEOUT = DEFAULT_SENSING_TIMEOUT;
+ public long SENSING_TIMEOUT = mDefaultSensingTimeout;
/**
* This is how long we will wait to try to get a good location fix before going in to
* idle mode.
* @see #KEY_LOCATING_TIMEOUT
*/
- public long LOCATING_TIMEOUT = DEFAULT_LOCATING_TIMEOUT;
+ public long LOCATING_TIMEOUT = mDefaultLocatingTimeout;
/**
* The desired maximum accuracy (in meters) we consider the location to be good enough to go
@@ -1158,7 +1159,7 @@
* {@link #LOCATING_TIMEOUT} expires.
* @see #KEY_LOCATION_ACCURACY
*/
- public float LOCATION_ACCURACY = DEFAULT_LOCATION_ACCURACY;
+ public float LOCATION_ACCURACY = mDefaultLocationAccuracy;
/**
* This is the time, after seeing motion, that we wait after becoming inactive from
@@ -1166,14 +1167,14 @@
*
* @see #KEY_MOTION_INACTIVE_TIMEOUT
*/
- public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+ public long MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
/**
* This is the alarm window size we will tolerate for motion detection timings.
*
* @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
*/
- public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+ public long MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
@@ -1181,7 +1182,7 @@
*
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
- public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ public long IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
/**
* This is the initial time, after being idle, that we will allow ourself to be back
@@ -1189,20 +1190,20 @@
* idle.
* @see #KEY_IDLE_PENDING_TIMEOUT
*/
- public long IDLE_PENDING_TIMEOUT = DEFAULT_IDLE_PENDING_TIMEOUT;
+ public long IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
/**
* Maximum pending idle timeout (time spent running) we will be allowed to use.
* @see #KEY_MAX_IDLE_PENDING_TIMEOUT
*/
- public long MAX_IDLE_PENDING_TIMEOUT = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
+ public long MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
/**
* Scaling factor to apply to current pending idle timeout each time we cycle through
* that state.
* @see #KEY_IDLE_PENDING_FACTOR
*/
- public float IDLE_PENDING_FACTOR = DEFAULT_IDLE_PENDING_FACTOR;
+ public float IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
/**
* This is amount of time we will wait from the point where we go into
@@ -1210,33 +1211,33 @@
* and other current activity to finish.
* @see #KEY_QUICK_DOZE_DELAY_TIMEOUT
*/
- public long QUICK_DOZE_DELAY_TIMEOUT = DEFAULT_QUICK_DOZE_DELAY_TIMEOUT;
+ public long QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
/**
* This is the initial time that we want to sit in the idle state before waking up
* again to return to pending idle and allowing normal work to run.
* @see #KEY_IDLE_TIMEOUT
*/
- public long IDLE_TIMEOUT = DEFAULT_IDLE_TIMEOUT;
+ public long IDLE_TIMEOUT = mDefaultIdleTimeout;
/**
* Maximum idle duration we will be allowed to use.
* @see #KEY_MAX_IDLE_TIMEOUT
*/
- public long MAX_IDLE_TIMEOUT = DEFAULT_MAX_IDLE_TIMEOUT;
+ public long MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
/**
* Scaling factor to apply to current idle timeout each time we cycle through that state.
* @see #KEY_IDLE_FACTOR
*/
- public float IDLE_FACTOR = DEFAULT_IDLE_FACTOR;
+ public float IDLE_FACTOR = mDefaultIdleFactor;
/**
* This is the minimum time we will allow until the next upcoming alarm for us to
* actually go in to idle mode.
* @see #KEY_MIN_TIME_TO_ALARM
*/
- public long MIN_TIME_TO_ALARM = DEFAULT_MIN_TIME_TO_ALARM;
+ public long MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
/**
* Max amount of time to temporarily whitelist an app when it receives a high priority
@@ -1244,48 +1245,49 @@
*
* @see #KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is receiving an MMS.
* @see #KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is receiving an SMS.
* @see #KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
* @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
*/
- public long NOTIFICATION_ALLOWLIST_DURATION_MS = DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS;
+ public long NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
/**
* Pre idle time factor use to make idle delay longer
*/
- public float PRE_IDLE_FACTOR_LONG = DEFAULT_PRE_IDLE_FACTOR_LONG;
+ public float PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
/**
* Pre idle time factor use to make idle delay shorter
*/
- public float PRE_IDLE_FACTOR_SHORT = DEFAULT_PRE_IDLE_FACTOR_SHORT;
+ public float PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
- public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
+ public boolean WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
/**
* Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
* False to use the legacy inexact alarms (call AlarmManager.set()).
*/
- public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+ public boolean USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
private final boolean mSmallBatteryDevice;
public Constants() {
+ initDefault();
mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
if (mSmallBatteryDevice) {
INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY;
@@ -1297,6 +1299,132 @@
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
}
+ private void initDefault() {
+ final Resources res = getContext().getResources();
+
+ mDefaultFlexTimeShort = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_flex_time_short_ms),
+ mDefaultFlexTimeShort);
+ mDefaultLightIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_after_inactive_to_ms),
+ mDefaultLightIdleAfterInactiveTimeout);
+ mDefaultLightIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_light_idle_to_ms),
+ mDefaultLightIdleTimeout);
+ mDefaultLightIdleFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_light_idle_factor);
+ mDefaultLightMaxIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms),
+ mDefaultLightMaxIdleTimeout);
+ mDefaultLightIdleMaintenanceMinBudget = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_idle_maintenance_min_budget_ms
+ ), mDefaultLightIdleMaintenanceMinBudget);
+ mDefaultLightIdleMaintenanceMaxBudget = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_idle_maintenance_max_budget_ms
+ ), mDefaultLightIdleMaintenanceMaxBudget);
+ mDefaultMinLightMaintenanceTime = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_light_maintenance_time_ms),
+ mDefaultMinLightMaintenanceTime);
+ mDefaultMinDeepMaintenanceTime = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_deep_maintenance_time_ms),
+ mDefaultMinDeepMaintenanceTime);
+ mDefaultInactiveTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_inactive_to_ms),
+ mDefaultInactiveTimeout);
+ mDefaultSensingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_sensing_to_ms),
+ mDefaultSensingTimeout);
+ mDefaultLocatingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_locating_to_ms),
+ mDefaultLocatingTimeout);
+ mDefaultLocationAccuracy = res.getFloat(
+ com.android.internal.R.integer.device_idle_location_accuracy);
+ mDefaultMotionInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_motion_inactive_to_ms),
+ mDefaultMotionInactiveTimeout);
+ mDefaultMotionInactiveTimeoutFlex = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_motion_inactive_to_flex_ms),
+ mDefaultMotionInactiveTimeoutFlex);
+ mDefaultIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_idle_after_inactive_to_ms),
+ mDefaultIdleAfterInactiveTimeout);
+ mDefaultIdlePendingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_idle_pending_to_ms),
+ mDefaultIdlePendingTimeout);
+ mDefaultMaxIdlePendingTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_max_idle_pending_to_ms),
+ mDefaultMaxIdlePendingTimeout);
+ mDefaultIdlePendingFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_idle_pending_factor);
+ mDefaultQuickDozeDelayTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_quick_doze_delay_to_ms),
+ mDefaultQuickDozeDelayTimeout);
+ mDefaultIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_idle_to_ms),
+ mDefaultIdleTimeout);
+ mDefaultMaxIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_max_idle_to_ms),
+ mDefaultMaxIdleTimeout);
+ mDefaultIdleFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_idle_factor);
+ mDefaultMinTimeToAlarm = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_time_to_alarm_ms),
+ mDefaultMinTimeToAlarm);
+ mDefaultMaxTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_max_temp_app_allowlist_duration_ms);
+ mDefaultMmsTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_mms_temp_app_allowlist_duration_ms);
+ mDefaultSmsTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_sms_temp_app_allowlist_duration_ms);
+ mDefaultNotificationAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_notification_allowlist_duration_ms);
+ mDefaultWaitForUnlock = res.getBoolean(
+ com.android.internal.R.bool.device_idle_wait_for_unlock);
+ mDefaultPreIdleFactorLong = res.getFloat(
+ com.android.internal.R.integer.device_idle_pre_idle_factor_long);
+ mDefaultPreIdleFactorShort = res.getFloat(
+ com.android.internal.R.integer.device_idle_pre_idle_factor_short);
+ mDefaultUseWindowAlarms = res.getBoolean(
+ com.android.internal.R.bool.device_idle_use_window_alarms);
+
+ FLEX_TIME_SHORT = mDefaultFlexTimeShort;
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
+ LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
+ LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
+ LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
+ MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
+ MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
+ INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
+ SENSING_TIMEOUT = mDefaultSensingTimeout;
+ LOCATING_TIMEOUT = mDefaultLocatingTimeout;
+ LOCATION_ACCURACY = mDefaultLocationAccuracy;
+ MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
+ MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
+ IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
+ IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
+ MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
+ IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
+ QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
+ IDLE_TIMEOUT = mDefaultIdleTimeout;
+ MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
+ IDLE_FACTOR = mDefaultIdleFactor;
+ MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
+ MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
+ MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
+ SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
+ NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
+ WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
+ PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
+ PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
+ USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
+ }
+
+ private long getTimeout(long defTimeout, long compTimeout) {
+ return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
+ }
+
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -1308,147 +1436,147 @@
switch (name) {
case KEY_FLEX_TIME_SHORT:
FLEX_TIME_SHORT = properties.getLong(
- KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+ KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
break;
case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+ mDefaultLightIdleAfterInactiveTimeout);
break;
case KEY_LIGHT_IDLE_TIMEOUT:
LIGHT_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
+ KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
break;
case KEY_LIGHT_IDLE_FACTOR:
LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
+ KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
break;
case KEY_LIGHT_MAX_IDLE_TIMEOUT:
LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT, DEFAULT_LIGHT_MAX_IDLE_TIMEOUT);
+ KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
break;
case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
- DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+ mDefaultLightIdleMaintenanceMinBudget);
break;
case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
- DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET);
+ mDefaultLightIdleMaintenanceMaxBudget);
break;
case KEY_MIN_LIGHT_MAINTENANCE_TIME:
MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
KEY_MIN_LIGHT_MAINTENANCE_TIME,
- DEFAULT_MIN_LIGHT_MAINTENANCE_TIME);
+ mDefaultMinLightMaintenanceTime);
break;
case KEY_MIN_DEEP_MAINTENANCE_TIME:
MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
- DEFAULT_MIN_DEEP_MAINTENANCE_TIME);
+ mDefaultMinDeepMaintenanceTime);
break;
case KEY_INACTIVE_TIMEOUT:
final long defaultInactiveTimeout = mSmallBatteryDevice
? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
- : DEFAULT_INACTIVE_TIMEOUT;
+ : mDefaultInactiveTimeout;
INACTIVE_TIMEOUT = properties.getLong(
KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
break;
case KEY_SENSING_TIMEOUT:
SENSING_TIMEOUT = properties.getLong(
- KEY_SENSING_TIMEOUT, DEFAULT_SENSING_TIMEOUT);
+ KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
break;
case KEY_LOCATING_TIMEOUT:
LOCATING_TIMEOUT = properties.getLong(
- KEY_LOCATING_TIMEOUT, DEFAULT_LOCATING_TIMEOUT);
+ KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
break;
case KEY_LOCATION_ACCURACY:
LOCATION_ACCURACY = properties.getFloat(
- KEY_LOCATION_ACCURACY, DEFAULT_LOCATION_ACCURACY);
+ KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
break;
case KEY_MOTION_INACTIVE_TIMEOUT:
MOTION_INACTIVE_TIMEOUT = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
+ KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
break;
case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
- DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+ mDefaultMotionInactiveTimeoutFlex);
break;
case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
- : DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ : mDefaultIdleAfterInactiveTimeout;
IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
defaultIdleAfterInactiveTimeout);
break;
case KEY_IDLE_PENDING_TIMEOUT:
IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_IDLE_PENDING_TIMEOUT, DEFAULT_IDLE_PENDING_TIMEOUT);
+ KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
break;
case KEY_MAX_IDLE_PENDING_TIMEOUT:
MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_PENDING_TIMEOUT, DEFAULT_MAX_IDLE_PENDING_TIMEOUT);
+ KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
break;
case KEY_IDLE_PENDING_FACTOR:
IDLE_PENDING_FACTOR = properties.getFloat(
- KEY_IDLE_PENDING_FACTOR, DEFAULT_IDLE_PENDING_FACTOR);
+ KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
break;
case KEY_QUICK_DOZE_DELAY_TIMEOUT:
QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
- KEY_QUICK_DOZE_DELAY_TIMEOUT, DEFAULT_QUICK_DOZE_DELAY_TIMEOUT);
+ KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
break;
case KEY_IDLE_TIMEOUT:
IDLE_TIMEOUT = properties.getLong(
- KEY_IDLE_TIMEOUT, DEFAULT_IDLE_TIMEOUT);
+ KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
break;
case KEY_MAX_IDLE_TIMEOUT:
MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_TIMEOUT, DEFAULT_MAX_IDLE_TIMEOUT);
+ KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
break;
case KEY_IDLE_FACTOR:
- IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, DEFAULT_IDLE_FACTOR);
+ IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, mDefaultIdleFactor);
break;
case KEY_MIN_TIME_TO_ALARM:
MIN_TIME_TO_ALARM = properties.getLong(
- KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM);
+ KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
break;
case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultMaxTempAppAllowlistDurationMs);
break;
case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultMmsTempAppAllowlistDurationMs);
break;
case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultSmsTempAppAllowlistDurationMs);
break;
case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
- DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS);
+ mDefaultNotificationAllowlistDurationMs);
break;
case KEY_WAIT_FOR_UNLOCK:
WAIT_FOR_UNLOCK = properties.getBoolean(
- KEY_WAIT_FOR_UNLOCK, DEFAULT_WAIT_FOR_UNLOCK);
+ KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
break;
case KEY_PRE_IDLE_FACTOR_LONG:
PRE_IDLE_FACTOR_LONG = properties.getFloat(
- KEY_PRE_IDLE_FACTOR_LONG, DEFAULT_PRE_IDLE_FACTOR_LONG);
+ KEY_PRE_IDLE_FACTOR_LONG, mDefaultPreIdleFactorLong);
break;
case KEY_PRE_IDLE_FACTOR_SHORT:
PRE_IDLE_FACTOR_SHORT = properties.getFloat(
- KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
+ KEY_PRE_IDLE_FACTOR_SHORT, mDefaultPreIdleFactorShort);
break;
case KEY_USE_WINDOW_ALARMS:
USE_WINDOW_ALARMS = properties.getBoolean(
- KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+ KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
break;
default:
Slog.e(TAG, "Unknown configuration key: " + name);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 3bbc5a3..d284a99 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -69,6 +69,11 @@
*/
private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
+ @GuardedBy("mLock")
+ private Boolean mLastReportedStatsdBatteryNotLow = null;
+ @GuardedBy("mLock")
+ private Boolean mLastReportedStatsdStablePower = null;
+
public BatteryController(JobSchedulerService service,
FlexibilityController flexibilityController) {
super(service);
@@ -176,12 +181,25 @@
Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+ powerConnected + "/" + stablePower + "/" + batteryNotLow);
}
+
+ if (mLastReportedStatsdStablePower == null
+ || mLastReportedStatsdStablePower != stablePower) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_CHARGING, stablePower);
+ mLastReportedStatsdStablePower = stablePower;
+ }
+ if (mLastReportedStatsdBatteryNotLow == null
+ || mLastReportedStatsdBatteryNotLow != stablePower) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_BATTERY_NOT_LOW,
+ batteryNotLow);
+ mLastReportedStatsdBatteryNotLow = batteryNotLow;
+ }
+
final long nowElapsed = sElapsedRealtimeClock.millis();
mFlexibilityController.setConstraintSatisfied(
JobStatus.CONSTRAINT_CHARGING, mService.isBatteryCharging(), nowElapsed);
mFlexibilityController.setConstraintSatisfied(
- JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
+ JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index f6de109..abbe177 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -153,6 +153,8 @@
changed = true;
}
mDeviceIdleMode = enabled;
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_DEVICE_NOT_DOZING,
+ !mDeviceIdleMode);
if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
mDeviceIdleUpdateFunctor.prepare();
if (enabled) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index dd06217..926cfc1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -96,6 +96,8 @@
@Override
public void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_IDLE, isIdle);
+
final long nowElapsed = sElapsedRealtimeClock.millis();
mFlexibilityController.setConstraintSatisfied(
JobStatus.CONSTRAINT_IDLE, isIdle, nowElapsed);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 4320db0..999a3c0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -170,13 +170,12 @@
*/
private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
| CONSTRAINT_DEADLINE
- | CONSTRAINT_IDLE
| CONSTRAINT_PREFETCH
| CONSTRAINT_TARE_WEALTH
| CONSTRAINT_TIMING_DELAY
| CONSTRAINT_WITHIN_QUOTA;
- // TODO(b/129954980)
+ // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
private static final boolean STATS_LOG_ENABLED = false;
// No override.
@@ -1982,7 +1981,7 @@
}
/** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
- private int getProtoConstraint(int constraint) {
+ static int getProtoConstraint(int constraint) {
switch (constraint) {
case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
@@ -1998,10 +1997,16 @@
return JobServerProtoEnums.CONSTRAINT_DEADLINE;
case CONSTRAINT_DEVICE_NOT_DOZING:
return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
+ case CONSTRAINT_FLEXIBLE:
+ return JobServerProtoEnums.CONSTRAINT_FLEXIBILITY;
case CONSTRAINT_IDLE:
return JobServerProtoEnums.CONSTRAINT_IDLE;
+ case CONSTRAINT_PREFETCH:
+ return JobServerProtoEnums.CONSTRAINT_PREFETCH;
case CONSTRAINT_STORAGE_NOT_LOW:
return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
+ case CONSTRAINT_TARE_WEALTH:
+ return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH;
case CONSTRAINT_TIMING_DELAY:
return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
case CONSTRAINT_WITHIN_QUOTA:
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 2a2d602..8453e53 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -26,6 +26,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
import com.android.server.job.StateChangedListener;
@@ -165,6 +166,15 @@
return mService.areComponentsInPlaceLocked(jobStatus);
}
+ protected void logDeviceWideConstraintStateToStatsd(int constraint, boolean satisfied) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED,
+ JobStatus.getProtoConstraint(constraint),
+ satisfied
+ ? FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
+ : FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
+ }
+
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 4ba6957..7391bcf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -55,12 +55,13 @@
static final int TYPE_ACTION = 1 << SHIFT_TYPE;
static final int TYPE_REWARD = 2 << SHIFT_TYPE;
- private static final int SHIFT_POLICY = 29;
- static final int MASK_POLICY = 0b1 << SHIFT_POLICY;
- static final int POLICY_AM = 0 << SHIFT_POLICY;
- static final int POLICY_JS = 1 << SHIFT_POLICY;
+ private static final int SHIFT_POLICY = 28;
+ static final int MASK_POLICY = 0b11 << SHIFT_POLICY;
+ // Reserve 0 for the base/common policy.
+ static final int POLICY_AM = 1 << SHIFT_POLICY;
+ static final int POLICY_JS = 2 << SHIFT_POLICY;
- static final int MASK_EVENT = ~0 - (0b111 << SHIFT_POLICY);
+ static final int MASK_EVENT = -1 ^ (MASK_TYPE | MASK_POLICY);
static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0;
static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1;
@@ -119,6 +120,7 @@
REWARD_NOTIFICATION_INTERACTION,
REWARD_WIDGET_INTERACTION,
REWARD_OTHER_USER_INTERACTION,
+ JobSchedulerEconomicPolicy.REWARD_APP_INSTALL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UtilityReward {
@@ -429,6 +431,8 @@
return "REWARD_WIDGET_INTERACTION";
case REWARD_OTHER_USER_INTERACTION:
return "REWARD_OTHER_USER_INTERACTION";
+ case JobSchedulerEconomicPolicy.REWARD_APP_INSTALL:
+ return "REWARD_JOB_APP_INSTALL";
}
return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
index da544bb..fcb3e67 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
@@ -17,9 +17,12 @@
package com.android.server.tare;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
-import android.os.UserHandle;
+import android.os.RemoteException;
/** POJO to cache only the information about installed packages that TARE cares about. */
class InstalledPackageInfo {
@@ -28,11 +31,21 @@
public final int uid;
public final String packageName;
public final boolean hasCode;
+ @Nullable
+ public final String installerPackageName;
InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- this.uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
- this.packageName = packageInfo.packageName;
- this.hasCode = applicationInfo != null && applicationInfo.hasCode();
+ uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
+ packageName = packageInfo.packageName;
+ hasCode = applicationInfo != null && applicationInfo.hasCode();
+ InstallSourceInfo installSourceInfo = null;
+ try {
+ installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ installerPackageName =
+ installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 59d4ded..c13e1dd9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -524,10 +524,15 @@
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- mPkgCache.add(userId, pkgName, new InstalledPackageInfo(packageInfo));
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
+ mPkgCache.add(userId, pkgName, ipo);
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
+ if (ipo.installerPackageName != null) {
+ mAgent.noteInstantaneousEventLocked(userId, ipo.installerPackageName,
+ JobSchedulerEconomicPolicy.REWARD_APP_INSTALL, null);
+ }
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index cbb88c0..55cc352 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -43,6 +43,9 @@
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES;
@@ -85,6 +88,9 @@
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
@@ -137,6 +143,8 @@
public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9;
public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10;
+ public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JS | 0;
+
private static final int[] COST_MODIFIERS = new int[]{
COST_MODIFIER_CHARGING,
COST_MODIFIER_DEVICE_IDLE,
@@ -374,6 +382,17 @@
getConstantAsCake(mParser, properties,
KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
+ mRewards.put(REWARD_APP_INSTALL,
+ new Reward(REWARD_APP_INSTALL,
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_INSTANT,
+ DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_ONGOING,
+ DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_MAX,
+ DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
}
@Override
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 2cda57d..8b8d361 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -18,24 +18,22 @@
#include "com_android_commands_hid_Device.h"
-#include <linux/uhid.h>
-
+#include <android-base/stringprintf.h>
+#include <android/looper.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <unistd.h>
-#include <cstdio>
-#include <cstring>
-#include <memory>
-
-#include <android/looper.h>
#include <jni.h>
+#include <linux/uhid.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <unistd.h>
-#include <android-base/stringprintf.h>
+#include <cstdio>
+#include <cstring>
+#include <memory>
// Log debug messages about the output.
static constexpr bool DEBUG_OUTPUT = false;
@@ -109,15 +107,15 @@
void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
JNIEnv* env = getJNIEnv();
- env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport,
- requestId, reportId);
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport, requestId,
+ reportId);
checkAndClearException(env, "onDeviceGetReport");
}
-void DeviceCallback::onDeviceSetReport(uint8_t rType,
- const std::vector<uint8_t>& data) {
+void DeviceCallback::onDeviceSetReport(uint32_t id, uint8_t rType,
+ const std::vector<uint8_t>& data) {
JNIEnv* env = getJNIEnv();
- env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType,
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, id, rType,
toJbyteArray(env, data).get());
checkAndClearException(env, "onDeviceSetReport");
}
@@ -236,6 +234,14 @@
writeEvent(mFd, ev, "UHID_GET_REPORT_REPLY");
}
+void Device::sendSetReportReply(uint32_t id, bool success) const {
+ struct uhid_event ev = {};
+ ev.type = UHID_SET_REPORT_REPLY;
+ ev.u.set_report_reply.id = id;
+ ev.u.set_report_reply.err = success ? 0 : EIO;
+ writeEvent(mFd, ev, "UHID_SET_REPORT_REPLY");
+}
+
int Device::handleEvents(int events) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
ALOGE("uhid node was closed or an error occurred. events=0x%x", events);
@@ -249,7 +255,6 @@
mDeviceCallback->onDeviceError();
return 0;
}
-
switch (ev.type) {
case UHID_OPEN: {
mDeviceCallback->onDeviceOpen();
@@ -271,7 +276,7 @@
ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
set_report.rnum, toString(data).c_str());
}
- mDeviceCallback->onDeviceSetReport(set_report.rtype, data);
+ mDeviceCallback->onDeviceSetReport(set_report.id, set_report.rtype, data);
break;
}
case UHID_OUTPUT: {
@@ -347,6 +352,15 @@
}
}
+static void sendSetReportReply(JNIEnv*, jclass /* clazz */, jlong ptr, jint id, jboolean success) {
+ uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
+ if (d) {
+ d->sendSetReportReply(id, success);
+ } else {
+ ALOGE("Could not send set report reply, Device* is null!");
+ }
+}
+
static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
if (d) {
@@ -362,6 +376,7 @@
{"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
{"nativeSendGetFeatureReportReply", "(JI[B)V",
reinterpret_cast<void*>(sendGetFeatureReportReply)},
+ {"nativeSendSetReportReply", "(JIZ)V", reinterpret_cast<void*>(sendSetReportReply)},
{"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)},
};
@@ -376,7 +391,7 @@
uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
uhid::gDeviceCallbackClassInfo.onDeviceSetReport =
- env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V");
+ env->GetMethodID(clazz, "onDeviceSetReport", "(IB[B)V");
uhid::gDeviceCallbackClassInfo.onDeviceOutput =
env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index d10a9aa..9c6060d 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#include <memory>
-#include <vector>
-
+#include <android-base/unique_fd.h>
#include <jni.h>
-#include <android-base/unique_fd.h>
+#include <memory>
+#include <vector>
namespace android {
namespace uhid {
@@ -31,7 +30,7 @@
void onDeviceOpen();
void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
- void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data);
+ void onDeviceSetReport(uint32_t id, uint8_t rType, const std::vector<uint8_t>& data);
void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
void onDeviceError();
@@ -50,9 +49,9 @@
~Device();
void sendReport(const std::vector<uint8_t>& report) const;
+ void sendSetReportReply(uint32_t id, bool success) const;
void sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const;
void close();
-
int handleEvents(int events);
private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 95b1e9a..0415037 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -42,7 +42,8 @@
private static final int MSG_OPEN_DEVICE = 1;
private static final int MSG_SEND_REPORT = 2;
private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3;
- private static final int MSG_CLOSE_DEVICE = 4;
+ private static final int MSG_SEND_SET_REPORT_REPLY = 4;
+ private static final int MSG_CLOSE_DEVICE = 5;
// Sync with linux uhid_event_type::UHID_OUTPUT
private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6;
@@ -56,21 +57,45 @@
private final Map<ByteBuffer, byte[]> mOutputs;
private final OutputStream mOutputStream;
private long mTimeToSend;
-
private final Object mCond = new Object();
+ /**
+ * The report id of the report received in UHID_EVENT_TYPE_SET_REPORT.
+ * Used for SET_REPORT_REPLY.
+ * This field gets overridden each time SET_REPORT is received.
+ */
+ private int mResponseId;
static {
System.loadLibrary("hidcommand_jni");
}
- private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus,
- byte[] descriptor, DeviceCallback callback);
+ private static native long nativeOpenDevice(
+ String name,
+ int id,
+ int vid,
+ int pid,
+ int bus,
+ byte[] descriptor,
+ DeviceCallback callback);
+
private static native void nativeSendReport(long ptr, byte[] data);
+
private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data);
+
+ private static native void nativeSendSetReportReply(long ptr, int id, boolean success);
+
private static native void nativeCloseDevice(long ptr);
- public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor,
- byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
+ public Device(
+ int id,
+ String name,
+ int vid,
+ int pid,
+ int bus,
+ byte[] descriptor,
+ byte[] report,
+ SparseArray<byte[]> featureReports,
+ Map<ByteBuffer, byte[]> outputs) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
@@ -100,6 +125,17 @@
mHandler.sendMessageAtTime(msg, mTimeToSend);
}
+ public void setGetReportResponse(byte[] report) {
+ mFeatureReports.put(report[0], report);
+ }
+
+ public void sendSetReportReply(boolean success) {
+ Message msg =
+ mHandler.obtainMessage(MSG_SEND_SET_REPORT_REPLY, mResponseId, success ? 1 : 0);
+
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
public void addDelay(int delay) {
mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
}
@@ -111,7 +147,8 @@
synchronized (mCond) {
mCond.wait();
}
- } catch (InterruptedException ignore) {}
+ } catch (InterruptedException ignore) {
+ }
}
private class DeviceHandler extends Handler {
@@ -127,8 +164,15 @@
switch (msg.what) {
case MSG_OPEN_DEVICE:
SomeArgs args = (SomeArgs) msg.obj;
- mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3,
- args.argi4, (byte[]) args.arg2, new DeviceCallback());
+ mPtr =
+ nativeOpenDevice(
+ (String) args.arg1,
+ args.argi1,
+ args.argi2,
+ args.argi3,
+ args.argi4,
+ (byte[]) args.arg2,
+ new DeviceCallback());
pauseEvents();
break;
case MSG_SEND_REPORT:
@@ -145,6 +189,14 @@
Log.e(TAG, "Tried to send feature report reply to closed device.");
}
break;
+ case MSG_SEND_SET_REPORT_REPLY:
+ if (mPtr != 0) {
+ final boolean success = msg.arg2 == 1;
+ nativeSendSetReportReply(mPtr, msg.arg1, success);
+ } else {
+ Log.e(TAG, "Tried to send set report reply to closed device.");
+ }
+ break;
case MSG_CLOSE_DEVICE:
if (mPtr != 0) {
nativeCloseDevice(mPtr);
@@ -173,14 +225,18 @@
}
private class DeviceCallback {
+
public void onDeviceOpen() {
mHandler.resumeEvents();
}
public void onDeviceGetReport(int requestId, int reportId) {
if (mFeatureReports == null) {
- Log.e(TAG, "Received GET_REPORT request for reportId=" + reportId
- + ", but 'feature_reports' section is not found");
+ Log.e(
+ TAG,
+ "Received GET_REPORT request for reportId="
+ + reportId
+ + ", but 'feature_reports' section is not found");
return;
}
byte[] report = mFeatureReports.get(reportId);
@@ -220,14 +276,15 @@
} catch (IOException e) {
throw new RuntimeException(e);
}
-
}
// native callback
- public void onDeviceSetReport(byte rtype, byte[] data) {
+ public void onDeviceSetReport(int id, byte rType, byte[] data) {
+ // Used by sendSetReportReply()
+ mResponseId = id;
// We don't need to reply for the SET_REPORT but just send it to HID output for test
// verification.
- sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data);
+ sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rType, data);
}
// native callback
@@ -239,7 +296,8 @@
}
byte[] response = mOutputs.get(ByteBuffer.wrap(data));
if (response == null) {
- Log.i(TAG,
+ Log.i(
+ TAG,
"Requested response for output " + Arrays.toString(data) + " is not found");
return;
}
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index d4bf1d8..3efb797 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -35,6 +35,8 @@
public static final String COMMAND_REGISTER = "register";
public static final String COMMAND_DELAY = "delay";
public static final String COMMAND_REPORT = "report";
+ public static final String COMMAND_SET_GET_REPORT_RESPONSE = "set_get_report_response";
+ public static final String COMMAND_SEND_SET_REPORT_REPLY = "send_set_report_reply";
// These constants come from "include/uapi/linux/input.h" in the kernel
enum Bus {
@@ -62,6 +64,7 @@
private SparseArray<byte[]> mFeatureReports;
private Map<ByteBuffer, byte[]> mOutputs;
private int mDuration;
+ private Boolean mReply;
public int getId() {
return mId;
@@ -107,6 +110,10 @@
return mDuration;
}
+ public Boolean getReply() {
+ return mReply;
+ }
+
public String toString() {
return "Event{id=" + mId
+ ", command=" + String.valueOf(mCommand)
@@ -119,6 +126,7 @@
+ ", feature_reports=" + mFeatureReports.toString()
+ ", outputs=" + mOutputs.toString()
+ ", duration=" + mDuration
+ + ", success=" + mReply.toString()
+ "}";
}
@@ -173,6 +181,10 @@
mEvent.mDuration = duration;
}
+ public void setReply(boolean success) {
+ mEvent.mReply = success;
+ }
+
public Event build() {
if (mEvent.mId == -1) {
throw new IllegalStateException("No event id");
@@ -183,6 +195,16 @@
if (mEvent.mDescriptor == null) {
throw new IllegalStateException("Device registration is missing descriptor");
}
+ }
+ if (COMMAND_SET_GET_REPORT_RESPONSE.equals(mEvent.mCommand)) {
+ if (mEvent.mReport == null) {
+ throw new IllegalStateException("Report command is missing response data");
+ }
+ }
+ if (COMMAND_SEND_SET_REPORT_REPLY.equals(mEvent.mCommand)) {
+ if (mEvent.mReply == null) {
+ throw new IllegalStateException("Reply command is missing reply");
+ }
} else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
if (mEvent.mDuration <= 0) {
throw new IllegalStateException("Delay has missing or invalid duration");
@@ -246,6 +268,9 @@
case "duration":
eb.setDuration(readInt());
break;
+ case "success":
+ eb.setReply(readBool());
+ break;
default:
mReader.skipValue();
}
@@ -292,6 +317,11 @@
return Integer.decode(val);
}
+ private boolean readBool() throws IOException {
+ String val = mReader.nextString();
+ return Boolean.parseBoolean(val);
+ }
+
private Bus readBus() throws IOException {
String val = mReader.nextString();
return Bus.valueOf(val.toUpperCase());
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index fac0ab2..2db791fe 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -93,6 +93,10 @@
d.addDelay(e.getDuration());
} else if (Event.COMMAND_REPORT.equals(e.getCommand())) {
d.sendReport(e.getReport());
+ } else if (Event.COMMAND_SET_GET_REPORT_RESPONSE.equals(e.getCommand())) {
+ d.setGetReportResponse(e.getReport());
+ } else if (Event.COMMAND_SEND_SET_REPORT_REPLY.equals(e.getCommand())) {
+ d.sendSetReportReply(e.getReply());
} else {
if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
error("Device id=" + e.getId() + " is already registered. Ignoring event.");
diff --git a/core/api/current.txt b/core/api/current.txt
index 4d25ad7..f554e71 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52951,7 +52951,7 @@
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
- ctor public BaseInputConnection(android.view.View, boolean);
+ ctor public BaseInputConnection(@NonNull android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method @CallSuper public void closeConnection();
@@ -52963,24 +52963,24 @@
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
- method public static int getComposingSpanEnd(android.text.Spannable);
- method public static int getComposingSpanStart(android.text.Spannable);
+ method public static int getComposingSpanEnd(@NonNull android.text.Spannable);
+ method public static int getComposingSpanStart(@NonNull android.text.Spannable);
method public int getCursorCapsMode(int);
- method public android.text.Editable getEditable();
- method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
- method public android.os.Handler getHandler();
- method public CharSequence getSelectedText(int);
+ method @Nullable public android.text.Editable getEditable();
+ method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
+ method @Nullable public android.os.Handler getHandler();
+ method @Nullable public CharSequence getSelectedText(int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
- method public static final void removeComposingSpans(android.text.Spannable);
+ method public static final void removeComposingSpans(@NonNull android.text.Spannable);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
- method public static void setComposingSpans(android.text.Spannable);
+ method public static void setComposingSpans(@NonNull android.text.Spannable);
method public boolean setComposingText(CharSequence, int);
method public boolean setSelection(int, int);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f09244a..619e9fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1318,6 +1318,7 @@
method @NonNull public java.time.Instant getStartTime();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
+ field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
field public static final int EVENT_COUGH = 1; // 0x1
field public static final int EVENT_SNORE = 2; // 0x2
field public static final int EVENT_UNKNOWN = 0; // 0x0
@@ -10347,6 +10348,7 @@
field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_BLOBSTORE = "blobstore";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ee5d2b7..cf5f10b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7838,7 +7838,7 @@
Files.move(new File(oldPath).toPath(), new File(newPath).toPath(),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e2) {
- Log.e(TAG, "Rename recovery failed ", e);
+ Log.e(TAG, "Rename recovery failed ", e2);
throw e;
}
} else {
diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java
index 979c910..5268ec4 100644
--- a/core/java/android/app/AppOpInfo.java
+++ b/core/java/android/app/AppOpInfo.java
@@ -26,6 +26,7 @@
* Information about a particular app op.
*/
class AppOpInfo {
+
/**
* A unique constant identifying this app op.
*/
@@ -91,6 +92,11 @@
*/
public final boolean restrictRead;
+ /**
+ * Whether to collect noteOp instances, and send them to callbacks.
+ */
+ public final boolean forceCollectNotes;
+
AppOpInfo(int code,
int switchCode,
@NonNull String name,
@@ -100,7 +106,8 @@
AppOpsManager.RestrictionBypass allowSystemRestrictionBypass,
int defaultMode,
boolean disableReset,
- boolean restrictRead) {
+ boolean restrictRead,
+ boolean forceCollectNotes) {
if (code < OP_NONE) throw new IllegalArgumentException();
if (switchCode < OP_NONE) throw new IllegalArgumentException();
Objects.requireNonNull(name);
@@ -115,6 +122,7 @@
this.defaultMode = defaultMode;
this.disableReset = disableReset;
this.restrictRead = restrictRead;
+ this.forceCollectNotes = forceCollectNotes;
}
static class Builder {
@@ -128,6 +136,7 @@
private int mDefaultMode = AppOpsManager.MODE_DEFAULT;
private boolean mDisableReset = false;
private boolean mRestrictRead = false;
+ private boolean mForceCollectNotes = false;
Builder(int code, @NonNull String name, @NonNull String simpleName) {
if (code < OP_NONE) throw new IllegalArgumentException();
@@ -190,9 +199,15 @@
return this;
}
+ public Builder setForceCollectNotes(boolean value) {
+ this.mForceCollectNotes = value;
+ return this;
+ }
+
public AppOpInfo build() {
return new AppOpInfo(mCode, mSwitchCode, mName, mSimpleName, mPermission, mRestriction,
- mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead);
+ mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead,
+ mForceCollectNotes);
}
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bc59d60..2a0553e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1831,6 +1831,9 @@
})
private @interface ShouldCollectNoteOp {}
+ /** Whether noting for an appop should be collected */
+ private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
+
private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
// RUNTIME PERMISSIONS
// Contacts
@@ -2281,10 +2284,19 @@
"ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
.setDisableReset(true).setRestrictRead(true).build(),
new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
+ "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
+ .setForceCollectNotes(true).build()
};
/**
+ * @hide
+ */
+ public static boolean shouldForceCollectNoteForOp(int op) {
+ Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "opCode");
+ return sAppOpInfos[op].forceCollectNotes;
+ }
+
+ /**
* Mapping from an app op name to the app op code.
*/
private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
@@ -2313,9 +2325,6 @@
private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
new ThreadLocal<>();
- /** Whether noting for an appop should be collected */
- private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
-
static {
if (sAppOpInfos.length != _NUM_OP) {
throw new IllegalStateException("mAppOpInfos length " + sAppOpInfos.length
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index bd999fc..9c8d010 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -522,9 +522,6 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
- /** Enables server-side binder tracing for the calling uid. */
- void enableBinderTracing();
-
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void suppressResizeConfigChanges(boolean suppress);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index af48fde..865e1fb 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -63,8 +63,6 @@
* The integer indicating a double-tap event was detected.
* For detecting this event type, there's no specific consent activity to request access, but
* the consent is implied through the double tap toggle in the Settings app.
- *
- * @hide
*/
public static final int EVENT_BACK_DOUBLE_TAP = 3;
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index 6b0f78f..0f66fcb 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -16,12 +16,16 @@
package android.ddm;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.util.Log;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
+
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -34,6 +38,7 @@
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
/**
* Handle various requests related to profiling / debugging of the view system.
@@ -123,14 +128,15 @@
}
if (type == CHUNK_VURT) {
- if (op == VURT_DUMP_HIERARCHY)
+ if (op == VURT_DUMP_HIERARCHY) {
return dumpHierarchy(rootView, in);
- else if (op == VURT_CAPTURE_LAYERS)
+ } else if (op == VURT_CAPTURE_LAYERS) {
return captureLayers(rootView);
- else if (op == VURT_DUMP_THEME)
+ } else if (op == VURT_DUMP_THEME) {
return dumpTheme(rootView);
- else
+ } else {
return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+ }
}
final View targetView = getTargetView(rootView, in);
@@ -207,9 +213,9 @@
/**
* Returns the view hierarchy and/or view properties starting at the provided view.
* Based on the input options, the return data may include:
- * - just the view hierarchy
- * - view hierarchy & the properties for each of the views
- * - just the view properties for a specific view.
+ * - just the view hierarchy
+ * - view hierarchy & the properties for each of the views
+ * - just the view properties for a specific view.
* TODO: Currently this only returns views starting at the root, need to fix so that
* it can return properties of any view.
*/
@@ -220,7 +226,7 @@
long start = System.currentTimeMillis();
- ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
+ ByteArrayOutputStream b = new ByteArrayOutputStream(2 * 1024 * 1024);
try {
if (v2) {
ViewDebug.dumpv2(rootView, b);
@@ -304,17 +310,47 @@
* Invokes provided method on the view.
* The method name and its arguments are passed in as inputs via the byte buffer.
* The buffer contains:<ol>
- * <li> len(method name) </li>
- * <li> method name </li>
- * <li> # of args </li>
- * <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
- * The type specifier is a single character as used in JNI:
- * (Z - boolean, B - byte, C - char, S - short, I - int, J - long,
- * F - float, D - double). <p>
- * The type specifier is followed by the actual value of argument.
- * Booleans are encoded via bytes with 0 indicating false.</li>
+ * <li> len(method name) </li>
+ * <li> method name (encoded as UTF-16 2-byte characters) </li>
+ * <li> # of args </li>
+ * <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
+ * The type specifier is one character modelled after JNI signatures:
+ * <ul>
+ * <li>[ - array<br>
+ * This is followed by a second character according to this spec, indicating the
+ * array type, then the array length as an Int, followed by a repeated encoding
+ * of the actual data.
+ * WARNING: Only <b>byte[]</b> is supported currently.
+ * </li>
+ * <li>Z - boolean<br>
+ * Booleans are encoded via bytes with 0 indicating false</li>
+ * <li>B - byte</li>
+ * <li>C - char</li>
+ * <li>S - short</li>
+ * <li>I - int</li>
+ * <li>J - long</li>
+ * <li>F - float</li>
+ * <li>D - double</li>
+ * <li>V - void<br>
+ * NOT followed by a value. Only used for return types</li>
+ * <li>R - String (not a real JNI type, but added for convenience)<br>
+ * Strings are encoded as an unsigned short of the number of <b>bytes</b>,
+ * followed by the actual UTF-8 encoded bytes.
+ * WARNING: This is the same encoding as produced by
+ * ViewHierarchyEncoder#writeString. However, note that this encoding is
+ * different to what DdmHandle#getString() expects, which is used in other places
+ * in this class.
+ * WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can
+ * contain up to 64k ASCII characters, yet depending on the actual data, the true
+ * maximum might be as little as 21844 unicode characters.
+ * <b>null</b> String objects are encoded as an empty string
+ * </li>
+ * </ul>
+ * </li>
* </ol>
* Methods that take no arguments need only specify the method name.
+ *
+ * The return value is encoded the same way as a single parameter (type + value)
*/
private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) {
int l = in.getInt();
@@ -327,54 +363,17 @@
args = new Object[0];
} else {
int nArgs = in.getInt();
-
argTypes = new Class<?>[nArgs];
args = new Object[nArgs];
- for (int i = 0; i < nArgs; i++) {
- char c = in.getChar();
- switch (c) {
- case 'Z':
- argTypes[i] = boolean.class;
- args[i] = in.get() == 0 ? false : true;
- break;
- case 'B':
- argTypes[i] = byte.class;
- args[i] = in.get();
- break;
- case 'C':
- argTypes[i] = char.class;
- args[i] = in.getChar();
- break;
- case 'S':
- argTypes[i] = short.class;
- args[i] = in.getShort();
- break;
- case 'I':
- argTypes[i] = int.class;
- args[i] = in.getInt();
- break;
- case 'J':
- argTypes[i] = long.class;
- args[i] = in.getLong();
- break;
- case 'F':
- argTypes[i] = float.class;
- args[i] = in.getFloat();
- break;
- case 'D':
- argTypes[i] = double.class;
- args[i] = in.getDouble();
- break;
- default:
- Log.e(TAG, "arg " + i + ", unrecognized type: " + c);
- return createFailChunk(ERR_INVALID_PARAM,
- "Unsupported parameter type (" + c + ") to invoke view method.");
- }
+ try {
+ deserializeMethodParameters(args, argTypes, in);
+ } catch (ViewMethodInvocationSerializationException e) {
+ return createFailChunk(ERR_INVALID_PARAM, e.getMessage());
}
}
- Method method = null;
+ Method method;
try {
method = targetView.getClass().getMethod(methodName, argTypes);
} catch (NoSuchMethodException e) {
@@ -384,7 +383,10 @@
}
try {
- ViewDebug.invokeViewMethod(targetView, method, args);
+ Object result = ViewDebug.invokeViewMethod(targetView, method, args);
+ Class<?> returnType = method.getReturnType();
+ byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result));
+ return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length);
} catch (Exception e) {
Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage());
String msg = e.getCause().getMessage();
@@ -393,8 +395,6 @@
}
return createFailChunk(ERR_EXCEPTION, msg);
}
-
- return null;
}
private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) {
@@ -406,7 +406,7 @@
} catch (Exception e) {
Log.e(TAG, "Exception setting layout parameter: " + e);
return createFailChunk(ERR_EXCEPTION, "Error accessing field "
- + param + ":" + e.getMessage());
+ + param + ":" + e.getMessage());
}
return null;
@@ -431,4 +431,175 @@
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VUOP, data, 0, data.length);
}
+
+ /**
+ * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in}
+ * buffer.
+ *
+ * The length of {@code args} determines how many arguments are read. The {@code argTypes} must
+ * be the same length, and will be set to the argument types of the data read.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void deserializeMethodParameters(
+ Object[] args, Class<?>[] argTypes, ByteBuffer in) throws
+ ViewMethodInvocationSerializationException {
+ checkArgument(args.length == argTypes.length);
+
+ for (int i = 0; i < args.length; i++) {
+ char typeSignature = in.getChar();
+ boolean isArray = typeSignature == SIG_ARRAY;
+ if (isArray) {
+ char arrayType = in.getChar();
+ if (arrayType != SIG_BYTE) {
+ // This implementation only supports byte-arrays for now.
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported array parameter type (" + typeSignature
+ + ") to invoke view method @argument " + i);
+ }
+
+ int arrayLength = in.getInt();
+ if (arrayLength > in.remaining()) {
+ // The sender did not actually sent the specified amount of bytes. This
+ // avoids a malformed packet to trigger an out-of-memory error.
+ throw new BufferUnderflowException();
+ }
+
+ byte[] byteArray = new byte[arrayLength];
+ in.get(byteArray);
+
+ argTypes[i] = byte[].class;
+ args[i] = byteArray;
+ } else {
+ switch (typeSignature) {
+ case SIG_BOOLEAN:
+ argTypes[i] = boolean.class;
+ args[i] = in.get() != 0;
+ break;
+ case SIG_BYTE:
+ argTypes[i] = byte.class;
+ args[i] = in.get();
+ break;
+ case SIG_CHAR:
+ argTypes[i] = char.class;
+ args[i] = in.getChar();
+ break;
+ case SIG_SHORT:
+ argTypes[i] = short.class;
+ args[i] = in.getShort();
+ break;
+ case SIG_INT:
+ argTypes[i] = int.class;
+ args[i] = in.getInt();
+ break;
+ case SIG_LONG:
+ argTypes[i] = long.class;
+ args[i] = in.getLong();
+ break;
+ case SIG_FLOAT:
+ argTypes[i] = float.class;
+ args[i] = in.getFloat();
+ break;
+ case SIG_DOUBLE:
+ argTypes[i] = double.class;
+ args[i] = in.getDouble();
+ break;
+ case SIG_STRING: {
+ argTypes[i] = String.class;
+ int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort());
+ byte[] rawStringBuffer = new byte[stringUtf8ByteCount];
+ in.get(rawStringBuffer);
+ args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8);
+ break;
+ }
+ default:
+ Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature);
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported parameter type (" + typeSignature
+ + ") to invoke view method.");
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD.
+ * @hide
+ */
+ @VisibleForTesting
+ public static byte[] serializeReturnValue(Class<?> type, Object value)
+ throws ViewMethodInvocationSerializationException, IOException {
+ ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024);
+ DataOutputStream dos = new DataOutputStream(byteOutStream);
+
+ if (type.isArray()) {
+ if (!type.equals(byte[].class)) {
+ // Only byte arrays are supported currently.
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported array return type (" + type + ")");
+ }
+ byte[] byteArray = (byte[]) value;
+ dos.writeChar(SIG_ARRAY);
+ dos.writeChar(SIG_BYTE);
+ dos.writeInt(byteArray.length);
+ dos.write(byteArray);
+ } else if (boolean.class.equals(type)) {
+ dos.writeChar(SIG_BOOLEAN);
+ dos.write((boolean) value ? 1 : 0);
+ } else if (byte.class.equals(type)) {
+ dos.writeChar(SIG_BYTE);
+ dos.writeByte((byte) value);
+ } else if (char.class.equals(type)) {
+ dos.writeChar(SIG_CHAR);
+ dos.writeChar((char) value);
+ } else if (short.class.equals(type)) {
+ dos.writeChar(SIG_SHORT);
+ dos.writeShort((short) value);
+ } else if (int.class.equals(type)) {
+ dos.writeChar(SIG_INT);
+ dos.writeInt((int) value);
+ } else if (long.class.equals(type)) {
+ dos.writeChar(SIG_LONG);
+ dos.writeLong((long) value);
+ } else if (double.class.equals(type)) {
+ dos.writeChar(SIG_DOUBLE);
+ dos.writeDouble((double) value);
+ } else if (float.class.equals(type)) {
+ dos.writeChar(SIG_FLOAT);
+ dos.writeFloat((float) value);
+ } else if (String.class.equals(type)) {
+ dos.writeChar(SIG_STRING);
+ dos.writeUTF(value != null ? (String) value : "");
+ } else {
+ dos.writeChar(SIG_VOID);
+ }
+
+ return byteOutStream.toByteArray();
+ }
+
+ // Prefixes for simple primitives. These match the JNI definitions.
+ private static final char SIG_ARRAY = '[';
+ private static final char SIG_BOOLEAN = 'Z';
+ private static final char SIG_BYTE = 'B';
+ private static final char SIG_SHORT = 'S';
+ private static final char SIG_CHAR = 'C';
+ private static final char SIG_INT = 'I';
+ private static final char SIG_LONG = 'J';
+ private static final char SIG_FLOAT = 'F';
+ private static final char SIG_DOUBLE = 'D';
+ private static final char SIG_VOID = 'V';
+ // Prefixes for some commonly used objects
+ private static final char SIG_STRING = 'R';
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static class ViewMethodInvocationSerializationException extends Exception {
+ ViewMethodInvocationSerializationException(String message) {
+ super(message);
+ }
+ }
}
diff --git a/core/java/android/ddm/OWNERS b/core/java/android/ddm/OWNERS
new file mode 100644
index 0000000..369025b
--- /dev/null
+++ b/core/java/android/ddm/OWNERS
@@ -0,0 +1 @@
+per-file DdmHandleViewDebug.java = michschn@google.com
diff --git a/core/java/android/hardware/BatteryState.java b/core/java/android/hardware/BatteryState.java
index aa75359..956fefc 100644
--- a/core/java/android/hardware/BatteryState.java
+++ b/core/java/android/hardware/BatteryState.java
@@ -62,7 +62,8 @@
*
* @return the battery status.
*/
- public abstract @BatteryStatus int getStatus();
+ @BatteryStatus
+ public abstract int getStatus();
/**
* Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
@@ -70,5 +71,6 @@
*
* @return the battery capacity.
*/
- public abstract @FloatRange(from = -1.0f, to = 1.0f) float getCapacity();
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public abstract float getCapacity();
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 98f571b..c59d757 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -217,7 +217,8 @@
FINGERPRINT_ACQUIRED_START,
FINGERPRINT_ACQUIRED_UNKNOWN,
FINGERPRINT_ACQUIRED_IMMOBILE,
- FINGERPRINT_ACQUIRED_TOO_BRIGHT})
+ FINGERPRINT_ACQUIRED_TOO_BRIGHT,
+ FINGERPRINT_ACQUIRED_POWER_PRESSED})
@Retention(RetentionPolicy.SOURCE)
@interface FingerprintAcquired {}
@@ -302,6 +303,13 @@
int FINGERPRINT_ACQUIRED_TOO_BRIGHT = 10;
/**
+ * For sensors that have the power button co-located with their sensor, this event will
+ * be sent during enrollment.
+ * @hide
+ */
+ int FINGERPRINT_ACQUIRED_POWER_PRESSED = 11;
+
+ /**
* @hide
*/
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 72d8122..0fd164d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1538,6 +1538,9 @@
case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_bright);
+ case FINGERPRINT_ACQUIRED_POWER_PRESSED:
+ return context.getString(
+ com.android.internal.R.string.fingerprint_acquired_power_press);
case FINGERPRINT_ACQUIRED_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_acquired_vendor);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 4f26ad2..d2e5f9e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,7 +22,6 @@
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ExceptionUtils;
-import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
@@ -144,9 +143,6 @@
*/
private static volatile boolean sStackTrackingEnabled = false;
- private static final Object sTracingUidsWriteLock = new Object();
- private static volatile IntArray sTracingUidsImmutable = new IntArray();
-
/**
* Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
* {@link TransactionTracker}.
@@ -167,17 +163,6 @@
}
/**
- * @hide
- */
- public static void enableTracingForUid(int uid) {
- synchronized (sTracingUidsWriteLock) {
- final IntArray copy = sTracingUidsImmutable.clone();
- copy.add(uid);
- sTracingUidsImmutable = copy;
- }
- }
-
- /**
* Check if binder transaction stack tracking is enabled.
*
* @hide
@@ -187,13 +172,6 @@
}
/**
- * @hide
- */
- public static boolean isTracingEnabled(int callingUid) {
- return sTracingUidsImmutable.indexOf(callingUid) != -1;
- }
-
- /**
* Get the binder transaction tracker for this process.
*
* @hide
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0a6a405..2c0be87 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -541,7 +541,8 @@
}
public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
- obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, new Boolean(mute)).sendToTarget();
+ obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, Boolean.valueOf(mute))
+ .sendToTarget();
}
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d125cbb..fe46c9e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -783,6 +783,14 @@
@TestApi
public static final String NAMESPACE_INPUT_METHOD_MANAGER = "input_method_manager";
+ /**
+ * Namespace for backup and restore service related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/security/keymaster/OWNERS b/core/java/android/security/keymaster/OWNERS
index 65129a4..c4d605c 100644
--- a/core/java/android/security/keymaster/OWNERS
+++ b/core/java/android/security/keymaster/OWNERS
@@ -1,5 +1,5 @@
# Bug component: 189335
swillden@google.com
-jdanis@google.com
+eranm@google.com
jbires@google.com
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 38911e0..42c37fd 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -1072,7 +1072,8 @@
*
* @return The lights manager associated with the device, never null.
*/
- public @NonNull LightsManager getLightsManager() {
+ @NonNull
+ public LightsManager getLightsManager() {
if (mLightsManager == null) {
mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
}
@@ -1090,7 +1091,8 @@
*
* @return The sensor manager service associated with the device, never null.
*/
- public @NonNull SensorManager getSensorManager() {
+ @NonNull
+ public SensorManager getSensorManager() {
synchronized (mMotionRanges) {
if (mSensorManager == null) {
mSensorManager = InputManager.getInstance().getInputDeviceSensorManager(mId);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8fee4db..b698316 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10995,8 +10995,19 @@
* description. An image of a floppy disk that is used to save a file may
* use "Save".
*
+ * <p>
+ * This should omit role or state. Role refers to the kind of user-interface element the View
+ * is, such as a Button or Checkbox. State refers to a frequently changing property of the View,
+ * such as an On/Off state of a button or the audio level of a volume slider.
+ *
+ * <p>
+ * Content description updates are not frequent, and are used when the semantic content - not
+ * the state - of the element changes. For example, a Play button might change to a Pause
+ * button during music playback.
+ *
* @param contentDescription The content description.
* @see #getContentDescription()
+ * @see #setStateDescription(CharSequence)} for state changes.
* @attr ref android.R.styleable#View_contentDescription
*/
@RemotableViewMethod
@@ -13897,6 +13908,11 @@
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
+ * <p>
+ * <b>Note:</b> Avoid setting accessibility focus. This is intended to be controlled by screen
+ * readers. Apps changing focus can confuse screen readers, so the resulting behavior can vary
+ * by device and screen reader version.
+ *
* @return Whether this view actually took accessibility focus.
*
* @hide
@@ -14735,6 +14751,12 @@
* {@link AccessibilityNodeInfo#ACTION_SCROLL_FORWARD} to nested scrolling parents if
* {@link #isNestedScrollingEnabled() nested scrolling is enabled} on this view.</p>
*
+ * <p>
+ * <b>Note:</b> Avoid setting accessibility focus with
+ * {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}. This is intended to be controlled
+ * by screen readers. Apps changing focus can confuse screen readers, so the resulting behavior
+ * can vary by device and screen reader version.
+ *
* @param action The action to perform.
* @param arguments Optional action arguments.
* @return Whether the action was performed.
@@ -24490,8 +24512,9 @@
/**
* Set the current default focus highlight.
* @param highlight the highlight drawable, or {@code null} if it's no longer needed.
+ * @hide
*/
- private void setDefaultFocusHighlight(Drawable highlight) {
+ void setDefaultFocusHighlight(Drawable highlight) {
mDefaultFocusHighlight = highlight;
mDefaultFocusHighlightSizeChanged = true;
if (highlight != null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 074cbe5..89a1557 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3815,6 +3815,13 @@
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
+ if (!hasWindowFocus) {
+ // Clear focus highlight if its window lost focus.
+ final View focused = mView.findFocus();
+ if (focused != null) {
+ focused.setDefaultFocusHighlight(null);
+ }
+ }
}
// Note: must be done after the focus change callbacks,
@@ -5846,7 +5853,13 @@
// be when the window is first being added, and mFocused isn't
// set yet.
final View focused = mView.findFocus();
- if (focused != null && !focused.isFocusableInTouchMode()) {
+ if (focused == null) {
+ return false;
+ }
+
+ // Clear default focus highlight if it entered touch mode.
+ focused.setDefaultFocusHighlight(null);
+ if (!focused.isFocusableInTouchMode()) {
final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused);
if (ancestorToTakeFocus != null) {
// there is an ancestor that wants focus after its
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1f33806..d07a797 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4829,6 +4829,9 @@
/**
* Action that gives accessibility focus to the node.
+ * <p>
+ * This is intended to be used by screen readers. Apps changing focus can confuse screen
+ * readers, so the resulting behavior can vary by device and screen reader version.
*/
public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 624937f..8d75072 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -20,6 +20,7 @@
import android.annotation.CallSuper;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -61,8 +62,15 @@
static final Object COMPOSING = new ComposingText();
/** @hide */
- protected final InputMethodManager mIMM;
- final View mTargetView;
+ @NonNull protected final InputMethodManager mIMM;
+
+ /**
+ * Target view for the input connection.
+ *
+ * <p>This could be null for a fallback input connection.
+ */
+ @Nullable final View mTargetView;
+
final boolean mFallbackMode;
private Object[] mDefaultComposingSpans;
@@ -70,20 +78,25 @@
Editable mEditable;
KeyCharacterMap mKeyCharacterMap;
- BaseInputConnection(InputMethodManager mgr, boolean fullEditor) {
+ BaseInputConnection(@NonNull InputMethodManager mgr, boolean fullEditor) {
mIMM = mgr;
mTargetView = null;
mFallbackMode = !fullEditor;
}
- public BaseInputConnection(View targetView, boolean fullEditor) {
+ public BaseInputConnection(@NonNull View targetView, boolean fullEditor) {
mIMM = (InputMethodManager)targetView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
mTargetView = targetView;
mFallbackMode = !fullEditor;
}
- public static final void removeComposingSpans(Spannable text) {
+ /**
+ * Removes the composing spans from the given text if any.
+ *
+ * @param text the spannable text to remove composing spans
+ */
+ public static final void removeComposingSpans(@NonNull Spannable text) {
text.removeSpan(COMPOSING);
Object[] sps = text.getSpans(0, text.length(), Object.class);
if (sps != null) {
@@ -96,12 +109,17 @@
}
}
- public static void setComposingSpans(Spannable text) {
+ /**
+ * Removes the composing spans from the given text if any.
+ *
+ * @param text the spannable text to remove composing spans
+ */
+ public static void setComposingSpans(@NonNull Spannable text) {
setComposingSpans(text, 0, text.length());
}
/** @hide */
- public static void setComposingSpans(Spannable text, int start, int end) {
+ public static void setComposingSpans(@NonNull Spannable text, int start, int end) {
final Object[] sps = text.getSpans(start, end, Object.class);
if (sps != null) {
for (int i=sps.length-1; i>=0; i--) {
@@ -114,7 +132,10 @@
final int fl = text.getSpanFlags(o);
if ((fl & (Spanned.SPAN_COMPOSING | Spanned.SPAN_POINT_MARK_MASK))
!= (Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
- text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+ text.setSpan(
+ o,
+ text.getSpanStart(o),
+ text.getSpanEnd(o),
(fl & ~Spanned.SPAN_POINT_MARK_MASK)
| Spanned.SPAN_COMPOSING
| Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -126,20 +147,24 @@
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
}
- public static int getComposingSpanStart(Spannable text) {
+ /** Return the beginning of the range of composing text, or -1 if there's no composing text. */
+ public static int getComposingSpanStart(@NonNull Spannable text) {
return text.getSpanStart(COMPOSING);
}
- public static int getComposingSpanEnd(Spannable text) {
+ /** Return the end of the range of composing text, or -1 if there's no composing text. */
+ public static int getComposingSpanEnd(@NonNull Spannable text) {
return text.getSpanEnd(COMPOSING);
}
/**
- * Return the target of edit operations. The default implementation
- * returns its own fake editable that is just used for composing text;
- * subclasses that are real text editors should override this and
- * supply their own.
+ * Return the target of edit operations. The default implementation returns its own fake
+ * editable that is just used for composing text; subclasses that are real text editors should
+ * override this and supply their own.
+ *
+ * <p>Subclasses could override this method to turn null.
*/
+ @Nullable
public Editable getEditable() {
if (mEditable == null) {
mEditable = Editable.Factory.getInstance().newEditable("");
@@ -148,16 +173,14 @@
return mEditable;
}
- /**
- * Default implementation does nothing.
- */
+ /** Default implementation does nothing. */
+ @Override
public boolean beginBatchEdit() {
return false;
}
- /**
- * Default implementation does nothing.
- */
+ /** Default implementation does nothing. */
+ @Override
public boolean endBatchEdit() {
return false;
}
@@ -165,29 +188,29 @@
/**
* Called after only the composing region is modified (so it isn't called if the text also
* changes).
- * <p>
- * Default implementation does nothing.
+ *
+ * <p>Default implementation does nothing.
*
* @hide
*/
- public void endComposingRegionEditInternal() {
- }
+ public void endComposingRegionEditInternal() {}
/**
- * Default implementation calls {@link #finishComposingText()} and
- * {@code setImeConsumesInput(false)}.
+ * Default implementation calls {@link #finishComposingText()} and {@code
+ * setImeConsumesInput(false)}.
*/
@CallSuper
+ @Override
public void closeConnection() {
finishComposingText();
setImeConsumesInput(false);
}
/**
- * Default implementation uses
- * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+ * Default implementation uses {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
* MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
*/
+ @Override
public boolean clearMetaKeyStates(int states) {
final Editable content = getEditable();
if (content == null) return false;
@@ -195,25 +218,24 @@
return true;
}
- /**
- * Default implementation does nothing and returns false.
- */
+ /** Default implementation does nothing and returns false. */
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return false;
}
- /**
- * Default implementation does nothing and returns false.
- */
+ /** Default implementation does nothing and returns false. */
+ @Override
public boolean commitCorrection(CorrectionInfo correctionInfo) {
return false;
}
/**
- * Default implementation replaces any existing composing text with
- * the given text. In addition, only if fallback mode, a key event is
- * sent for the new text and the current editable buffer cleared.
+ * Default implementation replaces any existing composing text with the given text. In addition,
+ * only if fallback mode, a key event is sent for the new text and the current editable buffer
+ * cleared.
*/
+ @Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
@@ -226,21 +248,19 @@
* editable text.
*
* @param beforeLength The number of characters before the cursor to be deleted, in code unit.
- * If this is greater than the number of existing characters between the beginning of the
- * text and the cursor, then this method does not fail but deletes all the characters in
- * that range.
- * @param afterLength The number of characters after the cursor to be deleted, in code unit.
- * If this is greater than the number of existing characters between the cursor and
- * the end of the text, then this method does not fail but deletes all the characters in
- * that range.
- *
- * @return {@code true} when selected text is deleted, {@code false} when either the
- * selection is invalid or not yet attached (i.e. selection start or end is -1),
- * or the editable text is {@code null}.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code unit. If
+ * this is greater than the number of existing characters between the cursor and the end of
+ * the text, then this method does not fail but deletes all the characters in that range.
+ * @return {@code true} when selected text is deleted, {@code false} when either the selection
+ * is invalid or not yet attached (i.e. selection start or end is -1), or the editable text
+ * is {@code null}.
*/
+ @Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
- + " / " + afterLength);
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
final Editable content = getEditable();
if (content == null) return false;
@@ -389,7 +409,7 @@
continue;
}
if (java.lang.Character.isLowSurrogate(c)) {
- return INVALID_INDEX; // A invalid surrogate pair is found.
+ return INVALID_INDEX; // A invalid surrogate pair is found.
}
waitingLowSurrogate = true;
++currentIndex;
@@ -399,18 +419,18 @@
/**
* The default implementation performs the deletion around the current selection position of the
* editable text.
+ *
* @param beforeLength The number of characters before the cursor to be deleted, in code points.
- * If this is greater than the number of existing characters between the beginning of the
- * text and the cursor, then this method does not fail but deletes all the characters in
- * that range.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
* @param afterLength The number of characters after the cursor to be deleted, in code points.
- * If this is greater than the number of existing characters between the cursor and
- * the end of the text, then this method does not fail but deletes all the characters in
- * that range.
+ * If this is greater than the number of existing characters between the cursor and the end
+ * of the text, then this method does not fail but deletes all the characters in that range.
*/
+ @Override
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
- if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
- + " / " + afterLength);
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
final Editable content = getEditable();
if (content == null) return false;
@@ -466,10 +486,11 @@
}
/**
- * The default implementation removes the composing state from the
- * current editable text. In addition, only if fallback mode, a key event is
- * sent for the new text and the current editable buffer cleared.
+ * The default implementation removes the composing state from the current editable text. In
+ * addition, only if fallback mode, a key event is sent for the new text and the current
+ * editable buffer cleared.
*/
+ @Override
public boolean finishComposingText() {
if (DEBUG) Log.v(TAG, "finishComposingText");
final Editable content = getEditable();
@@ -485,10 +506,11 @@
}
/**
- * The default implementation uses TextUtils.getCapsMode to get the
- * cursor caps mode for the current selection position in the editable
- * text, unless in fallback mode in which case 0 is always returned.
+ * The default implementation uses TextUtils.getCapsMode to get the cursor caps mode for the
+ * current selection position in the editable text, unless in fallback mode in which case 0 is
+ * always returned.
*/
+ @Override
public int getCursorCapsMode(int reqModes) {
if (mFallbackMode) return 0;
@@ -507,17 +529,18 @@
return TextUtils.getCapsMode(content, a, reqModes);
}
- /**
- * The default implementation always returns null.
- */
+ /** The default implementation always returns null. */
+ @Override
+ @Nullable
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
return null;
}
/**
- * The default implementation returns the given amount of text from the
- * current cursor position in the buffer.
+ * The default implementation returns the given amount of text from the current cursor position
+ * in the buffer.
*/
+ @Override
@Nullable
public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
Preconditions.checkArgumentNonnegative(length);
@@ -549,9 +572,10 @@
}
/**
- * The default implementation returns the text currently selected, or null if none is
- * selected.
+ * The default implementation returns the text currently selected, or null if none is selected.
*/
+ @Override
+ @Nullable
public CharSequence getSelectedText(int flags) {
final Editable content = getEditable();
if (content == null) return null;
@@ -574,9 +598,10 @@
}
/**
- * The default implementation returns the given amount of text from the
- * current cursor position in the buffer.
+ * The default implementation returns the given amount of text from the current cursor position
+ * in the buffer.
*/
+ @Override
@Nullable
public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
Preconditions.checkArgumentNonnegative(length);
@@ -602,7 +627,6 @@
length = content.length() - b;
}
-
if ((flags&GET_TEXT_WITH_STYLES) != 0) {
return content.subSequence(b, b + length);
}
@@ -613,9 +637,10 @@
* The default implementation returns the given amount of text around the current cursor
* position in the buffer.
*/
+ @Override
@Nullable
public SurroundingText getSurroundingText(
- @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
Preconditions.checkArgumentNonnegative(beforeLength);
Preconditions.checkArgumentNonnegative(afterLength);
@@ -659,60 +684,75 @@
surroundingText, selStart - startPos, selEnd - startPos, startPos);
}
- /**
- * The default implementation turns this into the enter key.
- */
+ /** The default implementation turns this into the enter key. */
+ @Override
public boolean performEditorAction(int actionCode) {
long eventTime = SystemClock.uptimeMillis();
- sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
- | KeyEvent.FLAG_EDITOR_ACTION));
- sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
- | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(
+ new KeyEvent(
+ eventTime,
+ eventTime,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_ENTER,
+ 0,
+ 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD
+ | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(
+ new KeyEvent(
+ SystemClock.uptimeMillis(),
+ eventTime,
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_ENTER,
+ 0,
+ 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD
+ | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
return true;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean performContextMenuAction(int id) {
return false;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean performPrivateCommand(String action, Bundle data) {
return false;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return false;
}
+ @Override
+ @Nullable
public Handler getHandler() {
return null;
}
/**
- * The default implementation places the given text into the editable,
- * replacing any existing composing text. The new text is marked as
- * in a composing state with the composing style.
+ * The default implementation places the given text into the editable, replacing any existing
+ * composing text. The new text is marked as in a composing state with the composing style.
*/
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
return true;
}
+ @Override
public boolean setComposingRegion(int start, int end) {
final Editable content = getEditable();
if (content != null) {
@@ -735,7 +775,10 @@
ensureDefaultComposingSpans();
if (mDefaultComposingSpans != null) {
for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
- content.setSpan(mDefaultComposingSpans[i], a, b,
+ content.setSpan(
+ mDefaultComposingSpans[i],
+ a,
+ b,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
}
}
@@ -751,10 +794,8 @@
return true;
}
- /**
- * The default implementation changes the selection position in the
- * current editable text.
- */
+ /** The default implementation changes the selection position in the current editable text. */
+ @Override
public boolean setSelection(int start, int end) {
if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
final Editable content = getEditable();
@@ -779,17 +820,17 @@
}
/**
- * Provides standard implementation for sending a key event to the window
- * attached to the input connection's view.
+ * Provides standard implementation for sending a key event to the window attached to the input
+ * connection's view.
*/
+ @Override
public boolean sendKeyEvent(KeyEvent event) {
mIMM.dispatchKeyEventFromInputMethod(mTargetView, event);
return false;
}
- /**
- * Updates InputMethodManager with the current fullscreen mode.
- */
+ /** Updates InputMethodManager with the current fullscreen mode. */
+ @Override
public boolean reportFullscreenMode(boolean enabled) {
return true;
}
@@ -934,8 +975,7 @@
newCursorPosition += a;
}
if (newCursorPosition < 0) newCursorPosition = 0;
- if (newCursorPosition > content.length())
- newCursorPosition = content.length();
+ if (newCursorPosition > content.length()) newCursorPosition = content.length();
Selection.setSelection(content, newCursorPosition);
content.replace(a, b, text);
@@ -950,11 +990,16 @@
}
/**
- * Default implementation which invokes {@link View#performReceiveContent} on the target
- * view if the view {@link View#getReceiveContentMimeTypes allows} content insertion;
- * otherwise returns false without any side effects.
+ * Default implementation which invokes {@link View#performReceiveContent} on the target view if
+ * the view {@link View#getReceiveContentMimeTypes allows} content insertion; otherwise returns
+ * false without any side effects.
*/
+ @Override
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ if (mTargetView == null) {
+ return false;
+ }
+
ClipDescription description = inputContentInfo.getDescription();
if (mTargetView.getReceiveContentMimeTypes() == null) {
if (DEBUG) {
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index fd58468..72a1bac 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -96,6 +96,9 @@
private void updateDisplay(int displayId) {
DisplayInfo info = mManager.getDisplayInfo(displayId);
+ if (info == null) {
+ return;
+ }
@Resolution int resolution = getResolution(info);
synchronized (mLock) {
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 50dcca64..664aeee 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -98,6 +98,18 @@
native_getValues(mNativeObject, array);
}
+ /**
+ * Combines contained values into a smaller array by aggregating them
+ * according to an index map.
+ */
+ public boolean combineValues(long[] array, int[] indexMap) {
+ if (indexMap.length != mLength) {
+ throw new IllegalArgumentException(
+ "Wrong index map size " + indexMap.length + ", expected " + mLength);
+ }
+ return native_combineValues(mNativeObject, array, indexMap);
+ }
+
@Override
public String toString() {
final long[] array = new long[mLength];
@@ -116,6 +128,10 @@
@FastNative
private native void native_getValues(long nativeObject, long[] array);
+
+ @FastNative
+ private native boolean native_combineValues(long nativeObject, long[] array,
+ int[] indexMap);
}
private static final NativeAllocationRegistry sRegistry =
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ea5f0b2..b1e7d15 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1001,24 +1001,16 @@
}
/**
- * This will enable jdwp by default for all apps. It is OK to cache this property
- * because we expect to reboot the system whenever this property changes
- */
- private static final boolean ENABLE_JDWP = SystemProperties.get(
- "persist.debuggable.dalvik.vm.jdwp.enabled").equals("1");
-
- /**
* Applies debugger system properties to the zygote arguments.
*
- * For eng builds all apps are debuggable. On userdebug and user builds
- * if persist.debuggable.dalvik.vm.jdwp.enabled is 1 all apps are
- * debuggable. Otherwise, the debugger state is specified via the
- * "--enable-jdwp" flag in the spawn request.
+ * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
+ * the debugger state is specified via the "--enable-jdwp" flag
+ * in the spawn request.
*
* @param args non-null; zygote spawner args
*/
static void applyDebuggerSystemProperty(ZygoteArguments args) {
- if (Build.IS_ENG || ENABLE_JDWP) {
+ if (RoSystemProperties.DEBUGGABLE) {
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
}
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index f24c666..746f88c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2179,7 +2179,7 @@
break;
}
- nAudioMix->mCriteria.add(nCriterion);
+ nAudioMix->mCriteria.push_back(nCriterion);
env->DeleteLocalRef(jCriterion);
}
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 5b946d5..a95b6e3 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -244,6 +244,38 @@
std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
}
+static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
+ jlongArray jarray, jintArray jindexMap) {
+ std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+ ScopedLongArrayRW scopedArray(env, jarray);
+ ScopedIntArrayRO scopedIndexMap(env, jindexMap);
+
+ const uint64_t *data = vector->data();
+ uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
+ const uint8_t size = scopedArray.size();
+
+ for (int i = 0; i < size; i++) {
+ array[i] = 0;
+ }
+
+ bool nonZero = false;
+ for (int i = 0; i < vector->size(); i++) {
+ jint index = scopedIndexMap[i];
+ if (index < 0 || index >= size) {
+ jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+ "Index %d is out of bounds: [0, %d]", index, size - 1);
+ return false;
+ }
+
+ if (data[i] != 0L) {
+ array[index] += data[i];
+ nonZero = true;
+ }
+ }
+
+ return nonZero;
+}
+
static const JNINativeMethod g_LongArrayContainer_methods[] = {
// @CriticalNative
{"native_init", "(I)J", (void *)native_init_LongArrayContainer},
@@ -253,6 +285,8 @@
{"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
// @FastNative
{"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
+ // @FastNative
+ {"native_combineValues", "(J[J[I)Z", (void *)native_combineValues_LongArrayContainer},
};
int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
new file mode 100644
index 0000000..8ed58f3
--- /dev/null
+++ b/core/res/res/values/config_device_idle.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". Some legacy
+ entries do not follow the convention, but all new entries should. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Default for DeviceIdleController.Constants.FLEX_TIME_SHORT -->
+ <integer name="device_idle_flex_time_short_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_light_after_inactive_to_ms">180000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT -->
+ <integer name="device_idle_light_idle_to_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR -->
+ <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
+ <integer name="device_idle_light_max_idle_to_ms">900000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_min_budget_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_max_budget_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_LIGHT_MAINTENANCE_TIME -->
+ <integer name="device_idle_min_light_maintenance_time_ms">5000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_DEEP_MAINTENANCE_TIME -->
+ <integer name="device_idle_min_deep_maintenance_time_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
+ <integer name="device_idle_inactive_to_ms">1800000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT -->
+ <integer name="device_idle_sensing_to_ms">240000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
+ <integer name="device_idle_locating_to_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LOCATION_ACCURACY -->
+ <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_motion_inactive_to_ms">600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT_FLEX -->
+ <integer name="device_idle_motion_inactive_to_flex_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_idle_after_inactive_to_ms">1800000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT -->
+ <integer name="device_idle_idle_pending_to_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_IDLE_PENDING_TIMEOUT -->
+ <integer name="device_idle_max_idle_pending_to_ms">600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_FACTOR -->
+ <item name="device_idle_idle_pending_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.QUICK_DOZE_DELAY_TIMEOUT -->
+ <integer name="device_idle_quick_doze_delay_to_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
+ <integer name="device_idle_idle_to_ms">3600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT -->
+ <integer name="device_idle_max_idle_to_ms">21600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_FACTOR -->
+ <item name="device_idle_idle_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_TIME_TO_ALARM -->
+ <integer name="device_idle_min_time_to_alarm_ms">3600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_max_temp_app_allowlist_duration_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_mms_temp_app_allowlist_duration_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_sms_temp_app_allowlist_duration_ms">20000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.NOTIFICATION_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_notification_allowlist_duration_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.WAIT_FOR_UNLOCK -->
+ <bool name="device_idle_wait_for_unlock">true</bool>
+
+ <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_LONG -->
+ <item name="device_idle_pre_idle_factor_long" format="float" type="integer">1.67</item>
+
+ <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_SHORT -->
+ <item name="device_idle_pre_idle_factor_short" format="float" type="integer">0.33</item>
+
+ <!-- Default for DeviceIdleController.Constants.USE_WINDOW_ALARMS -->
+ <bool name="device_idle_use_window_alarms">true</bool>
+</resources>
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 63eb83e..9214f43 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1701,6 +1701,8 @@
<string name="fingerprint_acquired_already_enrolled">Try another fingerprint</string>
<!-- Message shown during fingerprint acquisition when fingerprint sensor detected too much light.[CHAR LIMIT=50] -->
<string name="fingerprint_acquired_too_bright">Too bright</string>
+ <!-- Message shown during fingerprint acquisition when a Power press has been detected.[CHAR LIMIT=50] -->
+ <string name="fingerprint_acquired_power_press">Power press detected</string>
<!-- Message shown during fingerprint acquisition when a fingerprint must be adjusted.[CHAR LIMIT=50] -->
<string name="fingerprint_acquired_try_adjusting">Try adjusting</string>
<!-- Message shown during fingerprint acquisition when a fingeprint area has already been captured during enrollment [CHAR LIMIT=100] -->
@@ -3574,11 +3576,11 @@
<!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_title">Tap to turn off screen</string>
+ <string name="fp_power_button_enrollment_title">To end setup, turn off screen</string>
<!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
power button is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_button_text">Turn off screen</string>
+ <string name="fp_power_button_enrollment_button_text">Turn off</string>
<!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
is pressed during biometric prompt when a side fingerprint sensor is present. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 66bfbb6..7e8423a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2611,6 +2611,7 @@
<java-symbol type="string" name="fingerprint_acquired_imager_dirty" />
<java-symbol type="string" name="fingerprint_acquired_too_slow" />
<java-symbol type="string" name="fingerprint_acquired_too_fast" />
+ <java-symbol type="string" name="fingerprint_acquired_power_press" />
<java-symbol type="string" name="fingerprint_acquired_too_bright" />
<java-symbol type="array" name="fingerprint_acquired_vendor" />
<java-symbol type="string" name="fingerprint_error_canceled" />
@@ -4413,6 +4414,40 @@
<java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
+ <!-- To config device idle -->
+ <java-symbol type="integer" name="device_idle_flex_time_short_ms" />
+ <java-symbol type="integer" name="device_idle_light_after_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_factor" />
+ <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" />
+ <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" />
+ <java-symbol type="integer" name="device_idle_min_deep_maintenance_time_ms" />
+ <java-symbol type="integer" name="device_idle_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_sensing_to_ms" />
+ <java-symbol type="integer" name="device_idle_locating_to_ms" />
+ <java-symbol type="integer" name="device_idle_location_accuracy" />
+ <java-symbol type="integer" name="device_idle_motion_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_motion_inactive_to_flex_ms" />
+ <java-symbol type="integer" name="device_idle_idle_after_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_pending_to_ms" />
+ <java-symbol type="integer" name="device_idle_max_idle_pending_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_pending_factor" />
+ <java-symbol type="integer" name="device_idle_quick_doze_delay_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_max_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_factor" />
+ <java-symbol type="integer" name="device_idle_min_time_to_alarm_ms" />
+ <java-symbol type="integer" name="device_idle_max_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_mms_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_sms_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_notification_allowlist_duration_ms" />
+ <java-symbol type="bool" name="device_idle_wait_for_unlock" />
+ <java-symbol type="integer" name="device_idle_pre_idle_factor_long" />
+ <java-symbol type="integer" name="device_idle_pre_idle_factor_short" />
+ <java-symbol type="bool" name="device_idle_use_window_alarms" />
+
<!-- Binder heavy hitter watcher configs -->
<java-symbol type="bool" name="config_defaultBinderHeavyHitterWatcherEnabled" />
<java-symbol type="integer" name="config_defaultBinderHeavyHitterWatcherBatchSize" />
diff --git a/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
new file mode 100644
index 0000000..7248983
--- /dev/null
+++ b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2022 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.ddm;
+
+import static android.ddm.DdmHandleViewDebug.deserializeMethodParameters;
+import static android.ddm.DdmHandleViewDebug.serializeReturnValue;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.ddm.DdmHandleViewDebug.ViewMethodInvocationSerializationException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class DdmHandleViewDebugTest {
+ // true
+ private static final byte[] SERIALIZED_BOOLEAN_TRUE = {0x00, 0x5A, 1};
+
+ @Test
+ public void serializeReturnValue_booleanTrue() throws Exception {
+ assertArrayEquals(SERIALIZED_BOOLEAN_TRUE, serializeReturnValue(boolean.class, true));
+ }
+
+ @Test
+ public void deserializeMethodParameters_booleanTrue() throws Exception {
+ expectDeserializedArgument(boolean.class, true, SERIALIZED_BOOLEAN_TRUE);
+ }
+
+ // false
+ private static final byte[] SERIALIZED_BOOLEAN_FALSE = {0x00, 0x5A, 0};
+
+ @Test
+ public void serializeReturnValue_booleanFalse() throws Exception {
+ assertArrayEquals(SERIALIZED_BOOLEAN_FALSE, serializeReturnValue(boolean.class, false));
+ }
+
+ @Test
+ public void deserializeMethodParameters_booleanFalse() throws Exception {
+ expectDeserializedArgument(boolean.class, false, SERIALIZED_BOOLEAN_FALSE);
+ }
+
+ // (byte) 42
+ private static final byte[] SERIALIZED_BYTE = {0x00, 0x42, 42};
+
+ @Test
+ public void serializeReturnValue_byte() throws Exception {
+ assertArrayEquals(SERIALIZED_BYTE, serializeReturnValue(byte.class, (byte) 42));
+ }
+
+ @Test
+ public void deserializeMethodParameters_byte() throws Exception {
+ expectDeserializedArgument(byte.class, (byte) 42, SERIALIZED_BYTE);
+ }
+
+ // '\u1122'
+ private static final byte[] SERIALIZED_CHAR = {0x00, 0x43, 0x11, 0x22};
+
+ @Test
+ public void serializeReturnValue_char() throws Exception {
+ assertArrayEquals(SERIALIZED_CHAR, serializeReturnValue(char.class, '\u1122'));
+ }
+
+ @Test
+ public void deserializeMethodParameters_char() throws Exception {
+ expectDeserializedArgument(char.class, '\u1122', SERIALIZED_CHAR);
+ }
+
+ // (short) 0x1011
+ private static final byte[] SERIALIZED_SHORT = {0x00, 0x53, 0x10, 0x11};
+
+ @Test
+ public void serializeReturnValue_short() throws Exception {
+ assertArrayEquals(SERIALIZED_SHORT,
+ serializeReturnValue(short.class, (short) 0x1011));
+ }
+
+ @Test
+ public void deserializeMethodParameters_short() throws Exception {
+ expectDeserializedArgument(short.class, (short) 0x1011, SERIALIZED_SHORT);
+ }
+
+ // 0x11223344
+ private static final byte[] SERIALIZED_INT = {0x00, 0x49, 0x11, 0x22, 0x33, 0x44};
+
+ @Test
+ public void serializeReturnValue_int() throws Exception {
+ assertArrayEquals(SERIALIZED_INT,
+ serializeReturnValue(int.class, 0x11223344));
+ }
+
+ @Test
+ public void deserializeMethodParameters_int() throws Exception {
+ expectDeserializedArgument(int.class, 0x11223344, SERIALIZED_INT);
+ }
+
+ // 0x0011223344556677L
+ private static final byte[] SERIALIZED_LONG =
+ {0x00, 0x4a, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+
+ @Test
+ public void serializeReturnValue_long() throws Exception {
+ assertArrayEquals(SERIALIZED_LONG,
+ serializeReturnValue(long.class, 0x0011223344556677L));
+ }
+
+ @Test
+ public void deserializeMethodParameters_long() throws Exception {
+ expectDeserializedArgument(long.class, 0x0011223344556677L, SERIALIZED_LONG);
+ }
+
+ // 3.141d
+ private static final byte[] SERIALIZED_DOUBLE =
+ {0x00, 0x44, (byte) 0x40, (byte) 0x09, (byte) 0x20, (byte) 0xc4, (byte) 0x9b,
+ (byte) 0xa5, (byte) 0xe3, (byte) 0x54};
+
+ @Test
+ public void serializeReturnValue_double() throws Exception {
+ assertArrayEquals(
+ SERIALIZED_DOUBLE,
+ serializeReturnValue(double.class, 3.141d));
+ }
+
+ @Test
+ public void deserializeMethodParameters_double() throws Exception {
+ expectDeserializedArgument(double.class, 3.141d, SERIALIZED_DOUBLE);
+ }
+
+ // 3.141f
+ private static final byte[] SERIALIZED_FLOAT =
+ {0x00, 0x46, (byte) 0x40, (byte) 0x49, (byte) 0x06, (byte) 0x25};
+
+ @Test
+ public void serializeReturnValue_float() throws Exception {
+ assertArrayEquals(SERIALIZED_FLOAT,
+ serializeReturnValue(float.class, 3.141f));
+ }
+
+ @Test
+ public void deserializeMethodParameters_float() throws Exception {
+ expectDeserializedArgument(float.class, 3.141f, SERIALIZED_FLOAT);
+ }
+
+ // "foo"
+ private static final byte[] SERIALIZED_ASCII_STRING = {0x00, 0x52, 0, 3, 0x66, 0x6f, 0x6f};
+
+ @Test
+ public void serializeReturnValue_asciiString() throws Exception {
+ assertArrayEquals(SERIALIZED_ASCII_STRING,
+ serializeReturnValue(String.class, "foo"));
+ }
+
+ @Test
+ public void deserializeMethodParameters_asciiString() throws Exception {
+ expectDeserializedArgument(String.class, "foo", SERIALIZED_ASCII_STRING);
+ }
+
+ // "\u1122"
+ private static final byte[] SERIALIZED_NON_ASCII_STRING =
+ {0x00, 0x52, 0, 3, (byte) 0xe1, (byte) 0x84, (byte) 0xa2};
+
+ @Test
+ public void serializeReturnValue_nonAsciiString_encodesAsUtf8() throws Exception {
+ assertArrayEquals(SERIALIZED_NON_ASCII_STRING,
+ serializeReturnValue(String.class, "\u1122"));
+ }
+
+ @Test
+ public void deserializeMethodParameters_decodesFromUtf8() throws Exception {
+ expectDeserializedArgument(String.class, "\u1122", SERIALIZED_NON_ASCII_STRING);
+ }
+
+ // ""
+ private static final byte[] SERIALIZED_EMPTY_STRING = {0x00, 0x52, 0, 0};
+
+ @Test
+ public void serializeReturnValue_emptyString() throws Exception {
+ assertArrayEquals(SERIALIZED_EMPTY_STRING, serializeReturnValue(String.class, ""));
+ }
+
+ @Test
+ public void deserializeMethodParameters_emptyString() throws Exception {
+ expectDeserializedArgument(String.class, "", SERIALIZED_EMPTY_STRING);
+ }
+
+ @Test
+ public void serializeReturnValue_nullString_encodesAsEmptyString() throws Exception {
+ assertArrayEquals(new byte[]{0x00, 0x52, 0, 0}, serializeReturnValue(String.class, null));
+ }
+
+ // Illegal - string length exceeding actual bytes
+ private static final byte[] SERIALIZED_INVALID_STRING =
+ {0x00, 0x52, 0, 3, 0x66};
+
+ @Test
+ public void deserializeMethodParameters_stringPayloadMissing_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_STRING)));
+ }
+
+ @Test
+ public void serializeAndDeserialize_handlesStringsUpTo64k() throws Exception {
+ char[] chars = new char[65535];
+ Arrays.fill(chars, 'a');
+ String original = new String(chars);
+ byte[] serialized = serializeReturnValue(String.class, original);
+
+ // 2 bytes for the R signature char, 2 bytes char string byte count, 2^16-1 bytes ASCII
+ // payload
+ assertEquals(2 + 2 + 65535, serialized.length);
+
+ // length is unsigned short
+ assertArrayEquals(new byte[]{0x00, 0x52, (byte) 0xff, (byte) 0xff},
+ Arrays.copyOfRange(serialized, 0, 4));
+
+ // length of string must be interpreted as unsigned short, returning original content
+ expectDeserializedArgument(String.class, original, serialized);
+ }
+
+ private static final byte[] SERIALIZED_VOID = {0x00, 0x56};
+
+ @Test
+ public void serializeReturnValue_void() throws Exception {
+ assertArrayEquals(SERIALIZED_VOID, serializeReturnValue(void.class, null));
+ }
+
+ @Test
+ public void deserializeMethodParameters_void_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_VOID)));
+ }
+
+ // new byte[]{}
+ private static final byte[] SERIALIZED_EMPTY_BYTE_ARRAY = {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 0};
+
+ @Test
+ public void serializeReturnValue_emptyByteArray() throws Exception {
+ assertArrayEquals(SERIALIZED_EMPTY_BYTE_ARRAY,
+ serializeReturnValue(byte[].class, new byte[]{}));
+ }
+
+ @Test
+ public void deserializeMethodParameters_emptyByteArray() throws Exception {
+ expectDeserializedArgument(byte[].class, new byte[]{}, SERIALIZED_EMPTY_BYTE_ARRAY);
+ }
+
+ // new byte[]{0, 42}
+ private static final byte[] SERIALIZED_SIMPLE_BYTE_ARRAY =
+ {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 2, 0, 42};
+
+ @Test
+ public void serializeReturnValue_byteArray() throws Exception {
+ assertArrayEquals(SERIALIZED_SIMPLE_BYTE_ARRAY,
+ serializeReturnValue(byte[].class, new byte[]{0, 42}));
+ }
+
+ @Test
+ public void deserializeMethodParameters_byteArray() throws Exception {
+ expectDeserializedArgument(byte[].class, new byte[]{0, 42}, SERIALIZED_SIMPLE_BYTE_ARRAY);
+ }
+
+ @Test
+ public void serializeReturnValue_largeByteArray_encodesSizeCorrectly() throws Exception {
+ byte[] result = serializeReturnValue(byte[].class, new byte[0x012233]);
+ // 2 bytes for the each [Z signature char, 4 bytes int array length, 0x012233 bytes payload
+ assertEquals(2 + 2 + 4 + 74291, result.length);
+
+ assertArrayEquals(new byte[]{0x00, 0x5b, 0x00, 0x42, 0x00, 0x01, 0x22, 0x33},
+ Arrays.copyOfRange(result, 0, 8));
+ }
+
+ // Illegal - declared size exceeds remaining buffer length
+ private static final byte[] SERIALIZED_INVALID_BYTE_ARRAY =
+ {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 3, 0, 42};
+
+ @Test
+ public void deserializeMethodParameters_sizeExceedsBuffer_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_BYTE_ARRAY)));
+ }
+
+ // new int[]{}
+ private static final byte[] SERIALIZED_EMPTY_INT_ARRAY = {0x00, 0x5b, 0x00, 0x49, 0, 0, 0, 0};
+
+ @Test
+ public void serializeReturnValue_nonByteArrayType_throws() throws Exception {
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> serializeReturnValue(int[].class, 42));
+ }
+
+ @Test
+ public void deserializeMethodParameters_nonByteArrayType_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_EMPTY_INT_ARRAY)));
+ }
+
+ // new byte[]{0, 42}
+ private static final byte[] SERIALIZED_MULTIPLE_PARAMETERS =
+ {0x00, 0x42, 42, 0x00, 0x5A, 1};
+
+ @Test
+ public void deserializeMethodParameters_multipleParameters() throws Exception {
+ expectDeserializedArguments(new Class[]{byte.class, boolean.class},
+ new Object[]{(byte) 42, true}, SERIALIZED_MULTIPLE_PARAMETERS);
+ }
+
+ // Illegal - type 'X'
+ private static final byte[] SERIALIZED_INVALID_UNKNOWN_TYPE = {0x00, 0x58};
+
+ @Test
+ public void deserializeMethodParameters_unknownType_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_UNKNOWN_TYPE)));
+ }
+
+ @Test
+ public void deserializeMethodParameters_noArgumentsEmptyPacket_isNoop() throws Exception {
+ Object[] args = new Object[0];
+ Class<?>[] argTypes = new Class<?>[0];
+ deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0]));
+ }
+
+ @Test
+ public void deserializeMethodParameters_withArgumentsEmptyPacket_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0])));
+ }
+
+ private static void expectDeserializedArgument(Class<?> expectedType, Object expectedValue,
+ byte[] argumentBuffer) throws Exception {
+ expectDeserializedArguments(new Class[]{expectedType}, new Object[]{expectedValue},
+ argumentBuffer);
+ }
+
+ private static void expectDeserializedArguments(Class<?>[] expectedTypes,
+ Object[] expectedValues, byte[] argumentBuffer) throws Exception {
+ final int argCount = expectedTypes.length;
+ assertEquals("test helper not used correctly", argCount, expectedValues.length);
+ Object[] actualArgs = new Object[argCount];
+ Class<?>[] actualArgTypes = new Class<?>[argCount];
+
+ ByteBuffer buffer = ByteBuffer.wrap(argumentBuffer);
+ deserializeMethodParameters(actualArgs, actualArgTypes, buffer);
+
+ for (int i = 0; i < argCount; i++) {
+ String context = "argument " + i;
+ assertEquals(context, expectedTypes[i], actualArgTypes[i]);
+ if (byte[].class.equals(expectedTypes[i])) {
+ assertArrayEquals((byte[]) expectedValues[i], (byte[]) actualArgs[i]);
+ } else {
+ assertEquals(expectedValues[i], actualArgs[i]);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/ddm/OWNERS b/core/tests/coretests/src/android/ddm/OWNERS
new file mode 100644
index 0000000..c8be191
--- /dev/null
+++ b/core/tests/coretests/src/android/ddm/OWNERS
@@ -0,0 +1 @@
+michschn@google.com
diff --git a/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
new file mode 100644
index 0000000..2bb5abe
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2022 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.view.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BaseInputConnectionTest {
+ private static final int[] CURSOR_CAPS_MODES =
+ new int[] {
+ InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS,
+ InputType.TYPE_TEXT_FLAG_CAP_WORDS,
+ InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+ };
+
+ private BaseInputConnection mBaseInputConnection;
+ private Editable mEditable;
+ private View mMockView;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockView = new View(InstrumentationRegistry.getInstrumentation().getContext());
+ mBaseInputConnection = new BaseInputConnection(mMockView, /*fullEditor=*/ true);
+ mEditable = mBaseInputConnection.getEditable();
+ verifyContent("", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithoutSelectionAndComposing() {
+ // before commit: "|"
+ // after commit: "text1|"
+ assertThat(mBaseInputConnection.commitText("text1", 1)).isTrue();
+ verifyContent("text1", 5, 5, -1, -1);
+
+ // before commit: "text1|"
+ // after commit: "text1text2|"
+ assertThat(mBaseInputConnection.commitText("text2", "text1".length())).isTrue();
+ verifyContent("text1text2", 10, 10, -1, -1);
+
+ // before commit: "text1text2|"
+ // after commit: "text1text2text3|"
+ assertThat(mBaseInputConnection.commitText("text3", 100)).isTrue();
+ verifyContent("text1text2text3", 15, 15, -1, -1);
+
+ // before commit: "text1text2text3|"
+ // after commit: "text1text2text3text4|"
+ // BUG(b/21476564): this behavior is inconsistent with API description.
+ assertThat(mBaseInputConnection.commitText("text4", 0)).isTrue();
+ verifyContent("text1text2text3text4", 20, 20, -1, -1);
+
+ // before commit: "text1text2text3text4|"
+ // after commit: "text1text2text3text|4text5"
+ assertThat(mBaseInputConnection.commitText("text5", -1)).isTrue();
+ verifyContent("text1text2text3text4text5", 19, 19, -1, -1);
+
+ // before commit: "text1text2text3text|4text5"
+ // after commit: "text1text2text3te|xttext64text5"
+ assertThat(mBaseInputConnection.commitText("text6", -2)).isTrue();
+ verifyContent("text1text2text3texttext64text5", 17, 17, -1, -1);
+
+ // before commit: "text1text2text3te|xttext64text5"
+ // after commit: "|text1text2text3tetext7xttext64text5"
+ assertThat(mBaseInputConnection.commitText("text7", -100)).isTrue();
+ verifyContent("text1text2text3tetext7xttext64text5", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithSelection() {
+ // before commit: "123|456|789"
+ // before commit: "123text|789"
+ prepareContent("123456789", 3, 6, -1, -1);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "|123|"
+ // before commit: "|text"
+ prepareContent("123", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.commitText("text", 0)).isTrue();
+ verifyContent("text", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithComposing() {
+ // before commit: "123456|789"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "123456789|"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "|123456789|"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 0, 9, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+ }
+
+ @Test
+ public void deleteSurroundingText_fromEditorWithoutSelectionAndComposing() {
+ // before delete: "123456789|"
+ // after delete: "123456|"
+ prepareContent("123456789", 9, 9, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(3, 0)).isTrue();
+ verifyContent("123456", 6, 6, -1, -1);
+
+ // before delete: "123456|"
+ // after delete: "|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(100, 0)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before commit: "|123456789"
+ // after delete: "|456789"
+ prepareContent("123456789", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(0, 3)).isTrue();
+ verifyContent("456789", 0, 0, -1, -1);
+
+ // before delete: "|123456789"
+ // after delete: "|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(0, 100)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before delete: "123|456789"
+ // after delete: "1|789"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(2, 3)).isTrue();
+ verifyContent("1789", 1, 1, -1, -1);
+ }
+
+ @Test
+ public void deleteSurroundingText_fromEditorSelectionOrComposing() {
+ // before delete: "123|456|789"
+ // before delete: "12|456|9"
+ prepareContent("123456789", 3, 6, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, 2)).isTrue();
+ verifyContent("124569", 2, 5, -1, -1);
+
+ // before delete: "12|456|9"
+ // before delete: "|456|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(100, 100)).isTrue();
+ verifyContent("456", 0, 3, -1, -1);
+
+ // before commit: "123456|789"
+ // ---
+ // before commit: "1[456]|89"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.deleteSurroundingText(2, 1)).isTrue();
+ verifyContent("145689", 4, 4, 1, 4);
+
+ // before commit: "1234|56789"
+ // - --
+ // before commit: "124|56|89"
+ // - --
+ prepareContent("123456789", 4, 4, 3, 6);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, 1)).isTrue();
+ verifyContent("1245689", 3, 3, 2, 5);
+ }
+
+ @Test
+ public void deleteSurroundingText_negativeLength_willBeIgnored() {
+ // before delete: "123|45678"
+ // after delete: "123|45678"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(-1, -1)).isTrue();
+ verifyContent("123456789", 3, 3, -1, -1);
+
+ // before delete: "123|45678"
+ // after delete: "123|5678"
+ assertThat(mBaseInputConnection.deleteSurroundingText(-1, 1)).isTrue();
+ verifyContent("12356789", 3, 3, -1, -1);
+
+ // before delete: "123|45678"
+ // after delete: "12|45678"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, -1)).isTrue();
+ verifyContent("12456789", 2, 2, -1, -1);
+ }
+
+ @Test
+ public void testFinishComposingText() {
+ // before finish composing: "123456|789"
+ // ---
+ // before finish composing: "123456|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 6, 6, -1, -1);
+
+ // before finish composing: "123456789|"
+ // ---
+ // before finish composing: "123456789|"
+ prepareContent("123456789", 9, 9, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 9, 9, -1, -1);
+
+ // before finish composing: "|123456789|"
+ // ---
+ // before finish composing: "|123456789|"
+ prepareContent("123456789", 0, 9, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 0, 9, -1, -1);
+
+ // before finish composing: "1234|5|6789|"
+ // ---- - ----
+ // before finish composing: "1234|5|6789"
+ prepareContent("123456789", 4, 5, 0, 9);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 4, 5, -1, -1);
+ }
+
+ @Test
+ public void testGetCursorCapsMode() {
+ // "|"
+ prepareContent("", 0, 0, -1, -1);
+ verifyCursorCapsModeWithMode("", 0);
+
+ // Hello|
+ prepareContent("Hello", 5, 5, -1, -1);
+ verifyCursorCapsModeWithMode("Hello", 5);
+
+ // Hello. |
+ prepareContent("Hello. ", 7, 7, -1, -1);
+ verifyCursorCapsModeWithMode("Hello. ", 7);
+
+ // Hello. |Hi|
+ prepareContent("Hello. Hi", 7, 9, -1, -1);
+ verifyCursorCapsModeWithMode("Hello. Hi", 7);
+
+ // Hello. |
+ // -----
+ prepareContent("Hello. ", 7, 7, 0, 5);
+ verifyCursorCapsModeWithMode("Hello. ", 7);
+ }
+
+ private void verifyCursorCapsModeWithMode(CharSequence text, int off) {
+ for (int reqMode : CURSOR_CAPS_MODES) {
+ assertThat(mBaseInputConnection.getCursorCapsMode(reqMode))
+ .isEqualTo(TextUtils.getCapsMode(text, off, reqMode));
+ }
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithoutSelectionAndComposing() {
+ // before set composing text: "|"
+ // after set composing text: "abc|"
+ // ---
+ assertThat(mBaseInputConnection.setComposingText("abc", 1)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing text: "abc|"
+ // after set composing text: "abcdef|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", 100)).isTrue();
+ verifyContent("abcdef", 6, 6, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "abcdef|"
+ // ---
+ // BUG(b/21476564): this behavior is inconsistent with API description.
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", 0)).isTrue();
+ verifyContent("abcdef", 6, 6, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "ab|cdef"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", -1)).isTrue();
+ verifyContent("abcdef", 2, 2, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "|abcdef"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", -100)).isTrue();
+ verifyContent("abcdef", 0, 0, 3, 6);
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithComposing() {
+ // before set composing text: "abc|"
+ // ---
+ // after set composing text: "def|"
+ // ---
+ prepareContent("abc", 3, 3, 0, 3);
+ assertThat(mBaseInputConnection.setComposingText("def", 1)).isTrue();
+ verifyContent("def", 3, 3, 0, 3);
+
+ // before set composing text: "abc|"
+ // ---
+ // after set composing text: "hijkl|"
+ // -----
+ assertThat(mBaseInputConnection.setComposingText("hijkl", 1)).isTrue();
+ verifyContent("hijkl", 5, 5, 0, 5);
+
+ // before set composing text: "hijkl|"
+ // -----
+ // after set composing text: "|mn"
+ // --
+ assertThat(mBaseInputConnection.setComposingText("mn", 0)).isTrue();
+ verifyContent("mn", 0, 0, 0, 2);
+
+ // before set composing text: "|mn"
+ // --
+ // after set composing text: "|opq"
+ // ---
+ assertThat(mBaseInputConnection.setComposingText("opq", -1)).isTrue();
+ verifyContent("opq", 0, 0, 0, 3);
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithSelection() {
+ // before set composing text: "|abc|"
+ // after set composing text: "defgh|"
+ // -----
+ prepareContent("abc", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("defgh", 1)).isTrue();
+ verifyContent("defgh", 5, 5, 0, 5);
+
+ // before set composing text: "a|bcdef|g"
+ // after set composing text: "a|123g"
+ // ---
+ prepareContent("abcdefg", 1, 6, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("123", 0)).isTrue();
+ verifyContent("a123g", 1, 1, 1, 4);
+
+ // before set composing text: "a|bcdef|g"
+ // ---
+ // after set composing text: "ab123456|fg"
+ // ------
+ prepareContent("abcdefg", 1, 6, 2, 5);
+ assertThat(mBaseInputConnection.setComposingText("123456", 1)).isTrue();
+ verifyContent("ab123456fg", 8, 8, 2, 8);
+
+ // before set composing text: "a|bc"
+ // ----
+ // after set composing text: "|12345"
+ // -----
+ prepareContent("abc", 1, 1, 0, 3);
+ assertThat(mBaseInputConnection.setComposingText("12345", -1)).isTrue();
+ verifyContent("12345", 0, 0, 0, 5);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithoutSelectionAndComposing() {
+ // before set composing region: "|"
+ // after set composing region: "|"
+ assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+ verifyContent("abc", 3, 3, -1, -1);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // -
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+ verifyContent("abc", 3, 3, 1, 2);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(3, 0)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(-100, 100)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithSelection() {
+ // before set composing region: "|abc|"
+ // after set composing region: "|abc|"
+ // ---
+ prepareContent("abc", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+ verifyContent("abc", 0, 3, 0, 3);
+
+ // before set composing region: "ab|cd|ef"
+ // after set composing region: "ab|cd|ef"
+ // - -- -
+ prepareContent("abcdef", 2, 4, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+ verifyContent("abcdef", 2, 4, 1, 5);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithComposing() {
+ // before set composing region: "abc|"
+ // ---
+ // after set composing region: "abc|"
+ // -
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+ verifyContent("abc", 3, 3, 1, 2);
+
+ // before set composing region: "ab|cd|ef"
+ // --
+ // after set composing region: "ab|cd|ef"
+ // - -- -
+ prepareContent("abcdef", 2, 4, 2, 4);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+ verifyContent("abcdef", 2, 4, 1, 5);
+ }
+
+ @Test
+ public void testSetSelection_toEditorWithoutComposing() {
+ // before set selection: "|"
+ // after set selection: "|"
+ assertThat(mBaseInputConnection.setSelection(0, 0)).isTrue();
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ assertThat(mBaseInputConnection.setSelection(-1, -1)).isTrue();
+
+ // before set selection: "abc|"
+ // after set selection: "a|b|c"
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ verifyContent("abc", 1, 1, -1, -1);
+
+ // before set selection: "abcdef|"
+ // after set selection: "ab|cd|ef"
+ prepareContent("abcdef", 6, 6, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(4, 2)).isTrue();
+ verifyContent("abcdef", 4, 2, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "|abc"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(0, 100)).isTrue();
+ verifyContent("abc", 0, 0, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "ab|c"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(2, 2)).isTrue();
+ verifyContent("abc", 2, 2, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "|abc"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(-1, 2)).isTrue();
+ verifyContent("abc", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testSetSelection_toEditorWithComposing() {
+ // before set selection: "abc|"
+ // ---
+ // after set selection: "a|bc"
+ // - --
+ prepareContent("abc", 3, 3, 0, 3);
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ verifyContent("abc", 1, 1, 0, 3);
+
+ // before set selection: "abcdef|"
+ // ---
+ // after set selection: "|abcdef|"
+ // ---
+ prepareContent("abcdef", 6, 6, 2, 5);
+ assertThat(mBaseInputConnection.setSelection(0, 6)).isTrue();
+ verifyContent("abcdef", 0, 6, 2, 5);
+ }
+
+ @Test
+ public void testGetText_noStyle() {
+ // "123|456|789"
+ prepareContent("123456789", 3, 6, -1, -1);
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(1, 0), "3");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(1, 0), "7");
+ verifyContentEquals(mBaseInputConnection.getSelectedText(0), "456");
+ // This falls back to default implementation in {@code InputConnection}, which always return
+ // -1 for offset.
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(1, 1, 0)
+ .isEqualTo(new SurroundingText("34567", 1, 4, -1)))
+ .isTrue();
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(100, 0), "123");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(100, 0), "789");
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(100, 100, 0)
+ .isEqualTo(new SurroundingText("123456789", 3, 6, -1)))
+ .isTrue();
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(0, 0), "");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(0, 0), "");
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(0, 0, 0)
+ .isEqualTo(new SurroundingText("456", 0, 3, -1)))
+ .isTrue();
+
+ int cursorCapsMode =
+ TextUtils.getCapsMode(
+ "123456789",
+ 3,
+ TextUtils.CAP_MODE_CHARACTERS
+ | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES);
+ TextSnapshot expectedTextSnapshot =
+ new TextSnapshot(
+ new SurroundingText("123456789", 3, 6, -1), -1, -1, cursorCapsMode);
+ verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+ }
+
+ @Test
+ public void testGetText_withStyle() {
+ // "123|456|789"
+ SpannableStringBuilder text = new SpannableStringBuilder("123456789");
+ SuggestionSpan suggestionSpanA =
+ new SuggestionSpan(Locale.US, new String[] {"a"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ SuggestionSpan suggestionSpanB =
+ new SuggestionSpan(Locale.US, new String[] {"b"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ SuggestionSpan suggestionSpanC =
+ new SuggestionSpan(Locale.US, new String[] {"c"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ text.setSpan(suggestionSpanA, 0, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(suggestionSpanB, 3, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(suggestionSpanC, 6, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ prepareContent(text, 3, 6, -1, -1);
+
+ verifySpannableString(
+ mBaseInputConnection.getTextBeforeCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+ "3",
+ 1,
+ new int[][] {new int[] {0, 1}},
+ new Object[] {suggestionSpanA});
+ verifySpannableString(
+ mBaseInputConnection.getTextAfterCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+ "7",
+ 1,
+ new int[][] {new int[] {0, 1}},
+ new Object[] {suggestionSpanC});
+ verifySpannableString(
+ mBaseInputConnection.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES),
+ "456",
+ 1,
+ new int[][] {new int[] {0, 3}},
+ new Object[] {suggestionSpanB});
+ CharSequence surroundTextString =
+ TextUtils.concat(
+ text.subSequence(0, 3), text.subSequence(3, 6), text.subSequence(6, 9));
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(100, 100, InputConnection.GET_TEXT_WITH_STYLES)
+ .isEqualTo(new SurroundingText(surroundTextString, 3, 6, -1)))
+ .isTrue();
+
+ int cursorCapsMode =
+ TextUtils.getCapsMode(
+ "123456789",
+ 3,
+ TextUtils.CAP_MODE_CHARACTERS
+ | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES);
+ TextSnapshot expectedTextSnapshot =
+ new TextSnapshot(
+ new SurroundingText(surroundTextString, 3, 6, -1), -1, -1, cursorCapsMode);
+ verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+ }
+
+ private void prepareContent(
+ CharSequence text,
+ int selectionStart,
+ int selectionEnd,
+ int composingSpanStart,
+ int composingSpanEnd) {
+ mEditable.clear();
+ mEditable.append(text);
+ Selection.setSelection(mEditable, selectionStart, selectionEnd);
+ if (isValidComposingSpan(text.length(), composingSpanStart, composingSpanEnd)) {
+ BaseInputConnection.setComposingSpans(mEditable, composingSpanStart, composingSpanEnd);
+ }
+ verifyContent(text, selectionStart, selectionEnd, composingSpanStart, composingSpanEnd);
+ }
+
+ private boolean isValidComposingSpan(
+ int textLength, int composingSpanStart, int composingSpanEnd) {
+ return composingSpanStart >= 0
+ && composingSpanStart <= textLength
+ && composingSpanEnd >= 0
+ && composingSpanEnd <= textLength;
+ }
+
+ private void verifyContent(
+ CharSequence text,
+ int selectionStart,
+ int selectionEnd,
+ int composingSpanStart,
+ int composingSpanEnd) {
+ assertThat(mEditable).isNotNull();
+ verifyContentEquals(mEditable, text.toString());
+ assertThat(Selection.getSelectionStart(mEditable)).isEqualTo(selectionStart);
+ assertThat(Selection.getSelectionEnd(mEditable)).isEqualTo(selectionEnd);
+ assertThat(BaseInputConnection.getComposingSpanStart(mEditable))
+ .isEqualTo(composingSpanStart);
+ assertThat(BaseInputConnection.getComposingSpanEnd(mEditable)).isEqualTo(composingSpanEnd);
+ }
+
+ private void verifySpannableString(
+ CharSequence text,
+ String expectedString,
+ int expectedSpanSize,
+ int[][] expectedSpanRanges,
+ Object[] expectedSpans) {
+ verifyContentEquals(text, expectedString);
+ SpannableStringBuilder spannableString = new SpannableStringBuilder(text);
+ Object[] spanList = spannableString.getSpans(0, text.length(), Object.class);
+ assertThat(spanList).isNotNull();
+ assertThat(spanList).hasLength(expectedSpanSize);
+ for (int i = 0; i < expectedSpanSize; i++) {
+ assertThat(spannableString.getSpanStart(spanList[i]))
+ .isEqualTo(expectedSpanRanges[i][0]);
+ assertThat(spannableString.getSpanEnd(spanList[i])).isEqualTo(expectedSpanRanges[i][1]);
+ }
+ for (int i = 0; i < expectedSpanSize; i++) {
+ assertThat(spanList[i]).isEqualTo(expectedSpans[i]);
+ }
+ }
+
+ private void verifyContentEquals(CharSequence text, String expectedText) {
+ assertThat(text.toString().contentEquals(expectedText)).isTrue();
+ }
+
+ private void verifyTextSnapshotContentEquals(TextSnapshot t1, TextSnapshot t2) {
+ assertThat(t1.getCompositionStart()).isEqualTo(t2.getCompositionStart());
+ assertThat(t1.getCompositionEnd()).isEqualTo(t2.getCompositionEnd());
+ assertThat(t1.getCursorCapsMode()).isEqualTo(t2.getCursorCapsMode());
+ assertThat(t1.getSurroundingText().isEqualTo(t2.getSurroundingText())).isTrue();
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
index 50ce335..047f330 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -16,18 +16,21 @@
package android.view.inputmethod;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.SuggestionSpan;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Locale;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SurroundingTextTest {
@@ -35,22 +38,22 @@
@Test
public void testSurroundingTextBasicCreation() {
SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
- assertThat(surroundingText1.getText(), is("test"));
- assertThat(surroundingText1.getSelectionStart(), is(0));
- assertThat(surroundingText1.getSelectionEnd(), is(0));
- assertThat(surroundingText1.getOffset(), is(0));
+ assertThat(surroundingText1.getText().toString()).isEqualTo("test");
+ assertThat(surroundingText1.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText1.getSelectionEnd()).isEqualTo(0);
+ assertThat(surroundingText1.getOffset()).isEqualTo(0);
SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
- assertThat(surroundingText2.getText(), is(""));
- assertThat(surroundingText2.getSelectionStart(), is(-1));
- assertThat(surroundingText2.getSelectionEnd(), is(-1));
- assertThat(surroundingText2.getOffset(), is(-1));
+ assertThat(surroundingText2.getText().toString()).isEmpty();
+ assertThat(surroundingText2.getSelectionStart()).isEqualTo(-1);
+ assertThat(surroundingText2.getSelectionEnd()).isEqualTo(-1);
+ assertThat(surroundingText2.getOffset()).isEqualTo(-1);
SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
- assertThat(surroundingText3.getText(), is("hello"));
- assertThat(surroundingText3.getSelectionStart(), is(0));
- assertThat(surroundingText3.getSelectionEnd(), is(5));
- assertThat(surroundingText3.getOffset(), is(0));
+ assertThat(surroundingText3.getText().toString()).isEqualTo("hello");
+ assertThat(surroundingText3.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText3.getSelectionEnd()).isEqualTo(5);
+ assertThat(surroundingText3.getOffset()).isEqualTo(0);
}
@Test
@@ -62,20 +65,73 @@
parcel.setDataPosition(0);
SurroundingText surroundingTextFromParcel =
SurroundingText.CREATOR.createFromParcel(parcel);
- assertThat(surroundingText.getText(), is("text"));
- assertThat(surroundingText.getSelectionStart(), is(0));
- assertThat(surroundingText.getSelectionEnd(), is(1));
- assertThat(surroundingText.getOffset(), is(2));
- assertThat(surroundingTextFromParcel.getText(), is("text"));
- assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
- assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
- assertThat(surroundingTextFromParcel.getOffset(), is(2));
+ assertThat(surroundingText.getText().toString()).isEqualTo("text");
+ assertThat(surroundingText.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText.getSelectionEnd()).isEqualTo(1);
+ assertThat(surroundingText.getOffset()).isEqualTo(2);
+ assertThat(surroundingTextFromParcel.getText().toString()).isEqualTo("text");
+ assertThat(surroundingTextFromParcel.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingTextFromParcel.getSelectionEnd()).isEqualTo(1);
+ assertThat(surroundingTextFromParcel.getOffset()).isEqualTo(2);
}
@Test
- public void testIsEqualComparesText() {
- final SurroundingText text1 = new SurroundingText("hello", 0, 1, 0);
- final SurroundingText text2 = new SurroundingText("there", 0, 1, 0);
- assertFalse(text1.isEqualTo(text2));
+ public void testIsEqualComparesText_isNotEqualTo() {
+ final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+ verifySurroundingTextNotEquals(text, new SurroundingText("there", 0, 1, 0));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 1, -1));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 0, 0));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 1, 1, 0));
+
+ SpannableString spannableString = new SpannableString("hello");
+ spannableString.setSpan(
+ new SuggestionSpan(
+ Locale.US, new String[] {"Hello"}, SuggestionSpan.FLAG_EASY_CORRECT),
+ 0,
+ 5,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ verifySurroundingTextNotEquals(text, new SurroundingText(spannableString, 1, 1, 0));
+ }
+
+ private void verifySurroundingTextNotEquals(SurroundingText text1, SurroundingText text2) {
+ assertThat(text1.isEqualTo(text2)).isFalse();
+ assertThat(text1).isNotEqualTo(text2);
+ assertThat(text1.equals(text2)).isFalse();
+ assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+ assertThat(text1 == text2).isFalse();
+ }
+
+ @Test
+ public void testIsEqualComparesText_isEqualTo() {
+ final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+ verifySurroundingTextEquals(
+ text,
+ new SurroundingText("hello", 0, 1, 0),
+ /*equals=*/ false,
+ /*isSameInstance=*/ false);
+ verifySurroundingTextEquals(text, text, /*equals=*/ true, /*isSameInstance=*/ true);
+ }
+
+ private void verifySurroundingTextEquals(
+ SurroundingText text1, SurroundingText text2, boolean equals, boolean isSameInstance) {
+ assertThat(text1.isEqualTo(text2)).isTrue();
+ if (equals) {
+ assertThat(text1).isEqualTo(text2);
+ assertThat(text1.equals(text2)).isTrue();
+ assertThat(text1.hashCode()).isEqualTo(text2.hashCode());
+ } else {
+ assertThat(text1).isNotEqualTo(text2);
+ assertThat(text1.equals(text2)).isFalse();
+ assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+ }
+ if (isSameInstance) {
+ assertThat(text1 == text2).isTrue();
+ assertThat(text1).isSameInstanceAs(text2);
+ } else {
+ assertThat(text1 == text2).isFalse();
+ assertThat(text1).isNotSameInstanceAs(text2);
+ }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index a22dd1c..516dee7 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -161,4 +161,33 @@
assertThrows(RuntimeException.class,
() -> LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel));
}
+
+ @Test
+ public void combineValues() {
+ long[] values = new long[] {0, 1, 2, 3, 42};
+ LongArrayMultiStateCounter.LongArrayContainer container =
+ new LongArrayMultiStateCounter.LongArrayContainer(values.length);
+ container.setValues(values);
+
+ long[] out = new long[3];
+ int[] indexes = {2, 1, 1, 0, 0};
+ boolean nonZero = container.combineValues(out, indexes);
+ assertThat(nonZero).isTrue();
+ assertThat(out).isEqualTo(new long[]{45, 3, 0});
+
+ // All zeros
+ container.setValues(new long[]{0, 0, 0, 0, 0});
+ nonZero = container.combineValues(out, indexes);
+ assertThat(nonZero).isFalse();
+ assertThat(out).isEqualTo(new long[]{0, 0, 0});
+
+ // Index out of range
+ IndexOutOfBoundsException e1 = assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> container.combineValues(out, new int[]{0, 1, -1, 0, 0}));
+ assertThat(e1.getMessage()).isEqualTo("Index -1 is out of bounds: [0, 2]");
+ IndexOutOfBoundsException e2 = assertThrows(IndexOutOfBoundsException.class,
+ () -> container.combineValues(out, new int[]{0, 1, 4, 0, 0}));
+ assertThat(e2.getMessage()).isEqualTo("Index 4 is out of bounds: [0, 2]");
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 6af3d2b..126f835 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -139,6 +139,16 @@
}
}
+ @Override
+ public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+ // TODO: Implement this method
+ }
+
+ @Override
+ public void clearSplitAttributesCalculator() {
+ // TODO: Implement this method
+ }
+
@NonNull
List<EmbeddingRule> getSplitRules() {
return mSplitRules;
@@ -1516,13 +1526,20 @@
.toActivityStack();
final ActivityStack secondaryContainer = container.getSecondaryContainer()
.toActivityStack();
+ final SplitAttributes.SplitType splitType = shouldShowSideBySide(container)
+ ? new SplitAttributes.SplitType.RatioSplitType(
+ container.getSplitRule().getSplitRatio())
+ : new SplitAttributes.SplitType.ExpandContainersSplitType();
final SplitInfo splitState = new SplitInfo(primaryContainer, secondaryContainer,
// Splits that are not showing side-by-side are reported as having 0 split
// ratio, since by definition in the API the primary container occupies no
// width of the split when covered by the secondary.
- shouldShowSideBySide(container)
- ? container.getSplitRule().getSplitRatio()
- : 0.0f);
+ // TODO(b/241042437): use v2 APIs for splitAttributes
+ new SplitAttributes.Builder()
+ .setSplitType(splitType)
+ .setLayoutDirection(container.getSplitRule().getLayoutDirection())
+ .build()
+ );
splitStates.add(splitState);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index cdee9e3..af5d8c5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -16,7 +16,6 @@
package androidx.window.extensions.embedding;
-import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
@@ -41,30 +40,44 @@
*/
private static final int LAYER_NO_OVERRIDE = -1;
+ @NonNull
final Animation mAnimation;
+ @NonNull
final RemoteAnimationTarget mTarget;
+ @NonNull
final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ @NonNull
final Transformation mTransformation = new Transformation();
+ @NonNull
final float[] mMatrix = new float[9];
+ @NonNull
final float[] mVecs = new float[4];
+ @NonNull
final Rect mRect = new Rect();
private boolean mIsFirstFrame = true;
private int mOverrideLayer = LAYER_NO_OVERRIDE;
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target) {
- this(animation, target, target.leash);
+ this(animation, target, target.leash, target.screenSpaceBounds);
}
/**
* @param leash the surface to animate.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
*/
TaskFragmentAnimationAdapter(@NonNull Animation animation,
- @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
+ @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
mAnimation = animation;
mTarget = target;
mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
}
/**
@@ -94,23 +107,32 @@
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
mTransformation.getMatrix().postTranslate(
mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
- // Get current animation position.
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]);
- // The exiting surface starts at position: mTarget.localBounds and moves with
- // positionX varying. Offset our crop region by the amount we have slided so crop
- // regions stays exactly on the original container in split.
- final int cropOffsetX = mTarget.localBounds.left - positionX;
- final int cropOffsetY = mTarget.localBounds.top - positionY;
- final Rect cropRect = new Rect();
- cropRect.set(mTarget.localBounds);
- // Because window crop uses absolute position.
- cropRect.offsetTo(0, 0);
- cropRect.offset(cropOffsetX, cropOffsetY);
+ final Rect cropRect = new Rect(mTarget.screenSpaceBounds);
+ final Rect localBounds = mTarget.localBounds;
+ cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
t.setCrop(mLeash, cropRect);
}
@@ -124,52 +146,6 @@
}
/**
- * Should be used when the {@link RemoteAnimationTarget} is in split with others, and want to
- * animate together as one. This adapter will offset the animation leash to make the animate of
- * two windows look like a single window.
- */
- static class SplitAdapter extends TaskFragmentAnimationAdapter {
- private final boolean mIsLeftHalf;
- private final int mWholeAnimationWidth;
-
- /**
- * @param isLeftHalf whether this is the left half of the animation.
- * @param wholeAnimationWidth the whole animation windows width.
- */
- SplitAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target,
- boolean isLeftHalf, int wholeAnimationWidth) {
- super(animation, target);
- mIsLeftHalf = isLeftHalf;
- mWholeAnimationWidth = wholeAnimationWidth;
- if (wholeAnimationWidth == 0) {
- throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
- }
- }
-
- @Override
- void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
- float posX = mTarget.localBounds.left;
- final float posY = mTarget.localBounds.top;
- // This window is half of the whole animation window. Offset left/right to make it
- // look as one with the other half.
- mTransformation.getMatrix().getValues(mMatrix);
- final int targetWidth = mTarget.localBounds.width();
- final float scaleX = mMatrix[MSCALE_X];
- final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
- final float curOffset = targetWidth * (1 - scaleX) / 2;
- final float offsetDiff = totalOffset - curOffset;
- if (mIsLeftHalf) {
- posX += offsetDiff;
- } else {
- posX -= offsetDiff;
- }
- mTransformation.getMatrix().postTranslate(posX, posY);
- t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
- t.setAlpha(mLeash, mTransformation.getAlpha());
- }
- }
-
- /**
* Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
* size change.
*/
@@ -177,7 +153,7 @@
SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
// Start leash is the snapshot of the starting surface.
- super(animation, target, target.startLeash);
+ super(animation, target, target.startLeash, target.screenSpaceBounds);
}
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8af2d9c..8c416e8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -213,10 +213,10 @@
for (RemoteAnimationTarget target : targets) {
if (target.mode != MODE_CLOSING) {
openingTargets.add(target);
- openingWholeScreenBounds.union(target.localBounds);
+ openingWholeScreenBounds.union(target.screenSpaceBounds);
} else {
closingTargets.add(target);
- closingWholeScreenBounds.union(target.localBounds);
+ closingWholeScreenBounds.union(target.screenSpaceBounds);
}
}
@@ -249,20 +249,8 @@
@NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
- final Rect targetBounds = target.localBounds;
- if (targetBounds.left == wholeAnimationBounds.left
- && targetBounds.right != wholeAnimationBounds.right) {
- // This is the left split of the whole animation window.
- return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
- true /* isLeftHalf */, wholeAnimationBounds.width());
- } else if (targetBounds.left != wholeAnimationBounds.left
- && targetBounds.right == wholeAnimationBounds.right) {
- // This is the right split of the whole animation window.
- return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
- false /* isLeftHalf */, wholeAnimationBounds.width());
- }
- // Open/close window that fills the whole animation.
- return new TaskFragmentAnimationAdapter(animation, target);
+ return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+ wholeAnimationBounds);
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 97d42391b..ef5ea56 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -195,7 +195,10 @@
? com.android.internal.R.anim.task_fragment_open_enter
: com.android.internal.R.anim.task_fragment_open_exit);
}
- animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
@@ -215,7 +218,10 @@
? com.android.internal.R.anim.task_fragment_close_enter
: com.android.internal.R.anim.task_fragment_close_exit);
}
- animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index e9a1721..2c766d8 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index cc4db93..591e347 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.activityembedding;
-import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
@@ -42,31 +41,45 @@
*/
private static final int LAYER_NO_OVERRIDE = -1;
+ @NonNull
final Animation mAnimation;
+ @NonNull
final TransitionInfo.Change mChange;
+ @NonNull
final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ @NonNull
final Transformation mTransformation = new Transformation();
+ @NonNull
final float[] mMatrix = new float[9];
+ @NonNull
final float[] mVecs = new float[4];
+ @NonNull
final Rect mRect = new Rect();
private boolean mIsFirstFrame = true;
private int mOverrideLayer = LAYER_NO_OVERRIDE;
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
@NonNull TransitionInfo.Change change) {
- this(animation, change, change.getLeash());
+ this(animation, change, change.getLeash(), change.getEndAbsBounds());
}
/**
* @param leash the surface to animate, which is not necessary the same as
- * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+ * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
*/
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
- @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash) {
+ @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
mAnimation = animation;
mChange = change;
mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
}
/**
@@ -96,23 +109,31 @@
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
final Point offset = mChange.getEndRelOffset();
mTransformation.getMatrix().postTranslate(offset.x, offset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
- // Get current animation position.
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]);
- // The exiting surface starts at position: Change#getEndRelOffset() and moves with
- // positionX varying. Offset our crop region by the amount we have slided so crop
- // regions stays exactly on the original container in split.
- final int cropOffsetX = offset.x - positionX;
- final int cropOffsetY = offset.y - positionY;
- final Rect cropRect = new Rect();
- cropRect.set(mChange.getEndAbsBounds());
- // Because window crop uses absolute position.
- cropRect.offsetTo(0, 0);
- cropRect.offset(cropOffsetX, cropOffsetY);
+ final Rect cropRect = new Rect(mChange.getEndAbsBounds());
+ cropRect.offset(positionX - offset.x, positionY - offset.y);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
t.setCrop(mLeash, cropRect);
}
@@ -127,53 +148,6 @@
}
/**
- * Should be used when the {@link TransitionInfo.Change} is in split with others, and wants to
- * animate together as one. This adapter will offset the animation leash to make the animate of
- * two windows look like a single window.
- */
- static class SplitAdapter extends ActivityEmbeddingAnimationAdapter {
- private final boolean mIsLeftHalf;
- private final int mWholeAnimationWidth;
-
- /**
- * @param isLeftHalf whether this is the left half of the animation.
- * @param wholeAnimationWidth the whole animation windows width.
- */
- SplitAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
- boolean isLeftHalf, int wholeAnimationWidth) {
- super(animation, change);
- mIsLeftHalf = isLeftHalf;
- mWholeAnimationWidth = wholeAnimationWidth;
- if (wholeAnimationWidth == 0) {
- throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
- }
- }
-
- @Override
- void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
- final Point offset = mChange.getEndRelOffset();
- float posX = offset.x;
- final float posY = offset.y;
- // This window is half of the whole animation window. Offset left/right to make it
- // look as one with the other half.
- mTransformation.getMatrix().getValues(mMatrix);
- final int changeWidth = mChange.getEndAbsBounds().width();
- final float scaleX = mMatrix[MSCALE_X];
- final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
- final float curOffset = changeWidth * (1 - scaleX) / 2;
- final float offsetDiff = totalOffset - curOffset;
- if (mIsLeftHalf) {
- posX += offsetDiff;
- } else {
- posX -= offsetDiff;
- }
- mTransformation.getMatrix().postTranslate(posX, posY);
- t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
- t.setAlpha(mLeash, mTransformation.getAlpha());
- }
- }
-
- /**
* Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has
* size change.
*/
@@ -181,7 +155,7 @@
SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
@NonNull SurfaceControl snapshotLeash) {
- super(animation, change, snapshotLeash);
+ super(animation, change, snapshotLeash, change.getEndAbsBounds());
}
@Override
@@ -196,7 +170,9 @@
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
super.onAnimationEnd(t);
// Remove the screenshot leash after animation is finished.
- t.remove(mLeash);
+ if (mLeash.isValid()) {
+ t.remove(mLeash);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 7e0795d..d88cc00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -22,9 +22,9 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.ArraySet;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -40,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.function.BiFunction;
/** To run the ActivityEmbedding animations. */
@@ -169,15 +170,12 @@
final Rect openingWholeScreenBounds = new Rect();
final Rect closingWholeScreenBounds = new Rect();
for (TransitionInfo.Change change : info.getChanges()) {
- final Rect bounds = new Rect(change.getEndAbsBounds());
- final Point offset = change.getEndRelOffset();
- bounds.offsetTo(offset.x, offset.y);
if (Transitions.isOpeningType(change.getMode())) {
openingChanges.add(change);
- openingWholeScreenBounds.union(bounds);
+ openingWholeScreenBounds.union(change.getEndAbsBounds());
} else {
closingChanges.add(change);
- closingWholeScreenBounds.union(bounds);
+ closingWholeScreenBounds.union(change.getEndAbsBounds());
}
}
@@ -210,60 +208,73 @@
@NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
- final Rect bounds = new Rect(change.getEndAbsBounds());
- final Point offset = change.getEndRelOffset();
- bounds.offsetTo(offset.x, offset.y);
- if (bounds.left == wholeAnimationBounds.left
- && bounds.right != wholeAnimationBounds.right) {
- // This is the left split of the whole animation window.
- return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
- true /* isLeftHalf */, wholeAnimationBounds.width());
- } else if (bounds.left != wholeAnimationBounds.left
- && bounds.right == wholeAnimationBounds.right) {
- // This is the right split of the whole animation window.
- return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
- false /* isLeftHalf */, wholeAnimationBounds.width());
- }
- // Open/close window that fills the whole animation.
- return new ActivityEmbeddingAnimationAdapter(animation, change);
+ return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
+ wholeAnimationBounds);
}
@NonNull
private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
+ final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();
+
+ // For the first iteration, we prepare the animation for the change type windows. This is
+ // needed because there may be window that is reparented while resizing. In such case, we
+ // will do the following:
+ // 1. Capture a screenshot from the Activity surface.
+ // 2. Attach the screenshot surface to the top of TaskFragment (Activity's parent) surface.
+ // 3. Animate the TaskFragment using Activity Change info (start/end bounds).
+ // This is because the TaskFragment surface/change won't contain the Activity's before its
+ // reparent.
for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getMode() == TRANSIT_CHANGE
- && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
- // This is the window with bounds change.
- final WindowContainerToken parentToken = change.getParent();
- final Rect parentBounds;
- if (parentToken != null) {
- TransitionInfo.Change parentChange = info.getChange(parentToken);
- parentBounds = parentChange != null
- ? parentChange.getEndAbsBounds()
- : change.getEndAbsBounds();
- } else {
- parentBounds = change.getEndAbsBounds();
- }
- final Animation[] animations =
- mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
- // Adapter for the starting screenshot leash.
- final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction);
- if (screenshotLeash != null) {
- // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
- adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
- animations[0], change, screenshotLeash));
- } else {
- Log.e(TAG, "Failed to take screenshot for change=" + change);
- }
- // Adapter for the ending bounds changed leash.
- adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
- animations[1], change));
+ if (change.getMode() != TRANSIT_CHANGE
+ || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
continue;
}
- // These are the other windows that don't have bounds change in the same transition.
+ // This is the window with bounds change.
+ handledChanges.add(change);
+ final WindowContainerToken parentToken = change.getParent();
+ TransitionInfo.Change boundsAnimationChange = change;
+ if (parentToken != null) {
+ // When the parent window is also included in the transition as an opening window,
+ // we would like to animate the parent window instead.
+ final TransitionInfo.Change parentChange = info.getChange(parentToken);
+ if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
+ // We won't create a separate animation for the parent, but to animate the
+ // parent for the child resizing.
+ handledChanges.add(parentChange);
+ boundsAnimationChange = parentChange;
+ }
+ }
+
+ final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
+ boundsAnimationChange.getEndAbsBounds());
+
+ // Create a screenshot based on change, but attach it to the top of the
+ // boundsAnimationChange.
+ final SurfaceControl screenshotLeash = getOrCreateScreenshot(change,
+ boundsAnimationChange, startTransaction);
+ if (screenshotLeash != null) {
+ // Adapter for the starting screenshot leash.
+ // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
+ adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
+ animations[0], change, screenshotLeash));
+ } else {
+ Log.e(TAG, "Failed to take screenshot for change=" + change);
+ }
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
+ animations[1], boundsAnimationChange));
+ }
+
+ // Handle the other windows that don't have bounds change in the same transition.
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (handledChanges.contains(change)) {
+ // Skip windows that we have already handled in the previous iteration.
+ continue;
+ }
+
final Animation animation;
if (!TransitionInfo.isIndependent(change, info)) {
// No-op if it will be covered by the changing parent window.
@@ -278,13 +289,27 @@
return adapters;
}
- /** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */
+ /**
+ * Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
+ * The screenshot leash should be attached to the {@code animationChange} surface which we will
+ * animate later.
+ */
@Nullable
- private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startTransaction) {
- final Rect cropBounds = new Rect(change.getStartAbsBounds());
+ private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange,
+ @NonNull TransitionInfo.Change animationChange,
+ @NonNull SurfaceControl.Transaction t) {
+ final SurfaceControl screenshotLeash = screenshotChange.getSnapshot();
+ if (screenshotLeash != null) {
+ // If WM Core has already taken a screenshot, make sure it is reparented to the
+ // animation leash.
+ t.reparent(screenshotLeash, animationChange.getLeash());
+ return screenshotLeash;
+ }
+
+ // If WM Core hasn't taken a screenshot, take a screenshot now.
+ final Rect cropBounds = new Rect(screenshotChange.getStartAbsBounds());
cropBounds.offsetTo(0, 0);
- return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds,
- Integer.MAX_VALUE);
+ return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
+ animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 6f06f28..ad0dddf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -185,8 +185,10 @@
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_open_enter
: R.anim.task_fragment_open_exit);
- final Rect bounds = change.getEndAbsBounds();
- animation.initialize(bounds.width(), bounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
@@ -203,8 +205,10 @@
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_close_enter
: R.anim.task_fragment_close_exit);
- final Rect bounds = change.getEndAbsBounds();
- animation.initialize(bounds.width(), bounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index c4bd73b..2a1bf0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -28,7 +28,7 @@
public class ScreenshotUtils {
/**
- * Take a screenshot of the specified SurfaceControl.
+ * Takes a screenshot of the specified SurfaceControl.
*
* @param sc the SurfaceControl to take a screenshot of
* @param crop the crop to use when capturing the screenshot
@@ -49,11 +49,14 @@
SurfaceControl mScreenshot = null;
SurfaceControl.Transaction mTransaction;
SurfaceControl mSurfaceControl;
+ SurfaceControl mParentSurfaceControl;
int mLayer;
- BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) {
+ BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc,
+ int layer) {
mTransaction = t;
mSurfaceControl = sc;
+ mParentSurfaceControl = parentSc;
mLayer = layer;
}
@@ -72,7 +75,7 @@
mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
- mTransaction.reparent(mScreenshot, mSurfaceControl);
+ mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
mTransaction.show(mScreenshot);
mTransaction.apply();
@@ -80,7 +83,7 @@
}
/**
- * Take a screenshot of the specified SurfaceControl.
+ * Takes a screenshot of the specified SurfaceControl.
*
* @param t the transaction used to set changes on the resulting screenshot.
* @param sc the SurfaceControl to take a screenshot of
@@ -91,7 +94,23 @@
*/
public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
Rect crop, int layer) {
- BufferConsumer consumer = new BufferConsumer(t, sc, layer);
+ return takeScreenshot(t, sc, sc /* parentSc */, crop, layer);
+ }
+
+ /**
+ * Takes a screenshot of the specified SurfaceControl.
+ *
+ * @param t the transaction used to set changes on the resulting screenshot.
+ * @param sc the SurfaceControl to take a screenshot of
+ * @param parentSc the SurfaceControl to attach the screenshot to.
+ * @param crop the crop to use when capturing the screenshot
+ * @param layer the layer to place the screenshot
+ *
+ * @return A SurfaceControl where the screenshot will be attached, or null if failed.
+ */
+ public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
+ SurfaceControl parentSc, Rect crop, int layer) {
+ BufferConsumer consumer = new BufferConsumer(t, sc, parentSc, layer);
captureLayer(sc, crop, consumer);
return consumer.mScreenshot;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 4c85d20..419e62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -561,6 +561,10 @@
if (from == to) {
// No animation run, still callback to stop resizing.
mSplitLayoutHandler.onLayoutSizeChanged(this);
+
+ if (flingFinishedCallback != null) {
+ flingFinishedCallback.run();
+ }
InteractionJankMonitorUtils.endTracing(
CUJ_SPLIT_SCREEN_RESIZE);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
new file mode 100644
index 0000000..acc0caf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.pip.phone;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.Rect;
+
+import com.android.wm.shell.pip.PipBoundsState;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Static utilities to get appropriate {@link PipDoubleTapHelper.PipSizeSpec} on a double tap.
+ */
+public class PipDoubleTapHelper {
+
+ /**
+ * Should not be instantiated as a stateless class.
+ */
+ private PipDoubleTapHelper() {}
+
+ /**
+ * A constant that represents a pip screen size.
+ *
+ * <p>CUSTOM - user resized screen size (by pinching in/out)</p>
+ * <p>DEFAULT - normal screen size used as default when entering pip mode</p>
+ * <p>MAX - maximum allowed screen size</p>
+ */
+ @IntDef(value = {
+ SIZE_SPEC_CUSTOM,
+ SIZE_SPEC_DEFAULT,
+ SIZE_SPEC_MAX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PipSizeSpec {}
+
+ static final int SIZE_SPEC_CUSTOM = 2;
+ static final int SIZE_SPEC_DEFAULT = 0;
+ static final int SIZE_SPEC_MAX = 1;
+
+ /**
+ * Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from.
+ *
+ * <p>Each double tap toggles back and forth between {@code PipSizeSpec.CUSTOM} and
+ * either {@code PipSizeSpec.MAX} or {@code PipSizeSpec.DEFAULT}. The choice between
+ * the latter two sizes is determined based on the current state of the pip screen.</p>
+ *
+ * @param mPipBoundsState current state of the pip screen
+ */
+ @PipSizeSpec
+ private static int getMaxOrDefaultPipSizeSpec(@NonNull PipBoundsState mPipBoundsState) {
+ // determine the average pip screen width
+ int averageWidth = (mPipBoundsState.getMaxSize().x
+ + mPipBoundsState.getMinSize().x) / 2;
+
+ // If pip screen width is above average, DEFAULT is the size spec we need to
+ // toggle to. Otherwise, we choose MAX.
+ return (mPipBoundsState.getBounds().width() > averageWidth)
+ ? SIZE_SPEC_DEFAULT
+ : SIZE_SPEC_MAX;
+ }
+
+ /**
+ * Determines the {@link PipSizeSpec} to toggle to on double tap.
+ *
+ * @param mPipBoundsState current state of the pip screen
+ * @param userResizeBounds latest user resized bounds (by pinching in/out)
+ * @return pip screen size to switch to
+ */
+ @PipSizeSpec
+ static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState,
+ @NonNull Rect userResizeBounds) {
+ // is pip screen at its maximum
+ boolean isScreenMax = mPipBoundsState.getBounds().width()
+ == mPipBoundsState.getMaxSize().x;
+
+ // is pip screen at its normal default size
+ boolean isScreenDefault = (mPipBoundsState.getBounds().width()
+ == mPipBoundsState.getNormalBounds().width())
+ && (mPipBoundsState.getBounds().height()
+ == mPipBoundsState.getNormalBounds().height());
+
+ // edge case 1
+ // if user hasn't resized screen yet, i.e. CUSTOM size does not exist yet
+ // or if user has resized exactly to DEFAULT, then we just want to maximize
+ if (isScreenDefault
+ && userResizeBounds.width() == mPipBoundsState.getNormalBounds().width()) {
+ return SIZE_SPEC_MAX;
+ }
+
+ // edge case 2
+ // if user has maximized, then we want to toggle to DEFAULT
+ if (isScreenMax
+ && userResizeBounds.width() == mPipBoundsState.getMaxSize().x) {
+ return SIZE_SPEC_DEFAULT;
+ }
+
+ // otherwise in general we want to toggle back to user's CUSTOM size
+ if (isScreenDefault || isScreenMax) {
+ return SIZE_SPEC_CUSTOM;
+ }
+
+ // if we are currently in user resized CUSTOM size state
+ // then we toggle either to MAX or DEFAULT depending on the current pip screen state
+ return getMaxOrDefaultPipSizeSpec(mPipBoundsState);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a2fa058..84d9217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -929,9 +929,18 @@
if (mMenuController.isMenuVisible()) {
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
}
- if (toExpand) {
+
+ // the size to toggle to after a double tap
+ int nextSize = PipDoubleTapHelper
+ .nextSizeSpec(mPipBoundsState, getUserResizeBounds());
+
+ // actually toggle to the size chosen
+ if (nextSize == PipDoubleTapHelper.SIZE_SPEC_MAX) {
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
animateToMaximizedState(null);
+ } else if (nextSize == PipDoubleTapHelper.SIZE_SPEC_DEFAULT) {
+ mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
+ animateToNormalSize(null);
} else {
animateToUnexpandedState(getUserResizeBounds());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3714fe7..ecdafa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -21,6 +21,7 @@
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import com.android.internal.logging.InstanceId;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.window.RemoteTransition;
@@ -67,34 +68,35 @@
* Starts a shortcut in a stage.
*/
oneway void startShortcut(String packageName, String shortcutId, int position,
- in Bundle options, in UserHandle user) = 8;
+ in Bundle options, in UserHandle user, in InstanceId instanceId) = 8;
/**
* Starts an activity in a stage.
*/
oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
- in Bundle options) = 9;
+ in Bundle options, in InstanceId instanceId) = 9;
/**
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
in Bundle sideOptions, int sidePosition, float splitRatio,
- in RemoteTransition remoteTransition) = 10;
+ in RemoteTransition remoteTransition, in InstanceId instanceId) = 10;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
int sideTaskId, in Bundle sideOptions, int sidePosition,
- float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+ float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
/**
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
- int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter) = 12;
+ int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter,
+ in InstanceId instanceId) = 12;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
@@ -115,5 +117,5 @@
*/
oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
- in RemoteAnimationAdapter adapter) = 15;
+ in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 9206afb..025e559 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -387,6 +387,17 @@
}
}
+ /**
+ * See {@link #startShortcut(String, String, int, Bundle, UserHandle)}
+ * @param instanceId to be used by {@link SplitscreenEventLogger}
+ */
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+ @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId) {
+ mStageCoordinator.getLogger().enterRequested(instanceId);
+ startShortcut(packageName, shortcutId, position, options, user);
+ }
+
+ @Override
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -415,7 +426,6 @@
0 /* duration */, 0 /* statusBarTransitionDelay */);
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-
try {
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
@@ -425,6 +435,17 @@
}
}
+ /**
+ * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
+ * @param instanceId to be used by {@link SplitscreenEventLogger}
+ */
+ public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
+ @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
+ mStageCoordinator.getLogger().enterRequested(instanceId);
+ startIntent(intent, fillInIntent, position, options);
+ }
+
+ @Override
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
if (fillInIntent == null) {
@@ -446,7 +467,6 @@
mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
return;
}
-
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
@@ -772,60 +792,64 @@
@Override
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- splitRatio, adapter));
+ splitRatio, adapter, instanceId));
}
@Override
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
- int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startIntentAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
pendingIntent, fillInIntent, taskId, mainOptions, sideOptions,
- sidePosition, splitRatio, adapter));
+ sidePosition, splitRatio, adapter, instanceId));
}
@Override
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
- splitRatio, adapter));
+ splitRatio, adapter, instanceId));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
+ sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition,
+ instanceId));
}
@Override
public void startShortcut(String packageName, String shortcutId, int position,
- @Nullable Bundle options, UserHandle user) {
+ @Nullable Bundle options, UserHandle user, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, position, options, user);
+ controller.startShortcut(packageName, shortcutId, position, options, user,
+ instanceId);
});
}
@Override
public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
- @Nullable Bundle options) {
+ @Nullable Bundle options, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, position, options);
+ controller.startIntent(intent, fillInIntent, position, options, instanceId);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 3e7a100..626ccb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.splitscreen;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
@@ -59,7 +59,7 @@
// Drag info
private @SplitPosition int mDragEnterPosition;
- private InstanceId mDragEnterSessionId;
+ private InstanceId mEnterSessionId;
// For deduping async events
private int mLastMainStagePosition = -1;
@@ -82,9 +82,17 @@
/**
* May be called before logEnter() to indicate that the session was started from a drag.
*/
- public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
+ public void enterRequestedByDrag(@SplitPosition int position, InstanceId enterSessionId) {
mDragEnterPosition = position;
- mDragEnterSessionId = dragSessionId;
+ enterRequested(enterSessionId);
+ }
+
+ /**
+ * May be called before logEnter() to indicate that the session was started from launcher.
+ * This specifically is for all the scenarios where split started without a drag interaction
+ */
+ public void enterRequested(InstanceId enterSessionId) {
+ mEnterSessionId = enterSessionId;
}
/**
@@ -97,7 +105,7 @@
mLoggerSessionId = mIdSequence.newInstanceId();
int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
- : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+ : SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
mainStageUid);
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
@@ -112,7 +120,7 @@
mLastMainStageUid,
mLastSideStagePosition,
mLastSideStageUid,
- mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
+ mEnterSessionId != null ? mEnterSessionId.getId() : 0,
mLoggerSessionId.getId());
}
@@ -176,7 +184,7 @@
// Reset states
mLoggerSessionId = null;
mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
- mDragEnterSessionId = null;
+ mEnterSessionId = null;
mLastMainStagePosition = -1;
mLastMainStageUid = -1;
mLastSideStagePosition = -1;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8ed16ce..946dfde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -69,6 +69,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
@@ -87,6 +88,7 @@
import android.util.Slog;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -405,6 +407,10 @@
return result;
}
+ SplitscreenEventLogger getLogger() {
+ return mLogger;
+ }
+
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
@@ -497,7 +503,7 @@
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
sideOptions = sideOptions != null ? sideOptions : new Bundle();
@@ -526,46 +532,57 @@
mSplitTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
+ setEnterInstanceId(instanceId);
}
/** Starts 2 tasks in one legacy transition. */
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startTask(sideTaskId, sideOptions);
- startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
/** Start an intent and a task ordered by {@code intentFirst}. */
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
- startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);
- startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
- private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
+ /**
+ * @param instanceId if {@code null}, will not log. Otherwise it will be used in
+ * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
+ */
+ private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter) {
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
@@ -574,59 +591,56 @@
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
- LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+ prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
+
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback,
- SurfaceControl.Transaction t) {
- if (apps == null || apps.length == 0) {
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- setDividerVisibility(true, t);
- t.apply();
- onRemoteAnimationFinished(apps);
- try {
- adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- return;
- }
-
- // Wrap the divider bar into non-apps target to animate together.
- nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
- getDividerBarLegacyTarget());
-
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- setDividerVisibility(true, t);
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) {
- t.show(apps[i].leash);
- // Reset the surface position of the opening app to prevent double-offset.
- t.setPosition(apps[i].leash, 0, 0);
- }
- }
- t.apply();
-
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
IRemoteAnimationFinishedCallback wrapCallback =
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- onRemoteAnimationFinished(apps);
+ onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
finishedCallback.onAnimationFinished();
}
};
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try {
- adapter.getRunner().onAnimationStart(
- transit, apps, wallpapers, nonApps, wrapCallback);
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+ getDividerBarLegacyTarget()), wrapCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
+ try {
+ adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
}
};
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
- final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ mainOptions = mainActivityOptions.toBundle();
+ }
+
setSideStagePosition(sidePosition, wct);
if (!mMainStage.isActive()) {
mMainStage.activate(wct, false /* reparent */);
@@ -634,34 +648,40 @@
if (mainOptions == null) mainOptions = new Bundle();
addActivityOptions(mainOptions, mMainStage);
- wct.startTask(mainTaskId, mainOptions);
- wct.merge(sideWct, true);
-
updateWindowBounds(mSplitLayout, wct);
+ wct.startTask(mainTaskId, mainOptions);
wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
- mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> {
+ setDividerVisibility(true, t);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ });
+
+ setEnterInstanceId(instanceId);
}
- private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
+ private void setEnterInstanceId(InstanceId instanceId) {
+ if (instanceId != null) {
+ mLogger.enterRequested(instanceId);
+ }
+ }
+
+ private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
+ WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- if (apps == null || apps.length == 0) return;
-
- // If any stage has no child after finished animation, that side of the split will display
- // nothing. This might happen if starting the same app on the both sides while not
- // supporting multi-instance. Exit the split screen and expand that app to full screen.
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
- mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
- ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
- return;
+ // If any stage has no child after animation finished, it means that split will display
+ // nothing, such status will happen if task and intent is same app but not support
+ // multi-instance, we should exit split and expand that app as full screen.
+ if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
+ mMainExecutor.execute(() ->
+ exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ } else {
+ mSyncQueue.queue(evictWct);
}
-
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
- mSyncQueue.queue(evictWct);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
new file mode 100644
index 0000000..8ce3ca4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.pip.phone;
+
+import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_CUSTOM;
+import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_DEFAULT;
+import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_MAX;
+import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.nextSizeSpec;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipBoundsState;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit test against {@link PipDoubleTapHelper}.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipDoubleTapHelperTest extends ShellTestCase {
+ // represents the current pip window state and has information on current
+ // max, min, and normal sizes
+ @Mock private PipBoundsState mBoundStateMock;
+ // tied to boundsStateMock.getBounds() in setUp()
+ @Mock private Rect mBoundsMock;
+
+ // represents the most recent manually resized bounds
+ // i.e. dimensions from the most recent pinch in/out
+ @Mock private Rect mUserResizeBoundsMock;
+
+ // actual dimensions of the pip screen bounds
+ private static final int MAX_WIDTH = 100;
+ private static final int DEFAULT_WIDTH = 40;
+ private static final int MIN_WIDTH = 10;
+
+ private static final int AVERAGE_WIDTH = (MAX_WIDTH + MIN_WIDTH) / 2;
+
+ /**
+ * Initializes mocks and assigns values for different pip screen bounds.
+ */
+ @Before
+ public void setUp() {
+ // define pip bounds
+ when(mBoundStateMock.getMaxSize()).thenReturn(new Point(MAX_WIDTH, 20));
+ when(mBoundStateMock.getMinSize()).thenReturn(new Point(MIN_WIDTH, 2));
+
+ Rect rectMock = mock(Rect.class);
+ when(rectMock.width()).thenReturn(DEFAULT_WIDTH);
+ when(mBoundStateMock.getNormalBounds()).thenReturn(rectMock);
+
+ when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
+ when(mBoundStateMock.getBounds()).thenReturn(mBoundsMock);
+ }
+
+ /**
+ * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
+ *
+ * <p>when the user resizes the screen to a larger than the average but not the maximum width,
+ * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.DEFAULT}
+ */
+ @Test
+ public void testNextScreenSize_resizedWiderThanAverage_returnDefaultThenCustom() {
+ // make the user resize width in between MAX and average
+ when(mUserResizeBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);
+ // make current bounds same as resized bound since no double tap yet
+ when(mBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to DEFAULT state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_DEFAULT);
+
+ // once we toggle to DEFAULT our screen size gets updated
+ // but not the user resize bounds
+ when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to CUSTOM state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_CUSTOM);
+ }
+
+ /**
+ * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
+ *
+ * <p>when the user resizes the screen to a smaller than the average but not the default width,
+ * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.MAX}
+ */
+ @Test
+ public void testNextScreenSize_resizedNarrowerThanAverage_returnMaxThenCustom() {
+ // make the user resize width in between MIN and average
+ when(mUserResizeBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);
+ // make current bounds same as resized bound since no double tap yet
+ when(mBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to MAX state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_MAX);
+
+ // once we toggle to MAX our screen size gets updated
+ // but not the user resize bounds
+ when(mBoundsMock.width()).thenReturn(MAX_WIDTH);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to CUSTOM state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_CUSTOM);
+ }
+
+ /**
+ * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
+ *
+ * <p>when the user resizes the screen to exactly the maximum width
+ * then we toggle to {@code PipSizeSpec.DEFAULT}
+ */
+ @Test
+ public void testNextScreenSize_resizedToMax_returnDefault() {
+ // the resized width is the same as MAX_WIDTH
+ when(mUserResizeBoundsMock.width()).thenReturn(MAX_WIDTH);
+ // the current bounds are also at MAX_WIDTH
+ when(mBoundsMock.width()).thenReturn(MAX_WIDTH);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to DEFAULT state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_DEFAULT);
+ }
+
+ /**
+ * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
+ *
+ * <p>when the user resizes the screen to exactly the default width
+ * then we toggle to {@code PipSizeSpec.MAX}
+ */
+ @Test
+ public void testNextScreenSize_resizedToDefault_returnMax() {
+ // the resized width is the same as DEFAULT_WIDTH
+ when(mUserResizeBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
+ // the current bounds are also at DEFAULT_WIDTH
+ when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
+
+ // then nextScreenSize() i.e. double tapping should
+ // toggle to MAX state
+ Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
+ SIZE_SPEC_MAX);
+ }
+}
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 60c812a..819358b 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -677,21 +677,39 @@
@Override
public String toString() {
- return "AudioPlaybackConfiguration piid:" + mPlayerIId
- + " deviceId:" + mDeviceId
- + " type:" + toLogFriendlyPlayerType(mPlayerType)
- + " u/pid:" + mClientUid + "/" + mClientPid
- + " state:" + toLogFriendlyPlayerState(mPlayerState)
- + " attr:" + mPlayerAttr
- + " sessionId:" + mSessionId
- + " mutedState:"
- + " muteFromMasterMute=" + ((mMutedState & PLAYER_MUTE_MASTER) != 0)
- + " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
- + " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
- + " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
- != 0)
- + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
- + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+ StringBuilder apcToString = new StringBuilder();
+ apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
+ " deviceId:").append(mDeviceId).append(" type:").append(
+ toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append(
+ "/").append(mClientPid).append(" state:").append(
+ toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append(
+ " sessionId:").append(mSessionId).append(" mutedState:");
+ if (mMutedState == PLAYER_MUTE_INVALID) {
+ apcToString.append("invalid ");
+ } else if (mMutedState == 0) {
+ apcToString.append("none ");
+ } else {
+ if ((mMutedState & PLAYER_MUTE_MASTER) != 0) {
+ apcToString.append("master ");
+ }
+ if ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+ apcToString.append("streamVolume ");
+ }
+ if ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0) {
+ apcToString.append("streamMute ");
+ }
+ if ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+ apcToString.append("playbackRestricted ");
+ }
+ if ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+ apcToString.append("clientVolume ");
+ }
+ if ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+ apcToString.append("volumeShaper ");
+ }
+ }
+
+ return apcToString.toString();
}
//=====================================================================
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 6c6fccb..fbc4bb9 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -1014,7 +1014,7 @@
status_t res = lockImageFromBuffer(
buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image);
// Clear the fenceFd as it is already consumed by lock call.
- Image_setFenceFd(env, thiz, /*fenceFd*/-1);
+ env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, -1);
if (res != OK) {
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
"lock buffer failed for format 0x%x",
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 2ddfacf..8b5b726 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -28,7 +28,7 @@
"libaudioclient",
"libaudioutils",
"libaudiofoundation",
- "libbinder"
+ "libbinder",
],
export_shared_lib_headers: [
@@ -42,6 +42,7 @@
"-Werror",
"-Wunused",
"-Wunreachable-code",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
// Workaround Clang LTO crash.
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index d0f1ec6..09c45ea 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -142,7 +142,8 @@
mCaptureRate = rate;
if (cbk != NULL) {
- mCaptureThread = new CaptureThread(this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
+ mCaptureThread = sp<CaptureThread>::make(
+ sp<Visualizer>::fromExisting(this), rate, ((flags & CAPTURE_CALL_JAVA) != 0));
}
ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
rate, mCaptureThread.get(), mCaptureFlags);
@@ -439,7 +440,7 @@
//-------------------------------------------------------------------------
-Visualizer::CaptureThread::CaptureThread(Visualizer* receiver, uint32_t captureRate,
+Visualizer::CaptureThread::CaptureThread(const sp<Visualizer>& receiver, uint32_t captureRate,
bool bCanCallJava)
: Thread(bCanCallJava), mReceiver(receiver)
{
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index 3d5d74a..b38c01f 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -157,7 +157,8 @@
class CaptureThread : public Thread
{
public:
- CaptureThread(Visualizer* visualizer, uint32_t captureRate, bool bCanCallJava = false);
+ CaptureThread(const sp<Visualizer>& visualizer,
+ uint32_t captureRate, bool bCanCallJava = false);
private:
friend class Visualizer;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 2fb85a7..63e48aa 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -205,15 +205,15 @@
Mutex::Autolock l(sLock);
AudioEffect* const ae =
(AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
- return sp<AudioEffect>(ae);
+ return sp<AudioEffect>::fromExisting(ae);
}
static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
const sp<AudioEffect>& ae)
{
Mutex::Autolock l(sLock);
- sp<AudioEffect> old =
- (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+ sp<AudioEffect> old = sp<AudioEffect>::fromExisting(
+ (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect));
if (ae.get()) {
ae->incStrong((void*)setAudioEffect);
}
@@ -347,8 +347,8 @@
// create the native AudioEffect object
parcel = parcelForJavaObject(env, jAttributionSource);
attributionSource.readFromParcel(parcel);
- lpAudioEffect = new AudioEffect(attributionSource);
- if (lpAudioEffect == 0) {
+ lpAudioEffect = sp<AudioEffect>::make(attributionSource);
+ if (lpAudioEffect == 0) { // FIXME: I don't think this is actually possible.
ALOGE("Error creating AudioEffect");
goto setup_failure;
}
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index fac32e0..9407122 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -251,15 +251,15 @@
Mutex::Autolock l(sLock);
Visualizer* const v =
(Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
- return sp<Visualizer>(v);
+ return sp<Visualizer>::fromExisting(v);
}
static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
const sp<Visualizer>& v)
{
Mutex::Autolock l(sLock);
- sp<Visualizer> old =
- (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+ sp<Visualizer> old = sp<Visualizer>::fromExisting(
+ (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer));
if (v.get()) {
v->incStrong((void*)setVisualizer);
}
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index e5bf8ca..e34fedd 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -20,7 +20,8 @@
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_label"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true">
<activity
android:name=".MainActivity"
android:exported="true">
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 104966d..418d6cb 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -59,6 +59,7 @@
}
dependencies {
+ api "androidx.appcompat:appcompat:1.6.0-rc01"
api "androidx.compose.material3:material3:$jetpack_compose_material3_version"
api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index ae15da6..e5a1862 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,7 +22,10 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@@ -50,9 +53,6 @@
@Composable
private fun MainContent() {
- val destination =
- intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
-
val navController = rememberNavController()
CompositionLocalProvider(navController.localNavController()) {
NavHost(navController, ROOT_PAGE_NAME) {
@@ -70,13 +70,23 @@
}
}
}
+ }
+ InitialDestinationNavigator(navController)
+ }
+
+ @Composable
+ private fun InitialDestinationNavigator(navController: NavHostController) {
+ val destinationNavigated = rememberSaveable { mutableStateOf(false) }
+ if (destinationNavigated.value) return
+ destinationNavigated.value = true
+ LaunchedEffect(Unit) {
+ val destination =
+ intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
if (destination.isNotEmpty()) {
- LaunchedEffect(Unit) {
- navController.navigate(destination) {
- popUpTo(navController.graph.findStartDestination().id) {
- inclusive = true
- }
+ navController.navigate(destination) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ inclusive = true
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 766c036..7353cc0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -407,7 +407,7 @@
|| sessionInfo.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
}
- private void refreshDevices() {
+ private synchronized void refreshDevices() {
mMediaDevices.clear();
mCurrentConnectedDevice = null;
if (TextUtils.isEmpty(mPackageName)) {
@@ -437,7 +437,7 @@
return infos;
}
- private void buildAvailableRoutes() {
+ private synchronized void buildAvailableRoutes() {
for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) {
if (DEBUG) {
Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
@@ -447,7 +447,7 @@
}
}
- private List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+ private synchronized List<MediaRoute2Info> getAvailableRoutes(String packageName) {
final List<MediaRoute2Info> infos = new ArrayList<>();
RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
if (routingSessionInfo != null) {
@@ -571,7 +571,7 @@
@Override
public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
- dispatchDataChanged();
+ refreshDevices();
}
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f4af6e8..33fb91d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -666,12 +666,22 @@
}
@Test
- public void onSessionUpdated_shouldDispatchDataChanged() {
+ public void onSessionUpdated_shouldDispatchDeviceListAdded() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ mShadowRouter2Manager.setAllRoutes(routes);
+
+ mInfoMediaManager.mPackageName = "";
mInfoMediaManager.registerCallback(mCallback);
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
- verify(mCallback).onDeviceAttributesChanged();
+ verify(mCallback).onDeviceListAdded(any());
}
@Test
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e9cec70..3325eec 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -7,6 +7,7 @@
aaliomer@google.com
adamcohen@google.com
alexflo@google.com
+arteiro@google.com
asc@google.com
awickham@google.com
beverlyt@google.com
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
new file mode 100644
index 0000000..496f4b3
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 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.compose
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.ButtonColors
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.systemui.compose.theme.LocalAndroidColorScheme
+
+@Composable
+fun SysUiButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ content: @Composable RowScope.() -> Unit,
+) {
+ androidx.compose.material3.Button(
+ modifier = modifier.padding(vertical = 6.dp).height(36.dp),
+ colors = filledButtonColors(),
+ contentPadding = ButtonPaddings,
+ onClick = onClick,
+ enabled = enabled,
+ ) {
+ content()
+ }
+}
+
+@Composable
+fun SysUiOutlinedButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ content: @Composable RowScope.() -> Unit,
+) {
+ androidx.compose.material3.OutlinedButton(
+ modifier = modifier.padding(vertical = 6.dp).height(36.dp),
+ enabled = enabled,
+ colors = outlineButtonColors(),
+ border = outlineButtonBorder(),
+ contentPadding = ButtonPaddings,
+ onClick = onClick,
+ ) {
+ content()
+ }
+}
+
+@Composable
+fun SysUiTextButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ content: @Composable RowScope.() -> Unit,
+) {
+ androidx.compose.material3.TextButton(
+ onClick = onClick,
+ modifier = modifier,
+ enabled = enabled,
+ content = content,
+ )
+}
+
+private val ButtonPaddings = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
+
+@Composable
+private fun filledButtonColors(): ButtonColors {
+ val colors = LocalAndroidColorScheme.current
+ return ButtonDefaults.buttonColors(
+ containerColor = colors.colorAccentPrimary,
+ contentColor = colors.textColorOnAccent,
+ )
+}
+
+@Composable
+private fun outlineButtonColors(): ButtonColors {
+ val colors = LocalAndroidColorScheme.current
+ return ButtonDefaults.outlinedButtonColors(
+ contentColor = colors.textColorPrimary,
+ )
+}
+
+@Composable
+private fun outlineButtonBorder(): BorderStroke {
+ val colors = LocalAndroidColorScheme.current
+ return BorderStroke(
+ width = 1.dp,
+ color = colors.colorAccentPrimaryVariant,
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
new file mode 100644
index 0000000..e1f73e3
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.common.ui.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.systemui.common.shared.model.Text
+
+/** Returns the loaded [String] or `null` if there isn't one. */
+@Composable
+fun Text.load(): String? {
+ return when (this) {
+ is Text.Loaded -> text
+ is Text.Resource -> stringResource(res)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt
new file mode 100644
index 0000000..3175dcf
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2022 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.user.ui.compose
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.systemui.common.ui.compose.load
+import com.android.systemui.compose.SysUiOutlinedButton
+import com.android.systemui.compose.SysUiTextButton
+import com.android.systemui.compose.features.R
+import com.android.systemui.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.user.ui.viewmodel.UserActionViewModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.user.ui.viewmodel.UserViewModel
+import java.lang.Integer.min
+import kotlin.math.ceil
+
+@Composable
+fun UserSwitcherScreen(
+ viewModel: UserSwitcherViewModel,
+ onFinished: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val isFinishRequested: Boolean by viewModel.isFinishRequested.collectAsState(false)
+ val users: List<UserViewModel> by viewModel.users.collectAsState(emptyList())
+ val maxUserColumns: Int by viewModel.maximumUserColumns.collectAsState(1)
+ val menuActions: List<UserActionViewModel> by viewModel.menu.collectAsState(emptyList())
+ val isOpenMenuButtonVisible: Boolean by viewModel.isOpenMenuButtonVisible.collectAsState(false)
+ val isMenuVisible: Boolean by viewModel.isMenuVisible.collectAsState(false)
+
+ UserSwitcherScreenStateless(
+ isFinishRequested = isFinishRequested,
+ users = users,
+ maxUserColumns = maxUserColumns,
+ menuActions = menuActions,
+ isOpenMenuButtonVisible = isOpenMenuButtonVisible,
+ isMenuVisible = isMenuVisible,
+ onMenuClosed = viewModel::onMenuClosed,
+ onOpenMenuButtonClicked = viewModel::onOpenMenuButtonClicked,
+ onCancelButtonClicked = viewModel::onCancelButtonClicked,
+ onFinished = {
+ onFinished()
+ viewModel.onFinished()
+ },
+ modifier = modifier,
+ )
+}
+
+@Composable
+private fun UserSwitcherScreenStateless(
+ isFinishRequested: Boolean,
+ users: List<UserViewModel>,
+ maxUserColumns: Int,
+ menuActions: List<UserActionViewModel>,
+ isOpenMenuButtonVisible: Boolean,
+ isMenuVisible: Boolean,
+ onMenuClosed: () -> Unit,
+ onOpenMenuButtonClicked: () -> Unit,
+ onCancelButtonClicked: () -> Unit,
+ onFinished: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ LaunchedEffect(isFinishRequested) {
+ if (isFinishRequested) {
+ onFinished()
+ }
+ }
+
+ Box(
+ modifier =
+ modifier
+ .fillMaxSize()
+ .padding(
+ horizontal = 60.dp,
+ vertical = 40.dp,
+ ),
+ ) {
+ UserGrid(
+ users = users,
+ maxUserColumns = maxUserColumns,
+ modifier = Modifier.align(Alignment.Center),
+ )
+
+ Buttons(
+ menuActions = menuActions,
+ isOpenMenuButtonVisible = isOpenMenuButtonVisible,
+ isMenuVisible = isMenuVisible,
+ onMenuClosed = onMenuClosed,
+ onOpenMenuButtonClicked = onOpenMenuButtonClicked,
+ onCancelButtonClicked = onCancelButtonClicked,
+ modifier = Modifier.align(Alignment.BottomEnd),
+ )
+ }
+}
+
+@Composable
+private fun UserGrid(
+ users: List<UserViewModel>,
+ maxUserColumns: Int,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(44.dp),
+ modifier = modifier,
+ ) {
+ val rowCount = ceil(users.size / maxUserColumns.toFloat()).toInt()
+ (0 until rowCount).forEach { rowIndex ->
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(64.dp),
+ modifier = modifier,
+ ) {
+ val fromIndex = rowIndex * maxUserColumns
+ val toIndex = min(users.size, (rowIndex + 1) * maxUserColumns)
+ users.subList(fromIndex, toIndex).forEach { user ->
+ UserItem(
+ viewModel = user,
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun UserItem(
+ viewModel: UserViewModel,
+) {
+ val onClicked = viewModel.onClicked
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier =
+ if (onClicked != null) {
+ Modifier.clickable { onClicked() }
+ } else {
+ Modifier
+ }
+ .alpha(viewModel.alpha),
+ ) {
+ Box {
+ UserItemBackground(modifier = Modifier.align(Alignment.Center).size(222.dp))
+
+ UserItemIcon(
+ image = viewModel.image,
+ isSelectionMarkerVisible = viewModel.isSelectionMarkerVisible,
+ modifier = Modifier.align(Alignment.Center).size(222.dp)
+ )
+ }
+
+ // User name
+ val text = viewModel.name.load()
+ if (text != null) {
+ // We use the box to center-align the text vertically as that is not possible with Text
+ // alone.
+ Box(
+ modifier = Modifier.size(width = 222.dp, height = 48.dp),
+ ) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.titleLarge,
+ color = colorResource(com.android.internal.R.color.system_neutral1_50),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun UserItemBackground(
+ modifier: Modifier = Modifier,
+) {
+ Image(
+ painter = ColorPainter(LocalAndroidColorScheme.current.colorBackground),
+ contentDescription = null,
+ modifier = modifier.clip(CircleShape),
+ )
+}
+
+@Composable
+private fun UserItemIcon(
+ image: Drawable,
+ isSelectionMarkerVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Image(
+ bitmap = image.toBitmap().asImageBitmap(),
+ contentDescription = null,
+ modifier =
+ if (isSelectionMarkerVisible) {
+ // Draws a ring
+ modifier.border(
+ width = 8.dp,
+ color = LocalAndroidColorScheme.current.colorAccentPrimary,
+ shape = CircleShape,
+ )
+ } else {
+ modifier
+ }
+ .padding(16.dp)
+ .clip(CircleShape)
+ )
+}
+
+@Composable
+private fun Buttons(
+ menuActions: List<UserActionViewModel>,
+ isOpenMenuButtonVisible: Boolean,
+ isMenuVisible: Boolean,
+ onMenuClosed: () -> Unit,
+ onOpenMenuButtonClicked: () -> Unit,
+ onCancelButtonClicked: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier,
+ ) {
+ // Cancel button.
+ SysUiTextButton(
+ onClick = onCancelButtonClicked,
+ ) {
+ Text(stringResource(R.string.cancel))
+ }
+
+ // "Open menu" button.
+ if (isOpenMenuButtonVisible) {
+ Spacer(modifier = Modifier.width(8.dp))
+ // To properly use a DropdownMenu in Compose, we need to wrap the button that opens it
+ // and the menu itself in a Box.
+ Box {
+ SysUiOutlinedButton(
+ onClick = onOpenMenuButtonClicked,
+ ) {
+ Text(stringResource(R.string.add))
+ }
+ Menu(
+ viewModel = menuActions,
+ isMenuVisible = isMenuVisible,
+ onMenuClosed = onMenuClosed,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun Menu(
+ viewModel: List<UserActionViewModel>,
+ isMenuVisible: Boolean,
+ onMenuClosed: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val maxItemWidth = LocalConfiguration.current.screenWidthDp.dp / 4
+ DropdownMenu(
+ expanded = isMenuVisible,
+ onDismissRequest = onMenuClosed,
+ modifier =
+ modifier.background(
+ color = MaterialTheme.colorScheme.inverseOnSurface,
+ ),
+ ) {
+ viewModel.forEachIndexed { index, action ->
+ MenuItem(
+ viewModel = action,
+ onClicked = { action.onClicked() },
+ topPadding =
+ if (index == 0) {
+ 16.dp
+ } else {
+ 0.dp
+ },
+ bottomPadding =
+ if (index == viewModel.size - 1) {
+ 16.dp
+ } else {
+ 0.dp
+ },
+ modifier = Modifier.sizeIn(maxWidth = maxItemWidth),
+ )
+ }
+ }
+}
+
+@Composable
+private fun MenuItem(
+ viewModel: UserActionViewModel,
+ onClicked: () -> Unit,
+ topPadding: Dp,
+ bottomPadding: Dp,
+ modifier: Modifier = Modifier,
+) {
+ val context = LocalContext.current
+ val density = LocalDensity.current
+
+ val icon =
+ remember(viewModel.iconResourceId) {
+ val drawable =
+ checkNotNull(AppCompatResources.getDrawable(context, viewModel.iconResourceId))
+ drawable
+ .toBitmap(
+ size = with(density) { 20.dp.toPx() }.toInt(),
+ tintColor = Color.White,
+ )
+ .asImageBitmap()
+ }
+
+ DropdownMenuItem(
+ text = {
+ Text(
+ text = stringResource(viewModel.textResourceId),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ },
+ onClick = onClicked,
+ leadingIcon = {
+ Spacer(modifier = Modifier.width(10.dp))
+ Image(
+ bitmap = icon,
+ contentDescription = null,
+ )
+ },
+ modifier =
+ modifier
+ .heightIn(
+ min = 56.dp,
+ )
+ .padding(
+ start = 18.dp,
+ end = 65.dp,
+ top = topPadding,
+ bottom = bottomPadding,
+ ),
+ )
+}
+
+/**
+ * Converts the [Drawable] to a [Bitmap].
+ *
+ * Note that this is a relatively memory-heavy operation as it allocates a whole bitmap and draws
+ * the `Drawable` onto it. Use sparingly and with care.
+ */
+private fun Drawable.toBitmap(
+ size: Int? = null,
+ tintColor: Color? = null,
+): Bitmap {
+ val bitmap =
+ if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ } else {
+ Bitmap.createBitmap(
+ size ?: intrinsicWidth,
+ size ?: intrinsicHeight,
+ Bitmap.Config.ARGB_8888
+ )
+ }
+ val canvas = Canvas(bitmap)
+ setBounds(0, 0, canvas.width, canvas.height)
+ if (tintColor != null) {
+ setTint(tintColor.toArgb())
+ }
+ draw(canvas)
+ return bitmap
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt
new file mode 100644
index 0000000..881a1def
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ */
+
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+package com.android.systemui.compose.gallery
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.systemui.compose.SysUiButton
+import com.android.systemui.compose.SysUiOutlinedButton
+import com.android.systemui.compose.SysUiTextButton
+
+@Composable
+fun ButtonsScreen(
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ SysUiButton(
+ onClick = {},
+ ) {
+ Text("SysUiButton")
+ }
+
+ SysUiButton(
+ onClick = {},
+ enabled = false,
+ ) {
+ Text("SysUiButton - disabled")
+ }
+
+ SysUiOutlinedButton(
+ onClick = {},
+ ) {
+ Text("SysUiOutlinedButton")
+ }
+
+ SysUiOutlinedButton(
+ onClick = {},
+ enabled = false,
+ ) {
+ Text("SysUiOutlinedButton - disabled")
+ }
+
+ SysUiTextButton(
+ onClick = {},
+ ) {
+ Text("SysUiTextButton")
+ }
+
+ SysUiTextButton(
+ onClick = {},
+ enabled = false,
+ ) {
+ Text("SysUiTextButton - disabled")
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
index bb98fb3..2e6456b 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
@@ -31,6 +31,7 @@
val Typography = ChildScreen("typography") { TypographyScreen() }
val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
+ val Buttons = ChildScreen("buttons") { ButtonsScreen() }
val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
val PeopleEmpty =
@@ -63,6 +64,7 @@
"Material colors" to MaterialColors,
"Android colors" to AndroidColors,
"Example feature" to ExampleFeature,
+ "Buttons" to Buttons,
"People" to People,
)
)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
index 8bf982d..9c7fbe8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
@@ -3,7 +3,9 @@
interface QSContainerController {
fun setCustomizerAnimating(animating: Boolean)
- fun setCustomizerShowing(showing: Boolean)
+ fun setCustomizerShowing(showing: Boolean) = setCustomizerShowing(showing, 0L)
+
+ fun setCustomizerShowing(showing: Boolean, animationDuration: Long)
fun setDetailShowing(showing: Boolean)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
new file mode 100644
index 0000000..5c0a7c8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="20"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+ android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="40"
+ android:propertyName="pathData"
+ android:startOffset="20"
+ android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="40"
+ android:propertyName="pathData"
+ android:startOffset="60"
+ android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
new file mode 100644
index 0000000..a96b748
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="233"
+ android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueTo="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 32871f0..ac131ae 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -84,6 +84,10 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
+ <!-- Dimens for animation for the Bouncer PIN view -->
+ <dimen name="pin_view_trans_y_entry">120dp</dimen>
+ <dimen name="pin_view_trans_y_entry_offset">10dp</dimen>
+
<!-- Spacing around each button used for PIN view -->
<dimen name="num_pad_key_width">72dp</dimen>
<dimen name="num_pad_entry_row_margin_bottom">12dp</dimen>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
new file mode 100644
index 0000000..40bfd83
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="28dp"/>
+ <solid android:color="@color/media_dialog_background" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 93c16e4..b76de5a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -20,6 +20,7 @@
android:id="@+id/media_output_dialog"
android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
+ android:background="@drawable/media_output_dialog_background"
android:orientation="vertical">
<LinearLayout
diff --git a/packages/SystemUI/res/values/bools.xml b/packages/SystemUI/res/values/bools.xml
index 499e24e..c67ac8d 100644
--- a/packages/SystemUI/res/values/bools.xml
+++ b/packages/SystemUI/res/values/bools.xml
@@ -18,4 +18,6 @@
<resources>
<!-- Whether to show the user switcher in quick settings when only a single user is present. -->
<bool name="qs_show_user_switcher_for_single_user">false</bool>
+ <!-- Whether to show a custom biometric prompt size-->
+ <bool name="use_custom_bp_size">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index dd5987b..11dbe4a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -186,7 +186,7 @@
<color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color>
<color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
<color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color>
- <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+ <color name="media_dialog_seekbar_progress">@android:color/system_accent1_200</color>
<color name="media_dialog_button_background">@color/material_dynamic_primary40</color>
<color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9cb4cc4..e76047e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -520,7 +520,7 @@
<dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen>
<dimen name="qs_tile_margin_top_bottom">4dp</dimen>
<dimen name="qs_brightness_margin_top">8dp</dimen>
- <dimen name="qs_brightness_margin_bottom">24dp</dimen>
+ <dimen name="qs_brightness_margin_bottom">16dp</dimen>
<dimen name="qqs_layout_margin_top">16dp</dimen>
<dimen name="qqs_layout_padding_bottom">24dp</dimen>
@@ -945,6 +945,8 @@
<dimen name="biometric_dialog_medium_to_large_translation_offset">100dp</dimen>
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <dimen name="biometric_dialog_width">240dp</dimen>
+ <dimen name="biometric_dialog_height">240dp</dimen>
<!-- Biometric Auth Credential values -->
<dimen name="biometric_auth_icon_size">48dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 0773347..0b0df83 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -17,11 +17,10 @@
package com.android.systemui.shared.recents.model;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.graphics.Bitmap.Config.ARGB_8888;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
-
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
deleted file mode 100644
index 0f937bd..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.shared.system;
-
-import android.app.Activity;
-import android.view.View;
-import android.view.ViewHierarchyEncoder;
-
-import java.io.ByteArrayOutputStream;
-
-public class ActivityCompat {
- private final Activity mWrapped;
-
- public ActivityCompat(Activity activity) {
- mWrapped = activity;
- }
-
- /**
- * @see Activity#registerRemoteAnimations
- */
- public void registerRemoteAnimations(RemoteAnimationDefinitionCompat definition) {
- mWrapped.registerRemoteAnimations(definition.getWrapped());
- }
-
- /**
- * @see Activity#unregisterRemoteAnimations
- */
- public void unregisterRemoteAnimations() {
- mWrapped.unregisterRemoteAnimations();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
deleted file mode 100644
index be99b27..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ /dev/null
@@ -1,85 +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.shared.system;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.os.Handler;
-
-/**
- * Wrapper around internal ActivityOptions creation.
- */
-public abstract class ActivityOptionsCompat {
-
- /**
- * @return ActivityOptions for starting a task in freeform.
- */
- public static ActivityOptions makeFreeformOptions() {
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
- return options;
- }
-
- public static ActivityOptions makeRemoteAnimation(
- RemoteAnimationAdapterCompat remoteAnimationAdapter) {
- return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped(),
- remoteAnimationAdapter.getRemoteTransition().getTransition());
- }
-
- /**
- * Constructs an ActivityOptions object that will delegate its transition handling to a
- * `remoteTransition`.
- */
- public static ActivityOptions makeRemoteTransition(RemoteTransitionCompat remoteTransition) {
- return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
- }
-
- /**
- * Returns ActivityOptions for overriding task transition animation.
- */
- public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
- int exitResId, final Runnable callback, final Handler callbackHandler) {
- return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
- callbackHandler,
- new ActivityOptions.OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted(long elapsedRealTime) {
- if (callback != null) {
- callbackHandler.post(callback);
- }
- }
- }, null /* finishedListener */);
- }
-
- /**
- * Sets the flag to freeze the recents task list reordering as a part of launching the activity.
- */
- public static ActivityOptions setFreezeRecentTasksList(ActivityOptions opts) {
- opts.setFreezeRecentTasksReordering();
- return opts;
- }
-
- /**
- * Sets the launch event time from launcher.
- */
- public static ActivityOptions setLauncherSourceInfo(ActivityOptions opts, long uptimeMillis) {
- opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis);
- return opts;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java
deleted file mode 100644
index 1de740a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java
+++ /dev/null
@@ -1,28 +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 com.android.systemui.shared.system;
-
-import android.annotation.UserIdInt;
-import android.content.Context;
-
-public class ContextUtils {
-
- /** Get the user associated with this context */
- public static @UserIdInt int getUserId(Context context) {
- return context.getUserId();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
deleted file mode 100644
index c42e7e3..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 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.shared.system;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-
-public class KeyguardManagerCompat {
- private final KeyguardManager mKeyguardManager;
-
- public KeyguardManagerCompat(Context context) {
- mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- }
-
- public boolean isDeviceLocked(int userId) {
- return mKeyguardManager.isDeviceLocked(userId);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
deleted file mode 100644
index e6ae19e..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2018 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.shared.system;
-
-import android.content.Context;
-
-import com.android.internal.util.LatencyTracker;
-
-/**
- * @see LatencyTracker
- */
-public class LatencyTrackerCompat {
-
- /** @see LatencyTracker */
- public static void logToggleRecents(Context context, int duration) {
- LatencyTracker.getInstance(context).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
- duration);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 33e8e35..09cf7c5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -58,7 +58,7 @@
mRemoteTransition = buildRemoteTransition(runner, appThread);
}
- RemoteAnimationAdapter getWrapped() {
+ public RemoteAnimationAdapter getWrapped() {
return mWrapped;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
index 098698a..ab55037 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
@@ -34,7 +34,7 @@
mWrapped.addRemoteAnimation(transition, activityTypeFilter, adapter.getWrapped());
}
- RemoteAnimationDefinition getWrapped() {
+ public RemoteAnimationDefinition getWrapped() {
return mWrapped;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
deleted file mode 100644
index 30c062b..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2018 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.shared.system;
-
-import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.os.Trace;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.View;
-import android.view.ViewRootImpl;
-
-import java.util.function.Consumer;
-
-/**
- * Helper class to apply surface transactions in sync with RenderThread.
- *
- * NOTE: This is a modification of {@link android.view.SyncRtSurfaceTransactionApplier}, we can't
- * currently reference that class from the shared lib as it is hidden.
- */
-public class SyncRtSurfaceTransactionApplierCompat {
-
- public static final int FLAG_ALL = 0xffffffff;
- public static final int FLAG_ALPHA = 1;
- public static final int FLAG_MATRIX = 1 << 1;
- public static final int FLAG_WINDOW_CROP = 1 << 2;
- public static final int FLAG_LAYER = 1 << 3;
- public static final int FLAG_CORNER_RADIUS = 1 << 4;
- public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
- public static final int FLAG_VISIBILITY = 1 << 6;
- public static final int FLAG_RELATIVE_LAYER = 1 << 7;
- public static final int FLAG_SHADOW_RADIUS = 1 << 8;
-
- private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
-
- private final SurfaceControl mBarrierSurfaceControl;
- private final ViewRootImpl mTargetViewRootImpl;
- private final Handler mApplyHandler;
-
- private int mSequenceNumber = 0;
- private int mPendingSequenceNumber = 0;
- private Runnable mAfterApplyCallback;
-
- /**
- * @param targetView The view in the surface that acts as synchronization anchor.
- */
- public SyncRtSurfaceTransactionApplierCompat(View targetView) {
- mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
- mBarrierSurfaceControl = mTargetViewRootImpl != null
- ? mTargetViewRootImpl.getSurfaceControl() : null;
-
- mApplyHandler = new Handler(new Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
- onApplyMessage(msg.arg1);
- return true;
- }
- return false;
- }
- });
- }
-
- private void onApplyMessage(int seqNo) {
- mSequenceNumber = seqNo;
- if (mSequenceNumber == mPendingSequenceNumber && mAfterApplyCallback != null) {
- Runnable r = mAfterApplyCallback;
- mAfterApplyCallback = null;
- r.run();
- }
- }
-
- /**
- * Schedules applying surface parameters on the next frame.
- *
- * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
- * this method to avoid synchronization issues.
- */
- public void scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params) {
- if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) {
- return;
- }
-
- mPendingSequenceNumber++;
- final int toApplySeqNo = mPendingSequenceNumber;
- mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
- @Override
- public void onFrameDraw(long frame) {
- if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {
- Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
- .sendToTarget();
- return;
- }
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Sync transaction frameNumber=" + frame);
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
- params[i];
- surfaceParams.applyTo(t);
- }
- if (mTargetViewRootImpl != null) {
- mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
- } else {
- t.apply();
- }
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
- .sendToTarget();
- }
- });
-
- // Make sure a frame gets scheduled.
- mTargetViewRootImpl.getView().invalidate();
- }
-
- /**
- * Calls the runnable when any pending apply calls have completed
- */
- public void addAfterApplyCallback(final Runnable afterApplyCallback) {
- if (mSequenceNumber == mPendingSequenceNumber) {
- afterApplyCallback.run();
- } else {
- if (mAfterApplyCallback == null) {
- mAfterApplyCallback = afterApplyCallback;
- } else {
- final Runnable oldCallback = mAfterApplyCallback;
- mAfterApplyCallback = new Runnable() {
- @Override
- public void run() {
- afterApplyCallback.run();
- oldCallback.run();
- }
- };
- }
- }
- }
-
- public static void applyParams(TransactionCompat t,
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
- params.applyTo(t.mTransaction);
- }
-
- /**
- * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
- * attached if necessary.
- */
- public static void create(final View targetView,
- final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
- if (targetView == null) {
- // No target view, no applier
- callback.accept(null);
- } else if (targetView.getViewRootImpl() != null) {
- // Already attached, we're good to go
- callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
- } else {
- // Haven't been attached before we can get the view root
- targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- targetView.removeOnAttachStateChangeListener(this);
- callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- // Do nothing
- }
- });
- }
- }
-
- public static class SurfaceParams {
- public static class Builder {
- final SurfaceControl surface;
- int flags;
- float alpha;
- float cornerRadius;
- int backgroundBlurRadius;
- Matrix matrix;
- Rect windowCrop;
- int layer;
- SurfaceControl relativeTo;
- int relativeLayer;
- boolean visible;
- float shadowRadius;
-
- /**
- * @param surface The surface to modify.
- */
- public Builder(SurfaceControl surface) {
- this.surface = surface;
- }
-
- /**
- * @param alpha The alpha value to apply to the surface.
- * @return this Builder
- */
- public Builder withAlpha(float alpha) {
- this.alpha = alpha;
- flags |= FLAG_ALPHA;
- return this;
- }
-
- /**
- * @param matrix The matrix to apply to the surface.
- * @return this Builder
- */
- public Builder withMatrix(Matrix matrix) {
- this.matrix = new Matrix(matrix);
- flags |= FLAG_MATRIX;
- return this;
- }
-
- /**
- * @param windowCrop The window crop to apply to the surface.
- * @return this Builder
- */
- public Builder withWindowCrop(Rect windowCrop) {
- this.windowCrop = new Rect(windowCrop);
- flags |= FLAG_WINDOW_CROP;
- return this;
- }
-
- /**
- * @param layer The layer to assign the surface.
- * @return this Builder
- */
- public Builder withLayer(int layer) {
- this.layer = layer;
- flags |= FLAG_LAYER;
- return this;
- }
-
- /**
- * @param relativeTo The surface that's set relative layer to.
- * @param relativeLayer The relative layer.
- * @return this Builder
- */
- public Builder withRelativeLayerTo(SurfaceControl relativeTo, int relativeLayer) {
- this.relativeTo = relativeTo;
- this.relativeLayer = relativeLayer;
- flags |= FLAG_RELATIVE_LAYER;
- return this;
- }
-
- /**
- * @param radius the Radius for rounded corners to apply to the surface.
- * @return this Builder
- */
- public Builder withCornerRadius(float radius) {
- this.cornerRadius = radius;
- flags |= FLAG_CORNER_RADIUS;
- return this;
- }
-
- /**
- * @param radius the Radius for the shadows to apply to the surface.
- * @return this Builder
- */
- public Builder withShadowRadius(float radius) {
- this.shadowRadius = radius;
- flags |= FLAG_SHADOW_RADIUS;
- return this;
- }
-
- /**
- * @param radius the Radius for blur to apply to the background surfaces.
- * @return this Builder
- */
- public Builder withBackgroundBlur(int radius) {
- this.backgroundBlurRadius = radius;
- flags |= FLAG_BACKGROUND_BLUR_RADIUS;
- return this;
- }
-
- /**
- * @param visible The visibility to apply to the surface.
- * @return this Builder
- */
- public Builder withVisibility(boolean visible) {
- this.visible = visible;
- flags |= FLAG_VISIBILITY;
- return this;
- }
-
- /**
- * @return a new SurfaceParams instance
- */
- public SurfaceParams build() {
- return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
- relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible,
- shadowRadius);
- }
- }
-
- private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
- Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
- float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius) {
- this.flags = flags;
- this.surface = surface;
- this.alpha = alpha;
- this.matrix = matrix;
- this.windowCrop = windowCrop;
- this.layer = layer;
- this.relativeTo = relativeTo;
- this.relativeLayer = relativeLayer;
- this.cornerRadius = cornerRadius;
- this.backgroundBlurRadius = backgroundBlurRadius;
- this.visible = visible;
- this.shadowRadius = shadowRadius;
- }
-
- private final int flags;
- private final float[] mTmpValues = new float[9];
-
- public final SurfaceControl surface;
- public final float alpha;
- public final float cornerRadius;
- public final int backgroundBlurRadius;
- public final Matrix matrix;
- public final Rect windowCrop;
- public final int layer;
- public final SurfaceControl relativeTo;
- public final int relativeLayer;
- public final boolean visible;
- public final float shadowRadius;
-
- public void applyTo(SurfaceControl.Transaction t) {
- if ((flags & FLAG_MATRIX) != 0) {
- t.setMatrix(surface, matrix, mTmpValues);
- }
- if ((flags & FLAG_WINDOW_CROP) != 0) {
- t.setWindowCrop(surface, windowCrop);
- }
- if ((flags & FLAG_ALPHA) != 0) {
- t.setAlpha(surface, alpha);
- }
- if ((flags & FLAG_LAYER) != 0) {
- t.setLayer(surface, layer);
- }
- if ((flags & FLAG_CORNER_RADIUS) != 0) {
- t.setCornerRadius(surface, cornerRadius);
- }
- if ((flags & FLAG_BACKGROUND_BLUR_RADIUS) != 0) {
- t.setBackgroundBlurRadius(surface, backgroundBlurRadius);
- }
- if ((flags & FLAG_VISIBILITY) != 0) {
- if (visible) {
- t.show(surface);
- } else {
- t.hide(surface);
- }
- }
- if ((flags & FLAG_RELATIVE_LAYER) != 0) {
- t.setRelativeLayer(surface, relativeTo, relativeLayer);
- }
- if ((flags & FLAG_SHADOW_RADIUS) != 0) {
- t.setShadowRadius(surface, shadowRadius);
- }
- }
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
deleted file mode 100644
index 43a882a5..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2018 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.shared.system;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-public class TransactionCompat {
-
- final Transaction mTransaction;
-
- final float[] mTmpValues = new float[9];
-
- public TransactionCompat() {
- mTransaction = new Transaction();
- }
-
- public void apply() {
- mTransaction.apply();
- }
-
- public TransactionCompat show(SurfaceControl surfaceControl) {
- mTransaction.show(surfaceControl);
- return this;
- }
-
- public TransactionCompat hide(SurfaceControl surfaceControl) {
- mTransaction.hide(surfaceControl);
- return this;
- }
-
- public TransactionCompat setPosition(SurfaceControl surfaceControl, float x, float y) {
- mTransaction.setPosition(surfaceControl, x, y);
- return this;
- }
-
- public TransactionCompat setSize(SurfaceControl surfaceControl, int w, int h) {
- mTransaction.setBufferSize(surfaceControl, w, h);
- return this;
- }
-
- public TransactionCompat setLayer(SurfaceControl surfaceControl, int z) {
- mTransaction.setLayer(surfaceControl, z);
- return this;
- }
-
- public TransactionCompat setAlpha(SurfaceControl surfaceControl, float alpha) {
- mTransaction.setAlpha(surfaceControl, alpha);
- return this;
- }
-
- public TransactionCompat setOpaque(SurfaceControl surfaceControl, boolean opaque) {
- mTransaction.setOpaque(surfaceControl, opaque);
- return this;
- }
-
- public TransactionCompat setMatrix(SurfaceControl surfaceControl, float dsdx, float dtdx,
- float dtdy, float dsdy) {
- mTransaction.setMatrix(surfaceControl, dsdx, dtdx, dtdy, dsdy);
- return this;
- }
-
- public TransactionCompat setMatrix(SurfaceControl surfaceControl, Matrix matrix) {
- mTransaction.setMatrix(surfaceControl, matrix, mTmpValues);
- return this;
- }
-
- public TransactionCompat setWindowCrop(SurfaceControl surfaceControl, Rect crop) {
- mTransaction.setWindowCrop(surfaceControl, crop);
- return this;
- }
-
- public TransactionCompat setCornerRadius(SurfaceControl surfaceControl, float radius) {
- mTransaction.setCornerRadius(surfaceControl, radius);
- return this;
- }
-
- public TransactionCompat setBackgroundBlurRadius(SurfaceControl surfaceControl, int radius) {
- mTransaction.setBackgroundBlurRadius(surfaceControl, radius);
- return this;
- }
-
- public TransactionCompat setColor(SurfaceControl surfaceControl, float[] color) {
- mTransaction.setColor(surfaceControl, color);
- return this;
- }
-
- public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
- SurfaceControl relativeTo, int z) {
- t.setRelativeLayer(surfaceControl, relativeTo, z);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
deleted file mode 100644
index cfb23f9..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
+++ /dev/null
@@ -1,179 +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 com.android.systemui.shared.system;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-
-import java.util.HashMap;
-
-public class ViewTreeObserverWrapper {
-
- private static final HashMap<OnComputeInsetsListener, ViewTreeObserver>
- sListenerObserverMap = new HashMap<>();
- private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
- sListenerInternalListenerMap = new HashMap<>();
-
- /**
- * Register a callback to be invoked when the invoked when it is time to compute the window's
- * insets.
- *
- * @param observer The observer to be added
- * @param listener The callback to add
- * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
- */
- public static void addOnComputeInsetsListener(
- @NonNull ViewTreeObserver observer, @NonNull OnComputeInsetsListener listener) {
- final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
- final InsetsInfo inOutInfo = new InsetsInfo();
- inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
- inOutInfo.visibleInsets.set(internalInOutInfo.visibleInsets);
- inOutInfo.touchableRegion.set(internalInOutInfo.touchableRegion);
- listener.onComputeInsets(inOutInfo);
- internalInOutInfo.contentInsets.set(inOutInfo.contentInsets);
- internalInOutInfo.visibleInsets.set(inOutInfo.visibleInsets);
- internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
- internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
- };
- sListenerObserverMap.put(listener, observer);
- sListenerInternalListenerMap.put(listener, internalListener);
- observer.addOnComputeInternalInsetsListener(internalListener);
- }
-
- /**
- * Remove a previously installed insets computation callback.
- *
- * @param victim The callback to remove
- * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
- * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
- */
- public static void removeOnComputeInsetsListener(@NonNull OnComputeInsetsListener victim) {
- final ViewTreeObserver observer = sListenerObserverMap.get(victim);
- final OnComputeInternalInsetsListener listener = sListenerInternalListenerMap.get(victim);
- if (observer != null && listener != null) {
- observer.removeOnComputeInternalInsetsListener(listener);
- }
- sListenerObserverMap.remove(victim);
- sListenerInternalListenerMap.remove(victim);
- }
-
- /**
- * Interface definition for a callback to be invoked when layout has
- * completed and the client can compute its interior insets.
- */
- public interface OnComputeInsetsListener {
- /**
- * Callback method to be invoked when layout has completed and the
- * client can compute its interior insets.
- *
- * @param inoutInfo Should be filled in by the implementation with
- * the information about the insets of the window. This is called
- * with whatever values the previous OnComputeInsetsListener
- * returned, if there are multiple such listeners in the window.
- */
- void onComputeInsets(InsetsInfo inoutInfo);
- }
-
- /**
- * Parameters used with OnComputeInsetsListener.
- */
- public final static class InsetsInfo {
-
- /**
- * Offsets from the frame of the window at which the content of
- * windows behind it should be placed.
- */
- public final Rect contentInsets = new Rect();
-
- /**
- * Offsets from the frame of the window at which windows behind it
- * are visible.
- */
- public final Rect visibleInsets = new Rect();
-
- /**
- * Touchable region defined relative to the origin of the frame of the window.
- * Only used when {@link #setTouchableInsets(int)} is called with
- * the option {@link #TOUCHABLE_INSETS_REGION}.
- */
- public final Region touchableRegion = new Region();
-
- /**
- * Option for {@link #setTouchableInsets(int)}: the entire window frame
- * can be touched.
- */
- public static final int TOUCHABLE_INSETS_FRAME =
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
-
- /**
- * Option for {@link #setTouchableInsets(int)}: the area inside of
- * the content insets can be touched.
- */
- public static final int TOUCHABLE_INSETS_CONTENT =
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
-
- /**
- * Option for {@link #setTouchableInsets(int)}: the area inside of
- * the visible insets can be touched.
- */
- public static final int TOUCHABLE_INSETS_VISIBLE =
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-
- /**
- * Option for {@link #setTouchableInsets(int)}: the area inside of
- * the provided touchable region in {@link #touchableRegion} can be touched.
- */
- public static final int TOUCHABLE_INSETS_REGION =
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-
- /**
- * Set which parts of the window can be touched: either
- * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
- * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
- */
- public void setTouchableInsets(int val) {
- mTouchableInsets = val;
- }
-
- int mTouchableInsets;
-
- @Override
- public int hashCode() {
- int result = contentInsets.hashCode();
- result = 31 * result + visibleInsets.hashCode();
- result = 31 * result + touchableRegion.hashCode();
- result = 31 * result + mTouchableInsets;
- return result;
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- final InsetsInfo other = (InsetsInfo) o;
- return mTouchableInsets == other.mTouchableInsets &&
- contentInsets.equals(other.contentInsets) &&
- visibleInsets.equals(other.visibleInsets) &&
- touchableRegion.equals(other.touchableRegion);
- }
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
deleted file mode 100644
index 5577513..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ /dev/null
@@ -1,175 +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.shared.system;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.InsetsController;
-import android.view.InsetsFrameProvider;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-
-public class WindowManagerWrapper {
-
- private static final String TAG = "WindowManagerWrapper";
-
- public static final int TRANSIT_UNSET = WindowManager.TRANSIT_OLD_UNSET;
- public static final int TRANSIT_NONE = WindowManager.TRANSIT_OLD_NONE;
- public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
- public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
- public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_OLD_TASK_OPEN;
- public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_OLD_TASK_CLOSE;
- public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
- public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_OLD_TASK_TO_BACK;
- public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
- public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
- public static final int TRANSIT_WALLPAPER_INTRA_OPEN =
- WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
- public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
- WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
- public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
- public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
- public static final int TRANSIT_KEYGUARD_GOING_AWAY =
- WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
- public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
- WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
- public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
- public static final int TRANSIT_KEYGUARD_UNOCCLUDE =
- WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
-
- public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
- public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
- public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
- public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
-
- public static final int ACTIVITY_TYPE_STANDARD = WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-
- public static final int WINDOWING_MODE_UNDEFINED = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
- public static final int WINDOWING_MODE_FULLSCREEN =
- WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
- public static final int WINDOWING_MODE_MULTI_WINDOW =
- WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-
- public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
- public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
- public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = InsetsState.ITYPE_LEFT_TAPPABLE_ELEMENT;
- public static final int ITYPE_TOP_TAPPABLE_ELEMENT = InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
- public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT;
- public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
- InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
- public static final int ITYPE_SIZE = InsetsState.SIZE;
-
- public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE;
- public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR;
-
- private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
-
- public static WindowManagerWrapper getInstance() {
- return sInstance;
- }
-
-
- /**
- * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
- * @param params The window layout params.
- * @param providesInsetsTypes The inset types we would like this layout params to provide.
- */
- public void setProvidesInsetsTypes(WindowManager.LayoutParams params,
- int[] providesInsetsTypes) {
- final int length = providesInsetsTypes.length;
- params.providedInsets = new InsetsFrameProvider[length];
- for (int i = 0; i < length; i++) {
- params.providedInsets[i] = new InsetsFrameProvider(providesInsetsTypes[i]);
- }
- }
-
- /**
- * Overrides a pending app transition.
- */
- public void overridePendingAppTransitionMultiThumbFuture(
- AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
- Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
- RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
- animStartedCallback), scaleUp, displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
- }
- }
-
- /**
- * Enable or disable haptic feedback on the navigation bar buttons.
- */
- public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
- try {
- WindowManagerGlobal.getWindowManagerService()
- .setNavBarVirtualKeyHapticFeedbackEnabled(enabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e);
- }
- }
-
- /**
- * @param displayId the id of display to check if there is a software navigation bar.
- *
- * @return whether there is a soft nav bar on specific display.
- */
- public boolean hasSoftNavigationBar(int displayId) {
- try {
- return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
- * hierarchy.
- *
- * @param displayId The id of the display to mirror
- * @return The SurfaceControl for the root of the mirrored hierarchy.
- */
- public SurfaceControl mirrorDisplay(final int displayId) {
- try {
- SurfaceControl outSurfaceControl = new SurfaceControl();
- WindowManagerGlobal.getWindowManagerService().mirrorDisplay(displayId,
- outSurfaceControl);
- return outSurfaceControl;
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 919b71b..f82e7db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -55,6 +55,8 @@
val bouncerIsOrWillShow: Boolean,
val faceAuthenticated: Boolean,
val faceDisabled: Boolean,
+ val faceLockedOut: Boolean,
+ val fpLockedOut: Boolean,
val goingToSleep: Boolean,
val keyguardAwakeExcludingBouncerShowing: Boolean,
val keyguardGoingAway: Boolean,
@@ -65,7 +67,7 @@
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
val switchingUser: Boolean,
- val udfpsBouncerShowing: Boolean
+ val udfpsBouncerShowing: Boolean,
) : KeyguardListenModel()
/**
* Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 20fa8f8..453072b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -48,6 +48,9 @@
private ConstraintLayout mContainer;
private int mDisappearYTranslation;
private View[][] mViews;
+ private int mYTrans;
+ private int mYTransOffset;
+ private View mBouncerMessageView;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPINView(Context context) {
@@ -67,6 +70,8 @@
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
+ mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
+ mYTransOffset = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry_offset);
}
@Override
@@ -138,6 +143,7 @@
super.onFinishInflate();
mContainer = findViewById(R.id.pin_container);
+ mBouncerMessageView = findViewById(R.id.bouncer_message_area);
mViews = new View[][]{
new View[]{
findViewById(R.id.row0), null, null
@@ -206,6 +212,12 @@
/** Animate subviews according to expansion or time. */
private void animate(float progress) {
+ Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE;
+ Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
+
+ mBouncerMessageView.setTranslationY(
+ mYTrans - mYTrans * standardDecelerate.getInterpolation(progress));
+
for (int i = 0; i < mViews.length; i++) {
View[] row = mViews[i];
for (View view : row) {
@@ -213,14 +225,15 @@
continue;
}
- float scaledProgress = MathUtils.constrain(
+ float scaledProgress = legacyDecelerate.getInterpolation(MathUtils.constrain(
(progress - 0.075f * i) / (1f - 0.075f * mViews.length),
0f,
1f
- );
+ ));
view.setAlpha(scaledProgress);
- Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
- view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+ int yDistance = mYTrans + mYTransOffset * i;
+ view.setTranslationY(
+ yDistance - (yDistance * standardDecelerate.getInterpolation(progress)));
if (view instanceof NumPadAnimationListener) {
((NumPadAnimationListener) view).setProgress(scaledProgress);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9f4585f..89fcc47 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -82,6 +82,12 @@
}
@Override
+ public void startAppearAnimation() {
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+ super.startAppearAnimation();
+ }
+
+ @Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1463840..5ccab54 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2612,6 +2612,7 @@
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
+ final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2628,7 +2629,7 @@
&& strongAuthAllowsScanning && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
- && !fpLockedout;
+ && !fpOrFaceIsLockedOut;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2643,6 +2644,8 @@
mBouncerIsOrWillBeShowing,
faceAuthenticated,
faceDisabledForUser,
+ isFaceLockedOut(),
+ fpLockedout,
mGoingToSleep,
awakeKeyguardExcludingBouncerShowing,
mKeyguardGoingAway,
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index e0cafae..41111e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -86,7 +86,7 @@
public void setProgress(float progress) {
mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
- int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+ int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress);
int difference = mHeight - height;
mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 9138b23..9cfd399 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Notification;
@@ -29,7 +28,6 @@
import android.os.Bundle;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -130,13 +128,6 @@
ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
- // Enable binder tracing on system server for calls originating from SysUI
- try {
- ActivityManager.getService().enableBinderTracing();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to enable binder tracing", e);
- }
-
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 813f4dd..b976181 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -62,6 +62,7 @@
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -75,7 +76,6 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
import java.text.NumberFormat;
@@ -723,7 +723,7 @@
* child of the surfaceView.
*/
private void createMirror() {
- mMirrorSurface = WindowManagerWrapper.getInstance().mirrorDisplay(mDisplayId);
+ mMirrorSurface = mirrorDisplay(mDisplayId);
if (!mMirrorSurface.isValid()) {
return;
}
@@ -732,6 +732,25 @@
modifyWindowMagnification(false);
}
+ /**
+ * Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
+ * hierarchy.
+ *
+ * @param displayId The id of the display to mirror
+ * @return The SurfaceControl for the root of the mirrored hierarchy.
+ */
+ private SurfaceControl mirrorDisplay(final int displayId) {
+ try {
+ SurfaceControl outSurfaceControl = new SurfaceControl();
+ WindowManagerGlobal.getWindowManagerService().mirrorDisplay(displayId,
+ outSurfaceControl);
+ return outSurfaceControl;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach window manager", e);
+ }
+ return null;
+ }
+
private void addDragTouchListeners() {
mDragView = mMirrorView.findViewById(R.id.drag_handle);
mLeftDrag = mMirrorView.findViewById(R.id.left_handle);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e94b1f8..0ac71c4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -169,6 +169,10 @@
private Animator.AnimatorListener mJankListener;
+ private final boolean mUseCustomBpSize;
+ private final int mCustomBpWidth;
+ private final int mCustomBpHeight;
+
private final OnClickListener mBackgroundClickListener = (view) -> {
if (mState == STATE_AUTHENTICATED) {
Log.w(TAG, "Ignoring background click after authenticated");
@@ -209,6 +213,10 @@
handleResetAfterHelp();
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
};
+
+ mUseCustomBpSize = getResources().getBoolean(R.bool.use_custom_bp_size);
+ mCustomBpWidth = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_width);
+ mCustomBpHeight = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_height);
}
/** Delay after authentication is confirmed, before the dialog should be animated away. */
@@ -834,14 +842,17 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int height = MeasureSpec.getSize(heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
- final int newWidth = Math.min(width, height);
+ if (mUseCustomBpSize) {
+ width = mCustomBpWidth;
+ height = mCustomBpHeight;
+ } else {
+ width = Math.min(width, height);
+ }
- // Use "newWidth" instead, so the landscape dialog width is the same as the portrait
- // width.
- mLayoutParams = onMeasureInternal(newWidth, height);
+ mLayoutParams = onMeasureInternal(width, height);
setMeasuredDimension(mLayoutParams.mMediumWidth, mLayoutParams.mMediumHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SharedLibraryModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SharedLibraryModule.java
index be156157..6b9d41c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SharedLibraryModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SharedLibraryModule.java
@@ -19,7 +19,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import dagger.Module;
import dagger.Provides;
@@ -49,10 +48,4 @@
return TaskStackChangeListeners.getInstance();
}
- /** */
- @Provides
- public WindowManagerWrapper providesWindowManagerWrapper() {
- return WindowManagerWrapper.getInstance();
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 78099d1f..3695df5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -98,7 +98,7 @@
* Flag to enable the usage of the new bouncer data source. This is a refactor of and
* eventual replacement of KeyguardBouncer.java.
*/
- public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
+ public static final UnreleasedFlag MODERN_BOUNCER = new UnreleasedFlag(208);
/** Whether UserSwitcherActivity should use modern architecture. */
public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index b39770d..2d09ddd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -310,6 +310,7 @@
if (icon.getType() != Icon.TYPE_BITMAP && icon.getType() != Icon.TYPE_ADAPTIVE_BITMAP) {
// icon doesn't support getBitmap, use default value for color scheme
updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
} else {
Configuration config = mContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
@@ -319,11 +320,14 @@
if (colorSetUpdated) {
mAdapter.updateColorScheme(wallpaperColors, isDarkThemeOn);
updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
}
}
mHeaderIcon.setVisibility(View.VISIBLE);
mHeaderIcon.setImageIcon(icon);
} else {
+ updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
mHeaderIcon.setVisibility(View.GONE);
}
if (appSourceIcon != null) {
@@ -381,11 +385,16 @@
private void updateButtonBackgroundColorFilter() {
ColorFilter buttonColorFilter = new PorterDuffColorFilter(
- mAdapter.getController().getColorButtonBackground(),
+ mMediaOutputController.getColorButtonBackground(),
PorterDuff.Mode.SRC_IN);
mDoneButton.getBackground().setColorFilter(buttonColorFilter);
mStopButton.getBackground().setColorFilter(buttonColorFilter);
- mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText());
+ mDoneButton.setTextColor(mMediaOutputController.getColorPositiveButtonText());
+ }
+
+ private void updateDialogBackgroundColor() {
+ getDialogView().getBackground().setTint(mMediaOutputController.getColorDialogBackground());
+ mDeviceListLayout.setBackgroundColor(mMediaOutputController.getColorDialogBackground());
}
private Drawable resizeDrawable(Drawable drawable, int size) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 96817c9..f040e06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -133,6 +133,7 @@
@VisibleForTesting
LocalMediaManager mLocalMediaManager;
private MediaOutputMetricLogger mMetricLogger;
+ private int mCurrentState;
private int mColorItemContent;
private int mColorSeekbarProgress;
@@ -140,6 +141,7 @@
private int mColorItemBackground;
private int mColorConnectedItemBackground;
private int mColorPositiveButtonText;
+ private int mColorDialogBackground;
private float mInactiveRadius;
private float mActiveRadius;
@@ -188,6 +190,8 @@
R.dimen.media_output_dialog_background_radius);
mActiveRadius = mContext.getResources().getDimension(
R.dimen.media_output_dialog_active_background_radius);
+ mColorDialogBackground = Utils.getColorStateListDefaultColor(mContext,
+ R.color.media_dialog_background);
}
void start(@NonNull Callback cb) {
@@ -204,6 +208,9 @@
if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
mMediaController = controller;
mMediaController.unregisterCallback(mCb);
+ if (mMediaController.getPlaybackState() != null) {
+ mCurrentState = mMediaController.getPlaybackState().getState();
+ }
mMediaController.registerCallback(mCb);
break;
}
@@ -461,6 +468,7 @@
mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
+ mColorDialogBackground = mCurrentColorScheme.getNeutral1().get(10); // N1-900
} else {
mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
@@ -468,6 +476,7 @@
mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
+ mColorDialogBackground = mCurrentColorScheme.getBackgroundColor();
}
}
@@ -487,6 +496,10 @@
return mColorPositiveButtonText;
}
+ public int getColorDialogBackground() {
+ return mColorDialogBackground;
+ }
+
public int getColorItemContent() {
return mColorItemContent;
}
@@ -976,10 +989,16 @@
@Override
public void onPlaybackStateChanged(PlaybackState playbackState) {
- final int state = playbackState.getState();
- if (state == PlaybackState.STATE_STOPPED || state == PlaybackState.STATE_PAUSED) {
+ final int newState =
+ playbackState == null ? PlaybackState.STATE_STOPPED : playbackState.getState();
+ if (mCurrentState == newState) {
+ return;
+ }
+
+ if (newState == PlaybackState.STATE_STOPPED || newState == PlaybackState.STATE_PAUSED) {
mCallback.onMediaStoppedOrPaused();
}
+ mCurrentState = newState;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 9e1ba5f..9702488 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -39,6 +39,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -51,6 +52,7 @@
import android.view.WindowInsets;
import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.FrameLayout;
@@ -78,7 +80,6 @@
import com.android.systemui.shared.rotation.RotationButtonController;
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.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -775,13 +776,24 @@
updateSlippery();
reloadNavIcons();
updateNavButtonIcons();
- mBgExecutor.execute(() -> WindowManagerWrapper.getInstance()
- .setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi));
+ mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi));
getHomeButton().setAccessibilityDelegate(
mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
}
/**
+ * Enable or disable haptic feedback on the navigation bar buttons.
+ */
+ private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .setNavBarVirtualKeyHapticFeedbackEnabled(enabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e);
+ }
+ }
+
+ /**
* Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up
* is enabled, or the notifications is fully opened without being in an animated state. If
* slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 920f463..e1289a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -99,7 +99,7 @@
// slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
@Nullable
private TouchAnimator mFirstPageAnimator;
- // TranslationX animator for QQS/QS tiles
+ // TranslationX animator for QQS/QS tiles. Only used on the first page!
private TouchAnimator mTranslationXAnimator;
// TranslationY animator for QS tiles (and their components) in the first page
private TouchAnimator mTranslationYAnimator;
@@ -107,13 +107,14 @@
private TouchAnimator mQQSTranslationYAnimator;
// Animates alpha of permanent views (QS tile layout, QQS tiles) when not in first page
private TouchAnimator mNonfirstPageAlphaAnimator;
- // TranslatesY the QS Tile layout using QS.getHeightDiff()
- private TouchAnimator mQSTileLayoutTranslatorAnimator;
// This animates fading of media player
private TouchAnimator mAllPagesDelayedAnimator;
- // Animator for brightness slider(s)
+ // Brightness slider translation driver, uses mQSExpansionPathInterpolator.yInterpolator
@Nullable
- private TouchAnimator mBrightnessAnimator;
+ private TouchAnimator mBrightnessTranslationAnimator;
+ // Brightness slider opacity driver. Uses linear interpolator.
+ @Nullable
+ private TouchAnimator mBrightnessOpacityAnimator;
// Animator for Footer actions in QQS
private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
@@ -137,7 +138,6 @@
private final QSTileHost mHost;
private final Executor mExecutor;
private boolean mShowCollapsedOnKeyguard;
- private boolean mTranslateWhileExpanding;
private int mQQSTop;
private int[] mTmpLoc1 = new int[2];
@@ -298,13 +298,6 @@
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
- int heightDiff = mQs.getHeightDiff();
- if (!mTranslateWhileExpanding) {
- heightDiff *= SHORT_PARALLAX_AMOUNT;
- }
- mQSTileLayoutTranslatorAnimator = new Builder()
- .addFloat(tileLayout, "translationY", heightDiff, 0)
- .build();
mLastQQSTileHeight = 0;
@@ -407,12 +400,7 @@
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
mAllViews.add(quickTileView.getSecondaryLabel());
- } else if (isIconInAnimatedRow(count)) {
-
- firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
-
- mAllViews.add(tileIcon);
- } else {
+ } else if (!isIconInAnimatedRow(count)) {
// Pretend there's a corresponding QQS tile (for the position) that we are
// expanding from.
SideLabelTileLayout qqsLayout =
@@ -442,7 +430,7 @@
}
}
- animateBrightnessSlider(firstPageBuilder);
+ animateBrightnessSlider();
mFirstPageAnimator = firstPageBuilder
// Fade in the tiles/labels as we reach the final position.
@@ -568,7 +556,9 @@
return new Pair<>(animator, builder.build());
}
- private void animateBrightnessSlider(Builder firstPageBuilder) {
+ private void animateBrightnessSlider() {
+ mBrightnessTranslationAnimator = null;
+ mBrightnessOpacityAnimator = null;
View qsBrightness = mQsPanelController.getBrightnessView();
View qqsBrightness = mQuickQSPanelController.getBrightnessView();
if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) {
@@ -576,25 +566,45 @@
mAnimatedQsViews.add(qsBrightness);
mAllViews.add(qqsBrightness);
int translationY = getRelativeTranslationY(qsBrightness, qqsBrightness);
- mBrightnessAnimator = new Builder()
+ mBrightnessTranslationAnimator = new Builder()
// we need to animate qs brightness even if animation will not be visible,
// as we might start from sliderScaleY set to 0.3 if device was in collapsed QS
// portrait orientation before
.addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
.addFloat(qqsBrightness, "translationY", 0, translationY)
+ .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
.build();
} else if (qsBrightness != null) {
- firstPageBuilder.addFloat(qsBrightness, "translationY",
- qsBrightness.getMeasuredHeight() * 0.5f, 0);
- mBrightnessAnimator = new Builder()
+ // The brightness slider's visible bottom edge must maintain a constant margin from the
+ // QS tiles during transition. Thus the slider must (1) perform the same vertical
+ // translation as the tiles, and (2) compensate for the slider scaling.
+
+ // For (1), compute the distance via the vertical distance between QQS and QS tile
+ // layout top.
+ View quickSettingsRootView = mQs.getView();
+ View qsTileLayout = (View) mQsPanelController.getTileLayout();
+ View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
+ getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
+ getRelativePosition(mTmpLoc2, qqsTileLayout, quickSettingsRootView);
+ int tileMovement = mTmpLoc2[1] - mTmpLoc1[1];
+
+ // For (2), the slider scales to the vertical center, so compensate with half the
+ // height at full collapse.
+ float scaleCompensation = qsBrightness.getMeasuredHeight() * 0.5f;
+ mBrightnessTranslationAnimator = new Builder()
+ .addFloat(qsBrightness, "translationY", scaleCompensation + tileMovement, 0)
+ .addFloat(qsBrightness, "sliderScaleY", 0, 1)
+ .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
+ .build();
+
+ // While the slider's position and unfurl is animated throughouth the motion, the
+ // fade in happens independently.
+ mBrightnessOpacityAnimator = new Builder()
.addFloat(qsBrightness, "alpha", 0, 1)
- .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
- .setInterpolator(Interpolators.ALPHA_IN)
- .setStartDelay(0.3f)
+ .setStartDelay(0.2f)
+ .setEndDelay(1 - 0.5f)
.build();
mAllViews.add(qsBrightness);
- } else {
- mBrightnessAnimator = null;
}
}
@@ -676,11 +686,13 @@
if (mQQSTileHeightAnimator != null) {
mQQSTileHeightAnimator.setPosition(position);
}
- mQSTileLayoutTranslatorAnimator.setPosition(position);
mQQSTranslationYAnimator.setPosition(position);
mAllPagesDelayedAnimator.setPosition(position);
- if (mBrightnessAnimator != null) {
- mBrightnessAnimator.setPosition(position);
+ if (mBrightnessOpacityAnimator != null) {
+ mBrightnessOpacityAnimator.setPosition(position);
+ }
+ if (mBrightnessTranslationAnimator != null) {
+ mBrightnessTranslationAnimator.setPosition(position);
}
if (mQQSFooterActionsAnimator != null) {
mQQSFooterActionsAnimator.setPosition(position);
@@ -774,13 +786,6 @@
setCurrentPosition();
};
- /**
- * True whe QS will be pulled from the top, false when it will be clipped.
- */
- public void setTranslateWhileExpanding(boolean shouldTranslate) {
- mTranslateWhileExpanding = shouldTranslate;
- }
-
private static class HeightExpansionAnimator {
private final List<View> mViews = new ArrayList<>();
private final ValueAnimator mAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index b02efba..de11d56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -39,8 +39,15 @@
mBackground = (TransitionDrawable) detail.getBackground();
}
- public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
- updateCircularClip(true /* animate */, x, y, in, listener);
+ /**
+ * @param x x position where animation should originate
+ * @param y y position where animation should originate
+ * @param in whether animating in or out
+ * @param listener Animation listener. Called whether or not {@code animate} is true.
+ * @return the duration of the circular animator
+ */
+ public long animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
+ return updateCircularClip(true /* animate */, x, y, in, listener);
}
/**
@@ -50,8 +57,9 @@
* @param y y position where animation should originate
* @param in whether animating in or out
* @param listener Animation listener. Called whether or not {@code animate} is true.
+ * @return the duration of the circular animator
*/
- public void updateCircularClip(boolean animate, int x, int y, boolean in,
+ public long updateCircularClip(boolean animate, int x, int y, boolean in,
AnimatorListener listener) {
if (mAnimator != null) {
mAnimator.cancel();
@@ -87,6 +95,7 @@
mAnimator.addListener(mGoneOnEnd);
}
mAnimator.start();
+ return mAnimator.getDuration();
}
private final Runnable mReverseBackground = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 072c32f..3820500 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -573,7 +573,6 @@
@Override
public void setInSplitShade(boolean inSplitShade) {
mInSplitShade = inSplitShade;
- mQSAnimator.setTranslateWhileExpanding(inSplitShade);
updateShowCollapsedOnKeyguard();
updateQsState();
}
@@ -669,7 +668,11 @@
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
- mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
+
+ float qsScrollViewTranslation =
+ onKeyguard && !mShowCollapsedOnKeyguard ? panelTranslationY : 0;
+ mQSPanelScrollView.setTranslationY(qsScrollViewTranslation);
+
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7155626..448e180 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -49,7 +49,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable {
@@ -291,7 +290,16 @@
} else {
topOffset = tileHeightOffset;
}
- int top = Objects.requireNonNull(mChildrenLayoutTop.get(child));
+ // Animation can occur before the layout pass, meaning setSquishinessFraction() gets
+ // called before onLayout(). So, a child view could be null because it has not
+ // been added to mChildrenLayoutTop yet (which happens in onLayout()).
+ // We use a continue statement here to catch this NPE because, on the layout pass,
+ // this code will be called again from onLayout() with the populated children views.
+ Integer childLayoutTop = mChildrenLayoutTop.get(child);
+ if (childLayoutTop == null) {
+ continue;
+ }
+ int top = childLayoutTop;
child.setLeftTopRightBottom(child.getLeft(), top + topOffset,
child.getRight(), top + topOffset + child.getHeight());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 8ad0119..cf10c79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -125,9 +125,10 @@
isShown = true;
mOpening = true;
setVisibility(View.VISIBLE);
- mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
+ long duration = mClipper.animateCircularClip(
+ mX, mY, true, new ExpandAnimatorListener(tileAdapter));
mQsContainerController.setCustomizerAnimating(true);
- mQsContainerController.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerShowing(true, duration);
}
}
@@ -153,13 +154,14 @@
// Make sure we're not opening (because we're closing). Nobody can think we are
// customizing after the next two lines.
mOpening = false;
+ long duration = 0;
if (animate) {
- mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
+ duration = mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
} else {
setVisibility(View.GONE);
}
mQsContainerController.setCustomizerAnimating(animate);
- mQsContainerController.setCustomizerShowing(false);
+ mQsContainerController.setCustomizerShowing(false, duration);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index e2964ea..f60e066 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles;
import android.app.UiModeManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Handler;
@@ -60,9 +61,7 @@
ConfigurationController.ConfigurationListener,
BatteryController.BatteryStateChangeCallback {
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
- private final Icon mIcon = ResourceIcon.get(
- com.android.internal.R.drawable.ic_qs_ui_mode_night);
- private UiModeManager mUiModeManager;
+ private final UiModeManager mUiModeManager;
private final BatteryController mBatteryController;
private final LocationController mLocationController;
@Inject
@@ -82,7 +81,8 @@
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mBatteryController = batteryController;
- mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
+ mUiModeManager = (UiModeManager) host.getUserContext().getSystemService(
+ Context.UI_MODE_SERVICE);
mLocationController = locationController;
configurationController.observe(getLifecycle(), this);
batteryController.observe(getLifecycle(), this);
@@ -155,7 +155,6 @@
}
state.value = nightMode;
state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label);
- state.icon = mIcon;
state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
? state.label
: TextUtils.concat(state.label, ", ", state.secondaryLabel);
@@ -164,6 +163,9 @@
} else {
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ state.icon = ResourceIcon.get(state.state == Tile.STATE_ACTIVE
+ ? R.drawable.qs_light_dark_theme_icon_on
+ : R.drawable.qs_light_dark_theme_icon_off);
state.showRippleEffect = false;
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 06c80a1..2ee5f05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -37,10 +37,12 @@
import android.text.SpannableStringBuilder;
import android.text.style.BulletSpan;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.Button;
@@ -54,7 +56,6 @@
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
@@ -67,6 +68,7 @@
public class ScreenPinningRequest implements View.OnClickListener,
NavigationModeController.ModeChangedListener {
+ private static final String TAG = "ScreenPinningRequest";
private final Context mContext;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -248,9 +250,8 @@
mLayout.findViewById(R.id.screen_pinning_text_area)
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
- WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
+ && hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
@@ -321,6 +322,20 @@
addView(mLayout, getRequestLayoutParams(rotation));
}
+ /**
+ * @param displayId the id of display to check if there is a software navigation bar.
+ *
+ * @return whether there is a soft nav bar on specific display.
+ */
+ private boolean hasSoftNavigationBar(int displayId) {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to check soft navigation bar", e);
+ return false;
+ }
+ }
+
private void swapChildrenIfRtlAndVertical(View group) {
if (mContext.getResources().getConfiguration().getLayoutDirection()
!= View.LAYOUT_DIRECTION_RTL) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 6b32daf..fe40d4c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -30,6 +30,7 @@
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -310,6 +311,14 @@
updateVisibility()
}
+ fun startCustomizingAnimation(show: Boolean, duration: Long) {
+ header.animate()
+ .setDuration(duration)
+ .alpha(if (show) 0f else 1f)
+ .setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
+ .start()
+ }
+
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 156b123..3429b9d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -443,6 +443,8 @@
private boolean mQsTouchAboveFalsingThreshold;
private int mQsFalsingThreshold;
+ /** Indicates drag starting height when swiping down or up on heads-up notifications */
+ private int mHeadsUpStartHeight;
private HeadsUpTouchHelper mHeadsUpTouchHelper;
private boolean mListenForHeadsUp;
private int mNavigationBarBottomHeight;
@@ -645,6 +647,8 @@
/** The drag distance required to fully expand the split shade. */
private int mSplitShadeFullTransitionDistance;
+ /** The drag distance required to fully transition scrims. */
+ private int mSplitShadeScrimTransitionDistance;
private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@@ -1052,6 +1056,8 @@
mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
R.dimen.notification_side_paddings);
mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
+ mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize(
+ R.dimen.split_shade_scrim_transition_distance);
}
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -1335,7 +1341,7 @@
mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mQsSizeChangeAnimator.addUpdateListener(animation -> {
requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
mQs.setHeightOverride(height);
});
@@ -2114,7 +2120,7 @@
mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
setQsExpandImmediate(true);
setShowShelfOnly(true);
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
// Normally, we start listening when the panel is expanded, but here we need to start
// earlier so the state is already up to date when dragging down.
@@ -2326,7 +2332,7 @@
// Reset scroll position and apply that position to the expanded height.
float height = mQsExpansionHeight;
setQsExpansion(height);
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
// When expanding QS, let's authenticate the user if possible,
@@ -2342,7 +2348,7 @@
if (changed) {
mQsExpanded = expanded;
updateQsState();
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
mFalsingCollector.setQsExpanded(expanded);
mCentralSurfaces.setQsExpanded(expanded);
mNotificationsQSContainerController.setQsExpanded(expanded);
@@ -3066,16 +3072,7 @@
int maxHeight;
if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
|| mPulsing || mSplitShadeEnabled) {
- if (mSplitShadeEnabled && mBarState == SHADE) {
- // Max panel height is used to calculate the fraction of the shade expansion.
- // Traditionally the value is based on the number of notifications.
- // On split-shade, we want the required distance to be a specific and constant
- // value, to make sure the expansion motion has the expected speed.
- // We also only want this on non-lockscreen for now.
- maxHeight = mSplitShadeFullTransitionDistance;
- } else {
- maxHeight = calculatePanelHeightQsExpanded();
- }
+ maxHeight = calculatePanelHeightQsExpanded();
} else {
maxHeight = calculatePanelHeightShade();
}
@@ -3434,6 +3431,31 @@
}
@Override
+ public int getMaxPanelTransitionDistance() {
+ // Traditionally the value is based on the number of notifications. On split-shade, we want
+ // the required distance to be a specific and constant value, to make sure the expansion
+ // motion has the expected speed. We also only want this on non-lockscreen for now.
+ if (mSplitShadeEnabled && mBarState == SHADE) {
+ boolean transitionFromHeadsUp =
+ mHeadsUpManager.isTrackingHeadsUp() || mExpandingFromHeadsUp;
+ // heads-up starting height is too close to mSplitShadeFullTransitionDistance and
+ // when dragging HUN transition is already 90% complete. It makes shade become
+ // immediately visible when starting to drag. We want to set distance so that
+ // nothing is immediately visible when dragging (important for HUN swipe up motion) -
+ // 0.4 expansion fraction is a good starting point.
+ if (transitionFromHeadsUp) {
+ double maxDistance = Math.max(mSplitShadeFullTransitionDistance,
+ mHeadsUpStartHeight * 2.5);
+ return (int) Math.min(getMaxPanelHeight(), maxDistance);
+ } else {
+ return mSplitShadeFullTransitionDistance;
+ }
+ } else {
+ return getMaxPanelHeight();
+ }
+ }
+
+ @Override
protected boolean isTrackingBlocked() {
return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
}
@@ -3632,10 +3654,35 @@
}
/**
+ * Called when heads-up notification is being dragged up or down to indicate what's the starting
+ * height for shade motion
+ */
+ public void setHeadsUpDraggingStartingHeight(int startHeight) {
+ mHeadsUpStartHeight = startHeight;
+ float scrimMinFraction;
+ if (mSplitShadeEnabled) {
+ boolean highHun = mHeadsUpStartHeight * 2.5 > mSplitShadeScrimTransitionDistance;
+ // if HUN height is higher than 40% of predefined transition distance, it means HUN
+ // is too high for regular transition. In that case we need to calculate transition
+ // distance - here we take scrim transition distance as equal to shade transition
+ // distance. It doesn't result in perfect motion - usually scrim transition distance
+ // should be longer - but it's good enough for HUN case.
+ float transitionDistance =
+ highHun ? getMaxPanelTransitionDistance() : mSplitShadeFullTransitionDistance;
+ scrimMinFraction = mHeadsUpStartHeight / transitionDistance;
+ } else {
+ int transitionDistance = getMaxPanelHeight();
+ scrimMinFraction = transitionDistance > 0f
+ ? (float) mHeadsUpStartHeight / transitionDistance : 0f;
+ }
+ setPanelScrimMinFraction(scrimMinFraction);
+ }
+
+ /**
* Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain
* cases, such as if there's a heads-up notification.
*/
- public void setPanelScrimMinFraction(float minFraction) {
+ private void setPanelScrimMinFraction(float minFraction) {
mMinFraction = minFraction;
mDepthController.setPanelPullDownMinFraction(mMinFraction);
mScrimController.setPanelScrimMinFraction(mMinFraction);
@@ -4368,7 +4415,7 @@
if (mKeyguardShowing) {
updateMaxDisplayedNotifications(true);
}
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
}
@Override
@@ -4501,7 +4548,7 @@
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
}
if (mAccessibilityManager.isEnabled()) {
mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
@@ -4770,7 +4817,7 @@
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
// Size has changed, start an animation.
if (mQsMaxExpansionHeight != oldMaxHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 2a46776..d6f0de8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -35,6 +35,7 @@
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
+ private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController,
private val featureFlags: FeatureFlags,
@Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
@@ -156,9 +157,12 @@
}
}
- override fun setCustomizerShowing(showing: Boolean) {
- isQSCustomizing = showing
- updateBottomSpacing()
+ override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) {
+ if (showing != isQSCustomizing) {
+ isQSCustomizing = showing
+ largeScreenShadeHeaderController.startCustomizingAnimation(showing, animationDuration)
+ updateBottomSpacing()
+ }
}
override fun setDetailShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index a0076937..c3f1e57 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -248,7 +248,7 @@
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
}
});
mAmbientState = ambientState;
@@ -730,7 +730,7 @@
setExpandedHeightInternal(height);
}
- protected void requestPanelHeightUpdate() {
+ void updateExpandedHeightToMaxHeight() {
float currentMaxPanelHeight = getMaxPanelHeight();
if (isFullyCollapsed()) {
@@ -753,6 +753,13 @@
setExpandedHeight(currentMaxPanelHeight);
}
+ /**
+ * Returns drag down distance after which panel should be fully expanded. Usually it's the
+ * same as max panel height but for large screen devices (especially split shade) we might
+ * want to return different value to shorten drag distance
+ */
+ public abstract int getMaxPanelTransitionDistance();
+
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
@@ -763,18 +770,15 @@
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
- float maxPanelHeight = getMaxPanelHeight();
+ float maxPanelHeight = getMaxPanelTransitionDistance();
if (mHeightAnimator == null) {
// Split shade has its own overscroll logic
if (mTracking && !mInSplitShade) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
}
- mExpandedHeight = Math.min(h, maxPanelHeight);
- } else {
- mExpandedHeight = h;
}
-
+ mExpandedHeight = Math.min(h, maxPanelHeight);
// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
@@ -832,7 +836,7 @@
protected abstract int getMaxPanelHeight();
public void setExpandedFraction(float frac) {
- setExpandedHeight(getMaxPanelHeight() * frac);
+ setExpandedHeight(getMaxPanelTransitionDistance() * frac);
}
public float getExpandedHeight() {
@@ -1029,7 +1033,7 @@
mHeightAnimator = animator;
if (animator == null && mPanelUpdateWhenAnimatorEnds) {
mPanelUpdateWhenAnimatorEnds = false;
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
}
}
@@ -1421,7 +1425,7 @@
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
- requestPanelHeightUpdate();
+ updateExpandedHeightToMaxHeight();
mHasLayoutedSinceDown = true;
if (mUpdateFlingOnLayout) {
abortAnimations();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
index afd57da..618c892 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
@@ -14,6 +14,7 @@
import com.android.systemui.statusbar.phone.panelstate.PanelState
import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.LargeScreenUtils
import java.io.PrintWriter
import javax.inject.Inject
@@ -28,6 +29,7 @@
private val scrimController: ScrimController,
@Main private val resources: Resources,
private val statusBarStateController: SysuiStatusBarStateController,
+ private val headsUpManager: HeadsUpManager
) {
private var inSplitShade = false
@@ -84,7 +86,11 @@
}
private fun canUseCustomFraction(panelState: Int?) =
- inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING
+ inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING &&
+ // in case of HUN we can't always use predefined distances to manage scrim
+ // transition because dragDownPxAmount can start from value bigger than
+ // splitShadeScrimTransitionDistance
+ !headsUpManager.isTrackingHeadsUp
private fun isScreenUnlocked() =
statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index ba26cfa..df81c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1369,6 +1369,8 @@
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
+ LinearLayout actionListMarginTarget = layout.findViewById(
+ com.android.internal.R.id.notification_action_list_margin_target);
if (bubbleButton == null || actionContainer == null) {
return;
}
@@ -1393,6 +1395,16 @@
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
bubbleButton.setVisibility(VISIBLE);
actionContainer.setVisibility(VISIBLE);
+ // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
+ if (actionListMarginTarget != null) {
+ ViewGroup.LayoutParams lp = actionListMarginTarget.getLayoutParams();
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ if (mlp.bottomMargin > 0) {
+ mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, 0);
+ }
+ }
+ }
} else {
bubbleButton.setVisibility(GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 0b2d443..4576a64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -23,7 +23,6 @@
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -654,10 +653,7 @@
final boolean fingerprintLockout = biometricSourceType == BiometricSourceType.FINGERPRINT
&& (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT);
- final boolean faceLockout = biometricSourceType == BiometricSourceType.FACE
- && (msgId == FaceManager.FACE_ERROR_LOCKOUT
- || msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT);
- if (fingerprintLockout || faceLockout) {
+ if (fingerprintLockout) {
startWakeAndUnlock(MODE_SHOW_BOUNCER);
UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7463dac..9603de9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -99,11 +99,14 @@
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
@@ -509,10 +512,17 @@
private CentralSurfacesComponent mCentralSurfacesComponent;
// Flags for disabling the status bar
- // Two variables becaseu the first one evidently ran out of room for new flags.
+ // Two variables because the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
private int mDisabled2 = 0;
+ /**
+ * This keeps track of whether we have (or haven't) registered the predictive back callback.
+ * Since we can have visible -> visible transitions, we need to avoid
+ * double-registering (or double-unregistering) our callback.
+ */
+ private boolean mIsBackCallbackRegistered = false;
+
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -636,6 +646,12 @@
private final InteractionJankMonitor mJankMonitor;
+ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+ if (DEBUG) {
+ Log.d(TAG, "mOnBackInvokedCallback() called");
+ }
+ onBackPressed();
+ };
/**
* Public constructor for CentralSurfaces.
@@ -2731,9 +2747,38 @@
if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser);
mNotificationLogger.startNotificationLogging();
+
+ if (!mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
+ }
} else {
mNotificationLogger.stopNotificationLogging();
handleVisibleToUserChangedImpl(visibleToUser);
+
+ if (mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
+ if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
+ + "unregistered)");
+ }
+ }
}
}
@@ -3444,6 +3489,12 @@
return mNotificationPanelViewController.getKeyguardBottomAreaView();
}
+ protected ViewRootImpl getViewRootImpl() {
+ NotificationShadeWindowView nswv = getNotificationShadeWindowView();
+ if (nswv != null) return nswv.getViewRootImpl();
+
+ return null;
+ }
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -3761,6 +3812,12 @@
updateScrimController();
}
+ @VisibleForTesting
+ public void setNotificationShadeWindowViewController(
+ NotificationShadeWindowViewController nswvc) {
+ mNotificationShadeWindowViewController = nswvc;
+ }
+
/**
* Set the amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 6bfb0da..90d0b69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -115,9 +115,7 @@
mInitialTouchY = y;
int startHeight = (int) (mPickedChild.getActualHeight()
+ mPickedChild.getTranslationY());
- float maxPanelHeight = mPanel.getMaxPanelHeight();
- mPanel.setPanelScrimMinFraction(maxPanelHeight > 0f
- ? (float) startHeight / maxPanelHeight : 0f);
+ mPanel.setHeadsUpDraggingStartingHeight(startHeight);
mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index e63c383..44aef7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -220,6 +220,7 @@
&& !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())
&& !needsFullscreenBouncer()
+ && !mKeyguardUpdateMonitor.isFaceLockedOut()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
index a6160aa..6b7c42e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
@@ -114,8 +114,8 @@
"end state=${state.panelStateToString()} " +
"f=$fraction " +
"expanded=$expanded " +
- "tracking=$tracking" +
- "drawDownPxAmount=$dragDownPxAmount " +
+ "tracking=$tracking " +
+ "dragDownPxAmount=$dragDownPxAmount " +
"${if (fullyOpened) " fullyOpened" else ""} " +
if (fullyClosed) " fullyClosed" else ""
)
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 8a51cd6..d43f739 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -36,6 +36,8 @@
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.constraintlayout.helper.widget.Flow
import androidx.lifecycle.ViewModelProvider
@@ -67,7 +69,7 @@
/**
* Support a fullscreen user switcher
*/
-class UserSwitcherActivity @Inject constructor(
+open class UserSwitcherActivity @Inject constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
private val falsingCollector: FalsingCollector,
@@ -83,6 +85,7 @@
private var popupMenu: UserSwitcherPopupMenu? = null
private lateinit var addButton: View
private var addUserRecords = mutableListOf<UserRecord>()
+ private val onBackCallback = OnBackInvokedCallback { finish() }
private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback {
override fun onUserChanged(newUser: Int, userContext: Context) {
finish()
@@ -105,7 +108,11 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ createActivity()
+ }
+ @VisibleForTesting
+ fun createActivity() {
setContentView(R.layout.user_switcher_fullscreen)
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -148,6 +155,9 @@
}
}
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackCallback)
+
userSwitcherController.init(parent)
initBroadcastReceiver()
@@ -278,7 +288,12 @@
if (isUsingModernArchitecture()) {
return
}
+ destroyActivity()
+ }
+ @VisibleForTesting
+ fun destroyActivity() {
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(onBackCallback)
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
userTracker.removeCallback(userSwitchedCallback)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index 0bf038d..2714cf4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -90,6 +90,8 @@
onlyFaceEnrolled = false,
faceAuthenticated = false,
faceDisabled = false,
+ faceLockedOut = false,
+ fpLockedOut = false,
goingToSleep = false,
keyguardAwakeExcludingBouncerShowing = false,
keyguardGoingAway = false,
@@ -99,5 +101,5 @@
scanningAllowedByStrongAuth = false,
secureCameraLaunched = false,
switchingUser = false,
- udfpsBouncerShowing = false
+ udfpsBouncerShowing = false,
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
new file mode 100644
index 0000000..9e5bfe5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.statusbar.policy.DevicePostureController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPinViewControllerTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+ @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
+
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Mock private lateinit var securityMode: SecurityMode
+
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+
+ @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+
+ @Mock
+ private lateinit var keyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+
+ @Mock
+ private lateinit var keyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+
+ @Mock private lateinit var mLatencyTracker: LatencyTracker
+
+ @Mock private lateinit var liftToActivateListener: LiftToActivateListener
+
+ @Mock private val mEmergencyButtonController: EmergencyButtonController? = null
+ private val falsingCollector: FalsingCollector = FalsingCollectorFake()
+ @Mock lateinit var postureController: DevicePostureController
+
+ lateinit var pinViewController: KeyguardPinViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ .thenReturn(keyguardMessageArea)
+ Mockito.`when`(
+ keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
+ )
+ .thenReturn(keyguardMessageAreaController)
+ pinViewController =
+ KeyguardPinViewController(
+ keyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController
+ )
+ }
+
+ @Test
+ fun startAppearAnimation() {
+ pinViewController.startAppearAnimation()
+ verify(keyguardMessageAreaController).setMessageIfEmpty(R.string.keyguard_enter_your_pin)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 093e592..508f81d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -20,6 +20,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -751,8 +752,7 @@
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- mKeyguardUpdateMonitor.mFaceAuthenticationCallback
- .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ faceAuthLockedOut();
verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
}
@@ -764,7 +764,7 @@
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
@@ -775,10 +775,9 @@
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- mKeyguardUpdateMonitor.mFaceAuthenticationCallback
- .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ faceAuthLockedOut();
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
@@ -1217,7 +1216,7 @@
public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
// Face auth should run when the following is true.
keyguardNotGoingAway();
- bouncerFullyVisibleAndNotGoingToSleep();
+ occludingAppRequestsFaceAuth();
currentUserIsPrimary();
strongAuthNotRequired();
biometricsEnabledForCurrentUser();
@@ -1225,6 +1224,7 @@
biometricsNotDisabledThroughDevicePolicyManager();
userNotCurrentlySwitching();
mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
fingerprintErrorLockedOut();
@@ -1500,6 +1500,27 @@
}
@Test
+ public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ // Face is locked out.
+ faceAuthLockedOut();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
throws RemoteException {
// Both fingerprint and face are enrolled by default
@@ -1587,6 +1608,11 @@
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
+ private void faceAuthLockedOut() {
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+ .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ }
+
private void faceAuthEnabled() {
// this ensures KeyguardUpdateMonitor updates the cached mIsFaceEnrolled flag using the
// face manager mock wire-up in setup()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index b98be75..2db58be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -158,6 +158,13 @@
assertThat(qsPanel.paddingBottom).isEqualTo(padding)
}
+ @Test
+ fun testSetSquishinessFraction_noCrash() {
+ qsPanel.addView(qsPanel.mTileLayout as View, 0)
+ qsPanel.addView(FrameLayout(context))
+ qsPanel.setSquishinessFraction(0.5f)
+ }
+
private infix fun View.isLeftOf(other: View): Boolean {
val rect = Rect()
getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
new file mode 100644
index 0000000..ea70c26
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.app.UiModeManager
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Handler
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UiModeNightTileTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var uiModeManager: UiModeManager
+ @Mock
+ private lateinit var resources: Resources
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var qsHost: QSTileHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var batteryController: BatteryController
+ @Mock
+ private lateinit var locationController: LocationController
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private val falsingManager = FalsingManagerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: UiModeNightTile
+ private lateinit var configuration: Configuration
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ configuration = Configuration()
+ mContext.addMockSystemService(Context.UI_MODE_SERVICE, uiModeManager)
+
+ `when`(qsHost.context).thenReturn(mockContext)
+ `when`(qsHost.userContext).thenReturn(mContext)
+ `when`(mockContext.resources).thenReturn(resources)
+ `when`(resources.configuration).thenReturn(configuration)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile = UiModeNightTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ configurationController,
+ batteryController,
+ locationController)
+ }
+
+ @Test
+ fun testIcon_whenNightModeOn_isOnState() {
+ val state = QSTile.BooleanState()
+ setNightModeOn()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenNightModeOn_isOffState() {
+ val state = QSTile.BooleanState()
+ setNightModeOff()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_off))
+ }
+
+ private fun setNightModeOn() {
+ `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_YES)
+ configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+ }
+
+ private fun setNightModeOff() {
+ `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_NO)
+ configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index e85ffb6..c448538 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -23,6 +23,7 @@
import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
import android.view.View
+import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.widget.TextView
import androidx.constraintlayout.motion.widget.MotionLayout
@@ -30,6 +31,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -64,6 +66,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyInt
@@ -614,6 +617,34 @@
)
}
+ @Test
+ fun animateOutOnStartCustomizing() {
+ val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ controller.startCustomizingAnimation(show = true, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(0f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
+ verify(animator).start()
+ }
+
+ @Test
+ fun animateInOnEndCustomizing() {
+ val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ controller.startCustomizingAnimation(show = false, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(1f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_IN)
+ verify(animator).start()
+ }
+
private fun createWindowInsets(
topCutout: Rect? = Rect()
): WindowInsets {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 8511443..5ecfc8eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -4,10 +4,12 @@
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
+import android.view.ViewPropertyAnimator
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -29,8 +31,10 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
@@ -198,4 +202,32 @@
context.getString(com.android.internal.R.string.status_bar_alarm_clock)
)
}
+
+ @Test
+ fun animateOutOnStartCustomizing() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(0f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
+ verify(animator).start()
+ }
+
+ @Test
+ fun animateInOnEndCustomizing() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(1f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_IN)
+ verify(animator).start()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index f1c54ef..e730713 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -183,6 +183,7 @@
@TestableLooper.RunWithLooper
public class NotificationPanelViewControllerTest extends SysuiTestCase {
+ private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
private static final int PANEL_WIDTH = 500; // Random value just for the test.
@@ -321,6 +322,8 @@
.thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal))
.thenReturn(10);
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
+ .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
@@ -666,8 +669,9 @@
}
@Test
- public void testSetPanelScrimMinFraction() {
- mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
+ public void testSetPanelScrimMinFractionWhenHeadsUpIsDragged() {
+ mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
+ mNotificationPanelViewController.getMaxPanelHeight() / 2);
verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
}
@@ -1363,40 +1367,47 @@
}
@Test
- public void getMaxPanelHeight_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
- int splitShadeFullTransitionDistance = 123456;
+ public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
enableSplitShade(true);
- setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
- int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+ int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- assertThat(maxPanelHeight).isEqualTo(splitShadeFullTransitionDistance);
+ assertThat(maxDistance).isEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
}
@Test
- public void getMaxPanelHeight_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
+ public void getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue() {
+ enableSplitShade(true);
+ mNotificationPanelViewController.expandWithQs();
+ when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true);
+ mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
+ SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
+
+ int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
+
+ assertThat(maxDistance).isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
+ }
+
+ @Test
+ public void getMaxPanelTransitionDistance_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
mStatusBarStateController.setState(KEYGUARD);
- int splitShadeFullTransitionDistance = 123456;
enableSplitShade(true);
- setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
- int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+ int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
}
@Test
- public void getMaxPanelHeight_expanding_notSplitShade_returnsNonSplitShadeValue() {
- int splitShadeFullTransitionDistance = 123456;
+ public void getMaxPanelTransitionDistance_expanding_notSplitShade_returnsNonSplitShadeValue() {
enableSplitShade(false);
- setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
mNotificationPanelViewController.expandWithQs();
- int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+ int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
- assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ assertThat(maxDistance).isNotEqualTo(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
}
@Test
@@ -1543,12 +1554,6 @@
mTouchHandler.onTouch(mView, ev);
}
- private void setSplitShadeFullTransitionDistance(int splitShadeFullTransitionDistance) {
- when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
- .thenReturn(splitShadeFullTransitionDistance);
- mNotificationPanelViewController.updateResources();
- }
-
private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
mNotificationPanelViewController.setDozing(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index 0c6a6a9..12ef036 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -20,6 +20,7 @@
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,10 +34,10 @@
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -63,6 +64,8 @@
@Mock
private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
@Mock
+ private lateinit var largeScreenShadeHeaderController: LargeScreenShadeHeaderController
+ @Mock
private lateinit var featureFlags: FeatureFlags
@Captor
lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
@@ -92,6 +95,7 @@
notificationsQSContainer,
navigationModeController,
overviewProxyService,
+ largeScreenShadeHeaderController,
featureFlags,
delayableExecutor
)
@@ -371,8 +375,14 @@
container.removeAllViews()
container.addView(newViewWithId(1))
container.addView(newViewWithId(View.NO_ID))
- val controller = NotificationsQSContainerController(container, navigationModeController,
- overviewProxyService, featureFlags, delayableExecutor)
+ val controller = NotificationsQSContainerController(
+ container,
+ navigationModeController,
+ overviewProxyService,
+ largeScreenShadeHeaderController,
+ featureFlags,
+ delayableExecutor
+ )
controller.updateConstraints()
assertThat(container.getChildAt(0).id).isEqualTo(1)
@@ -397,6 +407,21 @@
verify(notificationsQSContainer).setQSContainerPaddingBottom(STABLE_INSET_BOTTOM)
}
+ @Test
+ fun testStartCustomizingWithDuration() {
+ controller.setCustomizerShowing(true, 100L)
+ verify(largeScreenShadeHeaderController).startCustomizingAnimation(true, 100L)
+ }
+
+ @Test
+ fun testEndCustomizingWithDuration() {
+ controller.setCustomizerShowing(true, 0L) // Only tracks changes
+ reset(largeScreenShadeHeaderController)
+
+ controller.setCustomizerShowing(false, 100L)
+ verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L)
+ }
+
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
index baaa447..6be76a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
@@ -13,6 +13,7 @@
import com.android.systemui.statusbar.phone.panelstate.STATE_OPEN
import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,6 +29,7 @@
@Mock private lateinit var scrimController: ScrimController
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var headsUpManager: HeadsUpManager
private val configurationController = FakeConfigurationController()
private lateinit var controller: ScrimShadeTransitionController
@@ -42,7 +44,8 @@
dumpManager,
scrimController,
context.resources,
- statusBarStateController)
+ statusBarStateController,
+ headsUpManager)
controller.onPanelStateChanged(STATE_OPENING)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 14b7471..f510e48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -69,7 +70,11 @@
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -168,6 +173,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -279,6 +285,15 @@
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
+ /**
+ * The process of registering/unregistering a predictive back callback requires a
+ * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
+ * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
+ */
+ @Mock private ViewRootImpl mViewRootImpl;
+ @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -368,10 +383,10 @@
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
- mShadeController = new ShadeControllerImpl(mCommandQueue,
+ mShadeController = spy(new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
+ () -> Optional.of(mCentralSurfaces), () -> mAssistManager));
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
@@ -460,7 +475,14 @@
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
- mWiredChargingRippleController, mDreamManager);
+ mWiredChargingRippleController, mDreamManager) {
+ @Override
+ protected ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+ when(mViewRootImpl.getOnBackInvokedDispatcher())
+ .thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
@@ -738,6 +760,43 @@
}
}
+ /**
+ * Do the following:
+ * 1. verify that a predictive back callback is registered when CSurf becomes visible
+ * 2. verify that the same callback is unregistered when CSurf becomes invisible
+ */
+ @Test
+ public void testPredictiveBackCallback_registration() {
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ mCentralSurfaces.handleVisibleToUserChanged(false);
+ verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
+ eq(mOnBackInvokedCallback.getValue()));
+ }
+
+ /**
+ * Do the following:
+ * 1. capture the predictive back callback during registration
+ * 2. call the callback directly
+ * 3. verify that the ShadeController's panel collapse animation is invoked
+ */
+ @Test
+ public void testPredictiveBackCallback_invocationCollapsesPanel() {
+ mCentralSurfaces.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+ mOnBackInvokedCallback.getValue().onBackInvoked();
+ verify(mShadeController).animateCollapsePanels();
+ }
+
@Test
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 60a3d95..ab209d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -408,6 +408,16 @@
mBouncer.hide(false /* destroyView */);
verify(mHandler).removeCallbacks(eq(showRunnable.getValue()));
}
+
+ @Test
+ public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() {
+ when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ mBouncer.show(true /* reset */);
+
+ verify(mHandler, never()).postDelayed(any(), anyLong());
+ }
+
@Test
public void testShow_delaysIfFaceAuthIsRunning_unlessBypassEnabled() {
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
index 439beaa..3968bb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -16,11 +16,17 @@
package com.android.systemui.user
+import android.app.Application
import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
+import android.view.View
+import android.view.Window
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
@@ -29,12 +35,25 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -60,11 +79,22 @@
private lateinit var flags: FeatureFlags
@Mock
private lateinit var viewModelFactoryLazy: dagger.Lazy<UserSwitcherViewModel.Factory>
+ @Mock
+ private lateinit var onBackDispatcher: OnBackInvokedDispatcher
+ @Mock
+ private lateinit var decorView: View
+ @Mock
+ private lateinit var window: Window
+ @Mock
+ private lateinit var userSwitcherRootView: UserSwitcherRootView
+ @Captor
+ private lateinit var onBackInvokedCallback: ArgumentCaptor<OnBackInvokedCallback>
+ var isFinished = false
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- activity = UserSwitcherActivity(
+ activity = spy(object : UserSwitcherActivity(
userSwitcherController,
broadcastDispatcher,
falsingCollector,
@@ -73,7 +103,21 @@
userTracker,
flags,
viewModelFactoryLazy,
- )
+ ) {
+ override fun getOnBackInvokedDispatcher() = onBackDispatcher
+ override fun getMainExecutor(): Executor = FakeExecutor(FakeSystemClock())
+ override fun finish() {
+ isFinished = true
+ }
+ })
+ `when`(activity.window).thenReturn(window)
+ `when`(window.decorView).thenReturn(decorView)
+ `when`(activity.findViewById<UserSwitcherRootView>(R.id.user_switcher_root))
+ .thenReturn(userSwitcherRootView)
+ `when`(activity.findViewById<View>(R.id.cancel)).thenReturn(mock(View::class.java))
+ `when`(activity.findViewById<View>(R.id.add)).thenReturn(mock(View::class.java))
+ `when`(activity.application).thenReturn(mock(Application::class.java))
+ doNothing().`when`(activity).setContentView(anyInt())
}
@Test
@@ -85,4 +129,24 @@
assertThat(activity.getMaxColumns(7)).isEqualTo(4)
assertThat(activity.getMaxColumns(9)).isEqualTo(5)
}
+
+ @Test
+ fun onCreate_callbackRegistration() {
+ activity.createActivity()
+ verify(onBackDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any())
+
+ activity.destroyActivity()
+ verify(onBackDispatcher).unregisterOnBackInvokedCallback(any())
+ }
+
+ @Test
+ fun onBackInvokedCallback_finishesActivity() {
+ activity.createActivity()
+ verify(onBackDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), onBackInvokedCallback.capture())
+
+ onBackInvokedCallback.value.onBackInvoked()
+ assertThat(isFinished).isTrue()
+ }
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 0bba3c9..9f27f72 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -150,8 +150,9 @@
// Create a PendingIntent
final long token = Binder.clearCallingIdentity();
try {
- return PendingIntent.getActivity(mContext, /*requestCode */ associationId, intent,
- FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+ return PendingIntent.getActivityAsUser(mContext, /*requestCode */ associationId, intent,
+ FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, /* options= */ null,
+ UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index e21a3d7..6b3f5e2 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -98,12 +98,17 @@
}
private String getCallingPackageName() {
- final String[] packages = mContext.getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
+ final PackageManager pm = mContext.getPackageManager();
+ final int uid = Binder.getCallingUid();
+ final String[] packages = pm.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
return packages[0];
}
- return "unknown";
+ final String name = pm.getNameForUid(uid);
+ if (name != null) {
+ return name;
+ }
+ return String.valueOf(uid);
}
private void dumpTempValues(String pkg, PrintWriter pw, int type,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e52d21..842498d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16396,11 +16396,6 @@
}
}
- @Override
- public void enableBinderTracing() {
- Binder.enableTracingForUid(Binder.getCallingUid());
- }
-
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1954f76..6775c99 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -376,6 +376,11 @@
private boolean mDelayUserDataLocking;
/**
+ * Users are only allowed to be unlocked after boot complete.
+ */
+ private volatile boolean mAllowUserUnlocking;
+
+ /**
* Keep track of last active users for mDelayUserDataLocking.
* The latest stopped user is placed in front while the least recently stopped user in back.
*/
@@ -434,6 +439,11 @@
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
updateStartedUserArrayLU();
+
+ // TODO(b/232452368): currently mAllowUserUnlocking is only used on devices with HSUM
+ // (Headless System User Mode), but on master it will be used by all devices (and hence this
+ // initial assignment should be removed).
+ mAllowUserUnlocking = !UserManager.isHeadlessSystemUserMode();
}
void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
@@ -1801,6 +1811,22 @@
private boolean unlockUserCleared(final @UserIdInt int userId, byte[] secret,
IProgressListener listener) {
+ // Delay user unlocking for headless system user mode until the system boot
+ // completes. When the system boot completes, the {@link #onBootCompleted()}
+ // method unlocks all started users for headless system user mode. This is done
+ // to prevent unlocking the users too early during the system boot up.
+ // Otherwise, emulated volumes are mounted too early during the system
+ // boot up. When vold is reset on boot complete, vold kills all apps/services
+ // (that use these emulated volumes) before unmounting the volumes(b/241929666).
+ // In the past, these killings have caused the system to become too unstable on
+ // some occasions.
+ // Any unlocks that get delayed by this will be done by onBootComplete() instead.
+ if (!mAllowUserUnlocking) {
+ Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId);
+ notifyFinished(userId, listener);
+ return false;
+ }
+
UserState uss;
if (!StorageManager.isUserKeyUnlocked(userId)) {
final UserInfo userInfo = getUserInfo(userId);
@@ -2397,7 +2423,44 @@
}
}
+ @VisibleForTesting
+ void setAllowUserUnlocking(boolean allowed) {
+ mAllowUserUnlocking = allowed;
+ if (DEBUG_MU) {
+ // TODO(b/245335748): use Slogf.d instead
+ // Slogf.d(TAG, new Exception(), "setAllowUserUnlocking(%b)", allowed);
+ android.util.Slog.d(TAG, "setAllowUserUnlocking():" + allowed, new Exception());
+ }
+ }
+
+ /**
+ * @deprecated TODO(b/232452368): this logic will be merged into sendBootCompleted
+ */
+ @Deprecated
+ private void onBootCompletedOnHeadlessSystemUserModeDevices() {
+ setAllowUserUnlocking(true);
+
+ // Get a copy of mStartedUsers to use outside of lock.
+ SparseArray<UserState> startedUsers;
+ synchronized (mLock) {
+ startedUsers = mStartedUsers.clone();
+ }
+ // USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest.
+ Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM);
+ for (int i = 0; i < startedUsers.size(); i++) {
+ UserState uss = startedUsers.valueAt(i);
+ int userId = uss.mHandle.getIdentifier();
+ Slogf.i(TAG, "Attempting to unlock user %d on boot complete", userId);
+ maybeUnlockUser(userId);
+ }
+ }
+
void sendBootCompleted(IIntentReceiver resultTo) {
+ if (UserManager.isHeadlessSystemUserMode()) {
+ // Unlocking users is delayed until boot complete for headless system user mode.
+ onBootCompletedOnHeadlessSystemUserModeDevices();
+ }
+
// Get a copy of mStartedUsers to use outside of lock
SparseArray<UserState> startedUsers;
synchronized (mLock) {
@@ -2848,6 +2911,7 @@
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsers:" + mLastActiveUsers);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
+ pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking);
pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 62ae9b8..8b11c39 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -267,10 +267,6 @@
OP_CAMERA,
};
- private static final int[] WATCHABLE_NON_PERMISSION_OPS = {
- OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- };
-
private static final int MAX_UNFORWARDED_OPS = 10;
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
@@ -4252,7 +4248,7 @@
public boolean shouldCollectNotes(int opCode) {
Preconditions.checkArgumentInRange(opCode, 0, _NUM_OP - 1, "opCode");
- if (ArrayUtils.contains(WATCHABLE_NON_PERMISSION_OPS, opCode)) {
+ if (AppOpsManager.shouldForceCollectNoteForOp(opCode)) {
return true;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index e97d9c2..736914a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -17,9 +17,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -118,8 +121,39 @@
// TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
/*package*/ final Object mSetModeLock = new Object();
- /** PID of current audio mode owner communicated by AudioService */
- private int mModeOwnerPid = 0;
+ /** AudioModeInfo contains information on current audio mode owner
+ * communicated by AudioService */
+ /* package */ static final class AudioModeInfo {
+ /** Current audio mode */
+ final int mMode;
+ /** PID of current audio mode owner */
+ final int mPid;
+ /** UID of current audio mode owner */
+ final int mUid;
+
+ AudioModeInfo(int mode, int pid, int uid) {
+ mMode = mode;
+ mPid = pid;
+ mUid = uid;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioModeInfo: mMode=" + AudioSystem.modeToString(mMode)
+ + ", mPid=" + mPid
+ + ", mUid=" + mUid;
+ }
+ };
+
+ private AudioModeInfo mAudioModeOwner = new AudioModeInfo(AudioSystem.MODE_NORMAL, 0, 0);
+
+ /**
+ * Indicates that default communication device is chosen by routing rules in audio policy
+ * manager and not forced by AudioDeviceBroker.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
+ public static final long USE_SET_COMMUNICATION_DEVICE = 243827847L;
//-------------------------------------------------------------------
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
@@ -187,7 +221,7 @@
/*package*/ void onSystemReady() {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mModeOwnerPid = mAudioService.getModeOwnerPid();
+ mAudioModeOwner = mAudioService.getAudioModeOwner();
mBtHelper.onSystemReady();
}
}
@@ -368,11 +402,11 @@
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient topCommunicationRouteClient() {
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
- if (crc.getPid() == mModeOwnerPid) {
+ if (crc.getPid() == mAudioModeOwner.mPid) {
return crc;
}
}
- if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+ if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0) {
return mCommunicationRouteClients.get(0);
}
return null;
@@ -390,7 +424,7 @@
AudioDeviceAttributes device = crc != null ? crc.getDevice() : null;
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "requestedCommunicationDevice, device: "
- + device + " mode owner pid: " + mModeOwnerPid);
+ + device + "mAudioModeOwner: " + mAudioModeOwner.toString());
}
return device;
}
@@ -774,8 +808,9 @@
sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info);
}
- /*package*/ void postSetModeOwnerPid(int pid, int mode) {
- sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode);
+ /*package*/ void postSetModeOwner(int mode, int pid, int uid) {
+ sendLMsgNoDelay(MSG_I_SET_MODE_OWNER, SENDMSG_REPLACE,
+ new AudioModeInfo(mode, pid, uid));
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
@@ -1162,7 +1197,7 @@
pw.println(prefix + "mAccessibilityStrategyId: "
+ mAccessibilityStrategyId);
- pw.println("\n" + prefix + "mModeOwnerPid: " + mModeOwnerPid);
+ pw.println("\n" + prefix + "mAudioModeOwner: " + mAudioModeOwner);
mBtHelper.dump(pw, prefix);
}
@@ -1288,12 +1323,19 @@
}
break;
case MSG_L_SET_BT_ACTIVE_DEVICE:
- synchronized (mDeviceStateLock) {
- BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
- mDeviceInventory.onSetBtActiveDevice(btInfo,
- (btInfo.mProfile != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
- ? mAudioService.getBluetoothContextualVolumeStream()
- : AudioSystem.STREAM_DEFAULT);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ mDeviceInventory.onSetBtActiveDevice(btInfo,
+ (btInfo.mProfile
+ != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
+ ? mAudioService.getBluetoothContextualVolumeStream()
+ : AudioSystem.STREAM_DEFAULT);
+ if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
+ || btInfo.mProfile == BluetoothProfile.HEARING_AID) {
+ onUpdateCommunicationRouteClient("setBluetoothActiveDevice");
+ }
+ }
}
break;
case MSG_BT_HEADSET_CNCT_FAILED:
@@ -1338,11 +1380,11 @@
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
}
break;
- case MSG_I_SET_MODE_OWNER_PID:
+ case MSG_I_SET_MODE_OWNER:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mModeOwnerPid = msg.arg1;
- if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
+ mAudioModeOwner = (AudioModeInfo) msg.obj;
+ if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) {
onUpdateCommunicationRouteClient("setNewModeOwner");
}
}
@@ -1504,7 +1546,7 @@
private static final int MSG_REPORT_NEW_ROUTES = 13;
private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
- private static final int MSG_I_SET_MODE_OWNER_PID = 16;
+ private static final int MSG_I_SET_MODE_OWNER = 16;
private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
@@ -1826,8 +1868,16 @@
AudioSystem.setParameters("BT_SCO=on");
}
if (preferredCommunicationDevice == null) {
- removePreferredDevicesForStrategySync(mCommunicationStrategyId);
- removePreferredDevicesForStrategySync(mAccessibilityStrategyId);
+ AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice();
+ if (defaultDevice != null) {
+ setPreferredDevicesForStrategySync(
+ mCommunicationStrategyId, Arrays.asList(defaultDevice));
+ setPreferredDevicesForStrategySync(
+ mAccessibilityStrategyId, Arrays.asList(defaultDevice));
+ } else {
+ removePreferredDevicesForStrategySync(mCommunicationStrategyId);
+ removePreferredDevicesForStrategySync(mAccessibilityStrategyId);
+ }
} else {
setPreferredDevicesForStrategySync(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
@@ -1856,26 +1906,24 @@
}
}
+ // @GuardedBy("mSetModeLock")
+ @GuardedBy("mDeviceStateLock")
private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- boolean wasSpeakerphoneActive = isSpeakerphoneActive();
- mPreferredCommunicationDevice = device;
- updateActiveCommunicationDevice();
- if (wasSpeakerphoneActive != isSpeakerphoneActive()) {
- try {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- UserHandle.ALL);
- } catch (Exception e) {
- Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
- }
- }
- mAudioService.postUpdateRingerModeServiceInt();
- dispatchCommunicationDevice();
+ boolean wasSpeakerphoneActive = isSpeakerphoneActive();
+ mPreferredCommunicationDevice = device;
+ updateActiveCommunicationDevice();
+ if (wasSpeakerphoneActive != isSpeakerphoneActive()) {
+ try {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL);
+ } catch (Exception e) {
+ Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
}
}
+ mAudioService.postUpdateRingerModeServiceInt();
+ dispatchCommunicationDevice();
}
private CommunicationRouteClient removeCommunicationRouteClient(
@@ -1915,6 +1963,32 @@
return null;
}
+ @GuardedBy("mDeviceStateLock")
+ private boolean communnicationDeviceCompatOn() {
+ return mAudioModeOwner.mMode == AudioSystem.MODE_IN_COMMUNICATION
+ && !(CompatChanges.isChangeEnabled(
+ USE_SET_COMMUNICATION_DEVICE, mAudioModeOwner.mUid)
+ || mAudioModeOwner.mUid == android.os.Process.SYSTEM_UID);
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ AudioDeviceAttributes getDefaultCommunicationDevice() {
+ // For system server (Telecom) and APKs targeting S and above, we let the audio
+ // policy routing rules select the default communication device.
+ // For older APKs, we force Hearing Aid or LE Audio headset when connected as
+ // those APKs cannot select a LE Audio or Hearing Aid device explicitly.
+ AudioDeviceAttributes device = null;
+ if (communnicationDeviceCompatOn()) {
+ // If both LE and Hearing Aid are active (thie should not happen),
+ // priority to Hearing Aid.
+ device = mDeviceInventory.getDeviceOfType(AudioSystem.DEVICE_OUT_HEARING_AID);
+ if (device == null) {
+ device = mDeviceInventory.getDeviceOfType(AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+ }
+ return device;
+ }
+
@Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.getDeviceSensorUuid(device);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index c1f4969..e90bfe8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -294,6 +294,7 @@
}
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
if (AudioService.DEBUG_DEVICES) {
@@ -1526,6 +1527,19 @@
return di.mSensorUuid;
}
}
+
+ /* package */ AudioDeviceAttributes getDeviceOfType(int type) {
+ synchronized (mDevicesLock) {
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mDeviceType == type) {
+ return new AudioDeviceAttributes(
+ di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
+ }
+ }
+ }
+ return null;
+ }
+
//----------------------------------------------------------
// For tests only
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 31f8659..c8aecaf 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5230,16 +5230,17 @@
}
/**
- * Return the pid of the current audio mode owner
+ * Return information on the current audio mode owner
* @return 0 if nobody owns the mode
*/
@GuardedBy("mDeviceBroker.mSetModeLock")
- /*package*/ int getModeOwnerPid() {
+ /*package*/ AudioDeviceBroker.AudioModeInfo getAudioModeOwner() {
SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
if (hdlr != null) {
- return hdlr.getPid();
+ return new AudioDeviceBroker.AudioModeInfo(
+ hdlr.getMode(), hdlr.getPid(), hdlr.getUid());
}
- return 0;
+ return new AudioDeviceBroker.AudioModeInfo(AudioSystem.MODE_NORMAL, 0 , 0);
}
/**
@@ -5425,7 +5426,7 @@
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
// connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(pid, mode);
+ mDeviceBroker.postSetModeOwner(mode, pid, uid);
} else {
Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
}
@@ -5753,7 +5754,10 @@
}
return deviceIds.stream().mapToInt(Integer::intValue).toArray();
}
- /** @see AudioManager#setCommunicationDevice(int) */
+ /**
+ * @see AudioManager#setCommunicationDevice(int)
+ * @see AudioManager#clearCommunicationDevice()
+ */
public boolean setCommunicationDevice(IBinder cb, int portId) {
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
@@ -5768,7 +5772,8 @@
throw new IllegalArgumentException("invalid device type " + device.getType());
}
}
- final String eventSource = new StringBuilder("setCommunicationDevice(")
+ final String eventSource = new StringBuilder()
+ .append(device == null ? "clearCommunicationDevice(" : "setCommunicationDevice(")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 454894e..e5d40d9 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1150,18 +1150,29 @@
}
return builder.toString();
case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED:
- builder.append(" muteFromMasterMute:").append(
- (mEventValue & PLAYER_MUTE_MASTER) != 0);
- builder.append(" muteFromStreamVolume:").append(
- (mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0);
- builder.append(" muteFromStreamMute:").append(
- (mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
- builder.append(" muteFromPlaybackRestricted:").append(
- (mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
- builder.append(" muteFromClientVolume:").append(
- (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
- builder.append(" muteFromVolumeShaper:").append(
- (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+ builder.append(" source:");
+ if (mEventValue <= 0) {
+ builder.append("none ");
+ } else {
+ if ((mEventValue & PLAYER_MUTE_MASTER) != 0) {
+ builder.append("masterMute ");
+ }
+ if ((mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+ builder.append("streamVolume ");
+ }
+ if ((mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0) {
+ builder.append("streamMute ");
+ }
+ if ((mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+ builder.append("playbackRestricted ");
+ }
+ if ((mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+ builder.append("clientVolume ");
+ }
+ if ((mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+ builder.append("volumeShaper ");
+ }
+ }
return builder.toString();
default:
return builder.toString();
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 46d863d..2e1a363 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -59,7 +59,7 @@
// The following apply to all clients
- void onAcquired(int sensorId, int acquiredInfo, int vendorCode) throws RemoteException {
+ public void onAcquired(int sensorId, int acquiredInfo, int vendorCode) throws RemoteException {
if (mSensorReceiver != null) {
mSensorReceiver.onAcquired(sensorId, acquiredInfo, vendorCode);
} else if (mFaceServiceReceiver != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index e0393b5..612d906 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -50,12 +50,14 @@
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import java.util.function.Supplier;
-class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps {
+class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps,
+ PowerPressHandler {
private static final String TAG = "FingerprintEnrollClient";
@@ -266,4 +268,10 @@
Slog.e(TAG, "Unable to send UI ready", e);
}
}
+
+ @Override
+ public void onPowerPressed() {
+ onAcquired(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED,
+ 0 /* vendorCode */);
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 431be88..676dc19 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -99,6 +99,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileUtils;
+import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -2784,26 +2785,18 @@
// When restricted to test networks, select any network with TRANSPORT_TEST. Since the
// creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
// this is considered safe.
- final NetworkRequest req;
if (mProfile.isRestrictedToTestNetworks()) {
- req = new NetworkRequest.Builder()
+ final NetworkRequest req = new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
.addCapability(NET_CAPABILITY_NOT_VPN)
.build();
+ mConnectivityManager.requestNetwork(req, mNetworkCallback);
} else {
- // Basically, the request here is referring to the default request which is defined
- // in ConnectivityService. Ideally, ConnectivityManager should provide an new API
- // which can provide the status of physical network even though there is a virtual
- // network. b/147280869 is used for tracking the new API.
- // TODO: Use the new API to register default physical network.
- req = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
+ mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
+ new Handler(mLooper));
}
-
- mConnectivityManager.requestNetwork(req, mNetworkCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6145a91c..b3aee22 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -131,6 +131,8 @@
// Configuration object for determining thresholds to change brightness dynamically
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
+ private final HysteresisLevels mScreenBrightnessThresholdsIdle;
private boolean mLoggingEnabled;
@@ -242,7 +244,9 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context,
+ HysteresisLevels screenBrightnessThresholds,
+ HysteresisLevels ambientBrightnessThresholdsIdle,
+ HysteresisLevels screenBrightnessThresholdsIdle, Context context,
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
@@ -251,7 +255,8 @@
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
- ambientBrightnessThresholds, screenBrightnessThresholds, context,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
hbmController, brightnessThrottler, idleModeBrightnessMapper,
ambientLightHorizonShort, ambientLightHorizonLong
);
@@ -265,7 +270,9 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context,
+ HysteresisLevels screenBrightnessThresholds,
+ HysteresisLevels ambientBrightnessThresholdsIdle,
+ HysteresisLevels screenBrightnessThresholdsIdle, Context context,
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
@@ -289,7 +296,9 @@
mAmbientLightHorizonShort = ambientLightHorizonShort;
mWeightingIntercept = ambientLightHorizonLong;
mAmbientBrightnessThresholds = ambientBrightnessThresholds;
+ mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
mScreenBrightnessThresholds = screenBrightnessThresholds;
+ mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
mHandler = new AutomaticBrightnessHandler(looper);
@@ -586,6 +595,8 @@
pw.println();
mAmbientBrightnessThresholds.dump(pw);
mScreenBrightnessThresholds.dump(pw);
+ mScreenBrightnessThresholdsIdle.dump(pw);
+ mScreenBrightnessThresholdsIdle.dump(pw);
}
private String configStateToString(int state) {
@@ -680,8 +691,17 @@
lux = 0;
}
mAmbientLux = lux;
- mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
- mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+ if (isInIdleMode()) {
+ mAmbientBrighteningThreshold =
+ mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
+ } else {
+ mAmbientBrighteningThreshold =
+ mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+ }
mHbmController.onAmbientLuxChange(mAmbientLux);
// If the short term model was invalidated and the change is drastic enough, reset it.
@@ -903,10 +923,20 @@
mPreThresholdBrightness = mScreenAutoBrightness;
}
mScreenAutoBrightness = newScreenAutoBrightness;
- mScreenBrighteningThreshold = clampScreenBrightness(
- mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
- mScreenDarkeningThreshold = clampScreenBrightness(
- mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+ if (isInIdleMode()) {
+ mScreenBrighteningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholdsIdle.getBrighteningThreshold(
+ newScreenAutoBrightness));
+ mScreenDarkeningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholdsIdle.getDarkeningThreshold(
+ newScreenAutoBrightness));
+ } else {
+ mScreenBrighteningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholds.getBrighteningThreshold(
+ newScreenAutoBrightness));
+ mScreenDarkeningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+ }
if (sendUpdate) {
mCallbacks.updateBrightness();
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 12b2f47..4165186 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -188,8 +188,8 @@
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
*
- * <displayBrightnessChangeThresholds>
- * <brighteningThresholds>
+ * <displayBrightnessChangeThresholds> // Thresholds for screen changes
+ * <brighteningThresholds> // Thresholds for active mode brightness changes.
* <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
@@ -197,8 +197,8 @@
* </darkeningThresholds>
* </displayBrightnessChangeThresholds>
*
- * <ambientBrightnessChangeThresholds>
- * <brighteningThresholds>
+ * <ambientBrightnessChangeThresholds> // Thresholds for lux changes
+ * <brighteningThresholds> // Thresholds for active mode brightness changes.
* <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
@@ -206,6 +206,24 @@
* </darkeningThresholds>
* </ambientBrightnessChangeThresholds>
*
+ * <displayBrightnessChangeThresholdsIdle> // Thresholds for screen changes in idle mode
+ * <brighteningThresholds> // Thresholds for idle mode brightness changes.
+ * <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.002</minimum> // Minimum change needed in screen brightness to darken.
+ * </darkeningThresholds>
+ * </displayBrightnessChangeThresholdsIdle>
+ *
+ * <ambientBrightnessChangeThresholdsIdle> // Thresholds for lux changes in idle mode
+ * <brighteningThresholds> // Thresholds for idle mode brightness changes.
+ * <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.004</minimum> // Minimum change needed in ambient brightness to darken.
+ * </darkeningThresholds>
+ * </ambientBrightnessChangeThresholdsIdle>
+ *
* </displayConfiguration>
* }
* </pre>
@@ -319,9 +337,13 @@
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is
- private float mScreenDarkeningMinThreshold = 0.0f; // no minimum threshold for change in
- private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
- private float mAmbientLuxDarkeningMinThreshold = 0.0f; // brightness.
+ private float mScreenBrighteningMinThresholdIdle = 0.0f; // no minimum threshold for change in
+ private float mScreenDarkeningMinThreshold = 0.0f; // screen brightness or ambient
+ private float mScreenDarkeningMinThresholdIdle = 0.0f; // brightness.
+ private float mAmbientLuxBrighteningMinThreshold = 0.0f;
+ private float mAmbientLuxBrighteningMinThresholdIdle = 0.0f;
+ private float mAmbientLuxDarkeningMinThreshold = 0.0f;
+ private float mAmbientLuxDarkeningMinThresholdIdle = 0.0f;
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
@@ -625,22 +647,76 @@
return mAmbientHorizonShort;
}
+ /**
+ * The minimum value for the screen brightness increase to actually occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getScreenBrighteningMinThreshold() {
return mScreenBrighteningMinThreshold;
}
+ /**
+ * The minimum value for the screen brightness decrease to actually occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getScreenDarkeningMinThreshold() {
return mScreenDarkeningMinThreshold;
}
+ /**
+ * The minimum value for the screen brightness increase to actually occur while in idle screen
+ * brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getScreenBrighteningMinThresholdIdle() {
+ return mScreenBrighteningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the screen brightness decrease to actually occur while in idle screen
+ * brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getScreenDarkeningMinThresholdIdle() {
+ return mScreenDarkeningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the ambient lux increase for a screen brightness change to actually
+ * occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getAmbientLuxBrighteningMinThreshold() {
return mAmbientLuxBrighteningMinThreshold;
}
+ /**
+ * The minimum value for the ambient lux decrease for a screen brightness change to actually
+ * occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getAmbientLuxDarkeningMinThreshold() {
return mAmbientLuxDarkeningMinThreshold;
}
+ /**
+ * The minimum value for the ambient lux increase for a screen brightness change to actually
+ * occur while in idle screen brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getAmbientLuxBrighteningMinThresholdIdle() {
+ return mAmbientLuxBrighteningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the ambient lux decrease for a screen brightness change to actually
+ * occur while in idle screen brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getAmbientLuxDarkeningMinThresholdIdle() {
+ return mAmbientLuxDarkeningMinThresholdIdle;
+ }
+
SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
@@ -745,9 +821,14 @@
+ ", mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
+ ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
+ + ", mScreenDarkeningMinThresholdIdle=" + mScreenDarkeningMinThresholdIdle
+ ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
+ + ", mScreenBrighteningMinThresholdIdle=" + mScreenBrighteningMinThresholdIdle
+ ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
+ + ", mAmbientLuxDarkeningMinThresholdIdle=" + mAmbientLuxDarkeningMinThresholdIdle
+ ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
+ + ", mAmbientLuxBrighteningMinThresholdIdle="
+ + mAmbientLuxBrighteningMinThresholdIdle
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -1376,24 +1457,34 @@
private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
+ Thresholds displayBrightnessThresholdsIdle =
+ config.getDisplayBrightnessChangeThresholdsIdle();
+ Thresholds ambientBrightnessThresholdsIdle =
+ config.getAmbientBrightnessChangeThresholdsIdle();
+ loadDisplayBrightnessThresholds(displayBrightnessThresholds);
+ loadAmbientBrightnessThresholds(ambientBrightnessThresholds);
+ loadIdleDisplayBrightnessThresholds(displayBrightnessThresholdsIdle);
+ loadIdleAmbientBrightnessThresholds(ambientBrightnessThresholdsIdle);
+ }
+
+ private void loadDisplayBrightnessThresholds(Thresholds displayBrightnessThresholds) {
if (displayBrightnessThresholds != null) {
BrightnessThresholds brighteningScreen =
displayBrightnessThresholds.getBrighteningThresholds();
BrightnessThresholds darkeningScreen =
displayBrightnessThresholds.getDarkeningThresholds();
- final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
- final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
-
- if (screenBrighteningThreshold != null) {
- mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
+ if (brighteningScreen != null && brighteningScreen.getMinimum() != null) {
+ mScreenBrighteningMinThreshold = brighteningScreen.getMinimum().floatValue();
}
- if (screenDarkeningThreshold != null) {
- mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
+ if (darkeningScreen != null && darkeningScreen.getMinimum() != null) {
+ mScreenDarkeningMinThreshold = darkeningScreen.getMinimum().floatValue();
}
}
+ }
+ private void loadAmbientBrightnessThresholds(Thresholds ambientBrightnessThresholds) {
if (ambientBrightnessThresholds != null) {
BrightnessThresholds brighteningAmbientLux =
ambientBrightnessThresholds.getBrighteningThresholds();
@@ -1412,6 +1503,44 @@
}
}
+ private void loadIdleDisplayBrightnessThresholds(Thresholds idleDisplayBrightnessThresholds) {
+ if (idleDisplayBrightnessThresholds != null) {
+ BrightnessThresholds brighteningScreenIdle =
+ idleDisplayBrightnessThresholds.getBrighteningThresholds();
+ BrightnessThresholds darkeningScreenIdle =
+ idleDisplayBrightnessThresholds.getDarkeningThresholds();
+
+ if (brighteningScreenIdle != null
+ && brighteningScreenIdle.getMinimum() != null) {
+ mScreenBrighteningMinThresholdIdle =
+ brighteningScreenIdle.getMinimum().floatValue();
+ }
+ if (darkeningScreenIdle != null && darkeningScreenIdle.getMinimum() != null) {
+ mScreenDarkeningMinThresholdIdle =
+ darkeningScreenIdle.getMinimum().floatValue();
+ }
+ }
+ }
+
+ private void loadIdleAmbientBrightnessThresholds(Thresholds idleAmbientBrightnessThresholds) {
+ if (idleAmbientBrightnessThresholds != null) {
+ BrightnessThresholds brighteningAmbientLuxIdle =
+ idleAmbientBrightnessThresholds.getBrighteningThresholds();
+ BrightnessThresholds darkeningAmbientLuxIdle =
+ idleAmbientBrightnessThresholds.getDarkeningThresholds();
+
+ if (brighteningAmbientLuxIdle != null
+ && brighteningAmbientLuxIdle.getMinimum() != null) {
+ mAmbientLuxBrighteningMinThresholdIdle =
+ brighteningAmbientLuxIdle.getMinimum().floatValue();
+ }
+ if (darkeningAmbientLuxIdle != null && darkeningAmbientLuxIdle.getMinimum() != null) {
+ mAmbientLuxDarkeningMinThresholdIdle =
+ darkeningAmbientLuxIdle.getMinimum().floatValue();
+ }
+ }
+ }
+
private boolean thermalStatusIsValid(ThermalStatus value) {
if (value == null) {
return false;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e53ac37..fd30d7e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -200,6 +200,8 @@
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
+ private static final String PROP_USE_NEW_DISPLAY_POWER_CONTROLLER =
+ "persist.sys.use_new_display_power_controller";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
// This value needs to be in sync with the threshold
// in RefreshRateConfigs::getFrameRateDivisor.
@@ -274,7 +276,7 @@
new CopyOnWriteArrayList<>();
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
- private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
+ private final SparseArray<DisplayPowerControllerInterface> mDisplayPowerControllers =
new SparseArray<>();
/** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
@@ -497,7 +499,6 @@
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
mWideColorSpace = colorSpaces[1];
mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
-
mSystemReady = false;
}
@@ -577,7 +578,7 @@
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
- final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc == null) {
return;
@@ -1557,7 +1558,7 @@
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
@@ -1575,7 +1576,8 @@
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerController dpc = mDisplayPowerControllers.removeReturnOld(displayId);
+ final DisplayPowerControllerInterface dpc =
+ mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
dpc.stop();
}
@@ -1604,7 +1606,7 @@
mHandler.post(work);
}
final int displayId = display.getDisplayIdLocked();
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
@@ -1615,7 +1617,7 @@
private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDeviceStateTransition();
}
@@ -1852,14 +1854,14 @@
if (userId != mCurrentUserId) {
return;
}
- DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
dpc.setBrightnessConfiguration(c);
}
}
}
- private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
+ private DisplayPowerControllerInterface getDpcFromUniqueIdLocked(String uniqueId) {
final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
if (logicalDisplay != null) {
@@ -1900,7 +1902,7 @@
final BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
if (config != null) {
- final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc != null) {
dpc.setBrightnessConfiguration(config);
@@ -2132,8 +2134,8 @@
void setAutoBrightnessLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
}
@@ -2142,8 +2144,8 @@
void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
}
@@ -2170,8 +2172,8 @@
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAmbientColorTemperatureOverride(cct);
}
@@ -2180,8 +2182,8 @@
void setDockedAndIdleEnabled(boolean enabled, int displayId) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- displayId);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(displayId);
if (displayPowerController != null) {
displayPowerController.setAutomaticScreenBrightnessMode(enabled);
}
@@ -2554,10 +2556,19 @@
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
- final DisplayPowerController displayPowerController = new DisplayPowerController(
- mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
- mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ final DisplayPowerController displayPowerController;
+
+ if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) {
+ displayPowerController = new DisplayPowerController(
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ () -> handleBrightnessChange(display));
+ } else {
+ displayPowerController = new DisplayPowerController(
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ () -> handleBrightnessChange(display));
+ }
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
@@ -3212,7 +3223,7 @@
uniqueId, userSerial);
if (config == null) {
// Get default configuration
- DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
config = dpc.getDefaultBrightnessConfiguration();
}
@@ -3263,7 +3274,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
return dpc.getBrightnessInfo();
}
@@ -3310,7 +3321,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.setBrightness(brightness);
}
@@ -3330,7 +3341,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
brightness = dpc.getScreenBrightnessSetting();
}
@@ -3534,7 +3545,7 @@
id).getPrimaryDisplayDeviceLocked();
final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
- final DisplayPowerController displayPowerController =
+ final DisplayPowerControllerInterface displayPowerController =
mDisplayPowerControllers.get(id);
ready &= displayPowerController.requestPowerState(request,
waitForNegativeProximity);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2476350..58a80e3 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -104,7 +104,7 @@
* slower by changing the "animator duration scale" option in Development Settings.
*/
final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
- DisplayWhiteBalanceController.Callbacks {
+ DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
@@ -682,6 +682,7 @@
/**
* Returns true if the proximity sensor screen-off function is available.
*/
+ @Override
public boolean isProximitySensorAvailable() {
return mProximitySensor != null;
}
@@ -693,6 +694,7 @@
* @param includePackage if false will null out the package name in events
*/
@Nullable
+ @Override
public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
@UserIdInt int userId, boolean includePackage) {
if (mBrightnessTracker == null) {
@@ -701,6 +703,7 @@
return mBrightnessTracker.getEvents(userId, includePackage);
}
+ @Override
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
if (mBrightnessTracker != null) {
@@ -709,6 +712,7 @@
}
@Nullable
+ @Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@UserIdInt int userId) {
if (mBrightnessTracker == null) {
@@ -720,6 +724,7 @@
/**
* Persist the brightness slider events and ambient brightness stats to disk.
*/
+ @Override
public void persistBrightnessTrackerState() {
if (mBrightnessTracker != null) {
mBrightnessTracker.persistBrightnessTrackerState();
@@ -781,6 +786,7 @@
}
}
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -794,6 +800,7 @@
* of each display need to be properly reflected in AutomaticBrightnessController.
*/
@GuardedBy("DisplayManagerService.mSyncRoot")
+ @Override
public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
@@ -823,6 +830,7 @@
* This process involves turning off some displays so we need updatePowerState() to run and
* calculate the new state.
*/
+ @Override
public void onDeviceStateTransition() {
sendUpdatePowerState();
}
@@ -833,6 +841,7 @@
* This method should be called when the DisplayPowerController is no longer in use; i.e. when
* the {@link #mDisplayId display} has been removed.
*/
+ @Override
public void stop() {
synchronized (mLock) {
mStopped = true;
@@ -989,6 +998,25 @@
screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
screenDarkeningMinThreshold, screenBrighteningMinThreshold);
+ // Idle screen thresholds
+ float screenDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
+ float screenBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
+ HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
+
+ // Idle ambient thresholds
+ float ambientDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
+ float ambientBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
+ HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThresholdIdle,
+ ambientBrighteningMinThresholdIdle);
+
long brighteningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
@@ -1024,7 +1052,8 @@
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
- ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong());
@@ -1063,6 +1092,7 @@
}
}
+ @Override
public void setAutomaticScreenBrightnessMode(boolean isIdle) {
if (mAutomaticBrightnessController != null) {
if (isIdle) {
@@ -1766,27 +1796,32 @@
* Ignores the proximity sensor until the sensor state changes, but only if the sensor is
* currently enabled and forcing the screen to be dark.
*/
+ @Override
public void ignoreProximitySensorUntilChanged() {
mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
}
+ @Override
public void setBrightnessConfiguration(BrightnessConfiguration c) {
Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
msg.sendToTarget();
}
+ @Override
public void setTemporaryBrightness(float brightness) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
Float.floatToIntBits(brightness), 0 /*unused*/);
msg.sendToTarget();
}
+ @Override
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
Float.floatToIntBits(adjustment), 0 /*unused*/);
msg.sendToTarget();
}
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -2347,7 +2382,8 @@
return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}
- float getScreenBrightnessSetting() {
+ @Override
+ public float getScreenBrightnessSetting() {
float brightness = mBrightnessSetting.getBrightness();
if (Float.isNaN(brightness)) {
brightness = mScreenBrightnessDefault;
@@ -2362,7 +2398,8 @@
return clampScreenBrightnessForVr(brightnessFloat);
}
- void setBrightness(float brightnessValue) {
+ @Override
+ public void setBrightness(float brightnessValue) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
mBrightnessSetting.setBrightness(brightnessValue);
@@ -2511,6 +2548,7 @@
}
};
+ @Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
@@ -2919,7 +2957,8 @@
}
}
- void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ @Override
+ public void setAutoBrightnessLoggingEnabled(boolean enabled) {
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.setLoggingEnabled(enabled);
}
@@ -2930,14 +2969,16 @@
sendUpdatePowerState();
}
- void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ @Override
+ public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
}
}
- void setAmbientColorTemperatureOverride(float cct) {
+ @Override
+ public void setAmbientColorTemperatureOverride(float cct) {
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
// The ambient color temperature override is only applied when the ambient color
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
new file mode 100644
index 0000000..6677f35
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.pm.ParceledListSlice;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface to manage the display's power state and brightness
+ */
+public interface DisplayPowerControllerInterface {
+
+ /**
+ * Notified when the display is changed. We use this to apply any changes that might be needed
+ * when displays get swapped on foldable devices.
+ */
+ void onDisplayChanged();
+
+ /**
+ * Unregisters all listeners and interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+ * the display has been removed.
+ */
+ void stop();
+
+ /**
+ * Used to manage the displays preparing to transition from one device state to another.
+ */
+ void onDeviceStateTransition();
+
+ /**
+ * Used to update the display's BrightnessConfiguration
+ * @param config The new BrightnessConfiguration
+ */
+ void setBrightnessConfiguration(BrightnessConfiguration config);
+
+ /**
+ * Used to set the ambient color temperature of the Display
+ * @param ambientColorTemperature The target ambientColorTemperature
+ */
+ void setAmbientColorTemperatureOverride(float ambientColorTemperature);
+
+ /**
+ * Used to decide the associated AutomaticBrightnessController's BrightnessMode
+ * @param isIdle Flag which represents if the Idle BrightnessMode is to be set
+ */
+ void setAutomaticScreenBrightnessMode(boolean isIdle);
+
+ /**
+ * Used to enable/disable the logging of the WhileBalance associated entities
+ * @param enabled Flag which represents if the logging is the be enabled
+ */
+ void setDisplayWhiteBalanceLoggingEnabled(boolean enabled);
+
+ /**
+ * Used to dump the state.
+ * @param writer The PrintWriter used to dump the state.
+ */
+ void dump(PrintWriter writer);
+
+ /**
+ * Used to get the ambient brightness stats
+ */
+ ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId);
+
+ /**
+ * Get the default brightness configuration
+ */
+ BrightnessConfiguration getDefaultBrightnessConfiguration();
+
+ /**
+ * Set the screen brightness of the associated display
+ * @param brightness The value to which the brightness is to be set
+ */
+ void setBrightness(float brightness);
+
+ /**
+ * Checks if the proximity sensor is available
+ */
+ boolean isProximitySensorAvailable();
+
+ /**
+ * Persist the brightness slider events and ambient brightness stats to disk.
+ */
+ void persistBrightnessTrackerState();
+
+ /**
+ * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+ * currently enabled and forcing the screen to be dark.
+ */
+ void ignoreProximitySensorUntilChanged();
+
+ /**
+ * Requests a new power state.
+ *
+ * @param request The requested power state.
+ * @param waitForNegativeProximity If true, issues a request to wait for
+ * negative proximity before turning the screen back on,
+ * assuming the screen was turned off by the proximity sensor.
+ * @return True if display is ready, false if there are important changes that must
+ * be made asynchronously.
+ */
+ boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
+ boolean waitForNegativeProximity);
+
+ /**
+ * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
+ * value.
+ */
+ void setTemporaryAutoBrightnessAdjustment(float adjustment);
+
+ /**
+ * Gets the screen brightness setting
+ */
+ float getScreenBrightnessSetting();
+
+ /**
+ * Sets up the temporary brightness for the associated display
+ */
+ void setTemporaryBrightness(float brightness);
+
+ /**
+ * Gets the associated {@link BrightnessInfo}
+ */
+ BrightnessInfo getBrightnessInfo();
+
+ /**
+ * Get the {@link BrightnessChangeEvent}s for the specified user.
+ */
+ ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(int userId, boolean hasUsageStats);
+
+ /**
+ * Sets up the logging for the associated {@link AutomaticBrightnessController}
+ * @param enabled Flag to represent if the logging is to be enabled
+ */
+ void setAutoBrightnessLoggingEnabled(boolean enabled);
+
+ /**
+ * Handles the changes to be done to update the brightness when the user is changed
+ * @param newUserId The new userId
+ */
+ void onSwitchUser(int newUserId);
+}
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 7a932ce..3413489 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,15 +18,12 @@
import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.PrintWriter;
import java.util.Arrays;
/**
* A helper class for handling access to illuminance hysteresis level values.
*/
-@VisibleForTesting
public class HysteresisLevels {
private static final String TAG = "HysteresisLevels";
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 6b568b7..63c0a88 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -29,16 +29,13 @@
import android.view.PointerIcon;
import android.view.VerifiedInputEvent;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.List;
/**
* An interface for the native methods of InputManagerService. We use a public interface so that
* this can be mocked for testing by Mockito.
*/
-@VisibleForTesting
-public interface NativeInputManagerService {
+interface NativeInputManagerService {
void start();
diff --git a/services/core/java/com/android/server/utils/Slogf.java b/services/core/java/com/android/server/utils/Slogf.java
index bbc5414..e88ac63 100644
--- a/services/core/java/com/android/server/utils/Slogf.java
+++ b/services/core/java/com/android/server/utils/Slogf.java
@@ -162,6 +162,21 @@
}
/**
+ * Logs a {@link Log.VEBOSE} message with an exception
+ *
+ * <p><strong>Note: </strong>the message will only be formatted if {@link Log#VERBOSE} logging
+ * is enabled for the given {@code tag}, but the compiler will still create an intermediate
+ * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
+ * you're calling this method in a critical path, make sure to explicitly do the check before
+ * calling it.
+ */
+ public static void v(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!isLoggable(tag, Log.VERBOSE)) return;
+
+ v(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Logs a {@link Log.DEBUG} message.
*
* <p><strong>Note: </strong>the message will only be formatted if {@link Log#DEBUG} logging is
@@ -177,6 +192,21 @@
}
/**
+ * Logs a {@link Log.DEBUG} message with an exception
+ *
+ * <p><strong>Note: </strong>the message will only be formatted if {@link Log#DEBUG} logging
+ * is enabled for the given {@code tag}, but the compiler will still create an intermediate
+ * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
+ * you're calling this method in a critical path, make sure to explicitly do the check before
+ * calling it.
+ */
+ public static void d(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!isLoggable(tag, Log.DEBUG)) return;
+
+ d(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Logs a {@link Log.INFO} message.
*
* <p><strong>Note: </strong>the message will only be formatted if {@link Log#INFO} logging is
@@ -192,6 +222,21 @@
}
/**
+ * Logs a {@link Log.INFO} message with an exception
+ *
+ * <p><strong>Note: </strong>the message will only be formatted if {@link Log#INFO} logging
+ * is enabled for the given {@code tag}, but the compiler will still create an intermediate
+ * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
+ * you're calling this method in a critical path, make sure to explicitly do the check before
+ * calling it.
+ */
+ public static void i(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!isLoggable(tag, Log.INFO)) return;
+
+ i(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Logs a {@link Log.WARN} message.
*
* <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
@@ -220,6 +265,7 @@
w(tag, getMessage(format, args), exception);
}
+
/**
* Logs a {@link Log.ERROR} message.
*
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 16b5ee5..1c583be 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1522,8 +1522,17 @@
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- // Animate change transition on TaskFragment level to get the correct window crop.
- newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ // For Shell transition, call #initializeChangeTransition directly to take the
+ // screenshot at the Activity level. And Shell will be in charge of handling the
+ // surface reparent and crop.
+ initializeChangeTransition(getBounds());
+ } else {
+ // For legacy app transition, we want to take a screenshot of the Activity surface,
+ // but animate the change transition on TaskFragment level to get the correct window
+ // crop.
+ newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+ }
}
super.onParentChanged(newParent, oldParent);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7471993..690c94a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1152,6 +1152,7 @@
mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
+ setWindowingMode(WINDOWING_MODE_FULLSCREEN);
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
}
@@ -2667,16 +2668,6 @@
}
@Override
- public void setWindowingMode(int windowingMode) {
- // Intentionally call onRequestedOverrideConfigurationChanged() directly to change windowing
- // mode and display windowing mode atomically.
- mTmpConfiguration.setTo(getRequestedOverrideConfiguration());
- mTmpConfiguration.windowConfiguration.setWindowingMode(windowingMode);
- mTmpConfiguration.windowConfiguration.setDisplayWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(mTmpConfiguration);
- }
-
- @Override
void setDisplayWindowingMode(int windowingMode) {
setWindowingMode(windowingMode);
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 9462d4f7..e0644b6 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -150,7 +150,10 @@
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
overrideSettings.mWindowingMode = mode;
- dc.setWindowingMode(mode);
+ final TaskDisplayArea defaultTda = dc.getDefaultTaskDisplayArea();
+ if (defaultTda != null) {
+ defaultTda.setWindowingMode(mode);
+ }
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
@@ -253,8 +256,10 @@
// Setting windowing mode first, because it may override overscan values later.
final int windowingMode = getWindowingModeLocked(settings, dc);
- dc.setWindowingMode(windowingMode);
-
+ final TaskDisplayArea defaultTda = dc.getDefaultTaskDisplayArea();
+ if (defaultTda != null) {
+ defaultTda.setWindowingMode(windowingMode);
+ }
final int userRotationMode = settings.mUserRotationMode != null
? settings.mUserRotationMode : WindowManagerPolicy.USER_ROTATION_FREE;
final int userRotation = settings.mUserRotation != null
@@ -311,10 +316,11 @@
* changed.
*/
boolean updateSettingsForDisplay(DisplayContent dc) {
- if (dc.getWindowingMode() != getWindowingModeLocked(dc)) {
+ final TaskDisplayArea defaultTda = dc.getDefaultTaskDisplayArea();
+ if (defaultTda != null && defaultTda.getWindowingMode() != getWindowingModeLocked(dc)) {
// For the time being the only thing that may change is windowing mode, so just update
// that.
- dc.setWindowingMode(getWindowingModeLocked(dc));
+ defaultTda.setWindowingMode(getWindowingModeLocked(dc));
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 1fd66fc..d497d8c 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -141,15 +141,16 @@
return false;
} else {
- mResultActivities.add(r);
if (r.resultTo != null) {
// If this activity is sending a reply to a previous activity, we can't do
// anything with it now until we reach the start of the reply chain.
// NOTE: that we are assuming the result is always to the previous activity,
// which is almost always the case but we really shouldn't count on.
+ mResultActivities.add(r);
return false;
} else if (mTargetTaskFound && allowTaskReparenting && mTargetTask.affinity != null
&& mTargetTask.affinity.equals(r.taskAffinity)) {
+ mResultActivities.add(r);
// This activity has an affinity for our task. Either remove it if we are
// clearing or move it over to our task. Note that we currently punt on the case
// where we are resetting a task that is not at the top but who has activities
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index fb68fe6..b9739f03 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -324,7 +324,7 @@
final int callingPid = Binder.getCallingPid();
// Validate and resolve ClipDescription data before clearing the calling identity
validateAndResolveDragMimeTypeExtras(data, callingUid, callingPid, mPackageName);
- validateDragFlags(flags, callingUid);
+ validateDragFlags(flags);
final long ident = Binder.clearCallingIdentity();
try {
return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
@@ -349,11 +349,7 @@
* Validates the given drag flags.
*/
@VisibleForTesting
- void validateDragFlags(int flags, int callingUid) {
- if (callingUid == Process.SYSTEM_UID) {
- throw new IllegalStateException("Need to validate before calling identify is cleared");
- }
-
+ void validateDragFlags(int flags) {
if ((flags & View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
if (!mCanStartTasksFromRecents) {
throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
@@ -367,9 +363,6 @@
@VisibleForTesting
void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid, int callingPid,
String callingPackage) {
- if (callingUid == Process.SYSTEM_UID) {
- throw new IllegalStateException("Need to validate before calling identify is cleared");
- }
final ClipDescription desc = data != null ? data.getDescription() : null;
if (desc == null) {
return;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4063cae4..b6c14bb 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -172,6 +172,8 @@
*/
private final boolean mCanHostHomeTask;
+ private final Configuration mTempConfiguration = new Configuration();
+
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
int displayAreaFeature) {
this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */,
@@ -1893,6 +1895,15 @@
}
@Override
+ public void setWindowingMode(int windowingMode) {
+ mTempConfiguration.setTo(getRequestedOverrideConfiguration());
+ WindowConfiguration tempRequestWindowConfiguration = mTempConfiguration.windowConfiguration;
+ tempRequestWindowConfiguration.setWindowingMode(windowingMode);
+ tempRequestWindowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mTempConfiguration);
+ }
+
+ @Override
TaskDisplayArea getTaskDisplayArea() {
return this;
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 1362094..8444489 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -135,7 +135,7 @@
final DisplayContent display = suggestedDisplayArea.mDisplayContent;
if (DEBUG) {
appendLog("display-id=" + display.getDisplayId()
- + " display-windowing-mode=" + display.getWindowingMode()
+ + " task-display-area-windowing-mode=" + suggestedDisplayArea.getWindowingMode()
+ " suggested-display-area=" + suggestedDisplayArea);
}
@@ -154,7 +154,7 @@
// source is a freeform window in a fullscreen display launching an activity on the same
// display.
if (launchMode == WINDOWING_MODE_UNDEFINED
- && canInheritWindowingModeFromSource(display, source)) {
+ && canInheritWindowingModeFromSource(display, suggestedDisplayArea, source)) {
// The source's windowing mode may be different from its task, e.g. activity is set
// to fullscreen and its task is pinned windowing mode when the activity is entering
// pip.
@@ -182,7 +182,8 @@
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalculating the bounds.
boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
- final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
+ final boolean canApplyFreeformPolicy =
+ canApplyFreeformWindowPolicy(suggestedDisplayArea, launchMode);
if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
&& (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
hasInitialBounds = true;
@@ -237,7 +238,8 @@
== display.getDisplayId())) {
// Only set windowing mode if display is in freeform. If the display is in fullscreen
// mode we should only launch a task in fullscreen mode.
- if (currentParams.hasWindowingMode() && display.inFreeformWindowingMode()) {
+ if (currentParams.hasWindowingMode()
+ && suggestedDisplayArea.inFreeformWindowingMode()) {
launchMode = currentParams.mWindowingMode;
fullyResolvedCurrentParam = launchMode != WINDOWING_MODE_FREEFORM;
if (DEBUG) {
@@ -265,11 +267,11 @@
// this step is to define the default policy when there is no initial bounds or a fully
// resolved current params from callers.
- // hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay is set if the outParams.mBounds
+ // hasInitialBoundsForSuggestedDisplayAreaInFreeformMode is set if the outParams.mBounds
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalcuating the bounds.
- boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = false;
- if (display.inFreeformWindowingMode()) {
+ boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = false;
+ if (suggestedDisplayArea.inFreeformWindowingMode()) {
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
} else if (!root.isResizeable()) {
@@ -278,7 +280,7 @@
if (outParams.mBounds.isEmpty()) {
getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
hasInitialBounds, outParams.mBounds);
- hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = true;
+ hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
}
if (DEBUG) appendLog("unresizable-freeform");
} else {
@@ -288,10 +290,10 @@
}
}
} else {
- if (DEBUG) appendLog("non-freeform-display");
+ if (DEBUG) appendLog("non-freeform-task-display-area");
}
// If launch mode matches display windowing mode, let it inherit from display.
- outParams.mWindowingMode = launchMode == display.getWindowingMode()
+ outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()
? WINDOWING_MODE_UNDEFINED : launchMode;
if (phase == PHASE_WINDOWING_MODE) {
@@ -301,7 +303,7 @@
// STEP 3: Finalize the display area. Here we allow WM shell route all launches that match
// certain criteria to specific task display areas.
final int resolvedMode = (launchMode != WINDOWING_MODE_UNDEFINED) ? launchMode
- : display.getWindowingMode();
+ : suggestedDisplayArea.getWindowingMode();
TaskDisplayArea taskDisplayArea = suggestedDisplayArea;
// If launch task display area is set in options we should just use it. We assume the
// suggestedDisplayArea has the right one in this case.
@@ -319,14 +321,17 @@
mTmpDisplayArea = displayArea;
return true;
});
- // We may need to recalculate the bounds if the new TaskDisplayArea is different from
- // the suggested one we used to calculate the bounds.
+ // We may need to recalculate the bounds and the windowing mode if the new
+ // TaskDisplayArea is different from the suggested one we used to calculate the two
+ // configurations.
if (mTmpDisplayArea != null && mTmpDisplayArea != suggestedDisplayArea) {
+ outParams.mWindowingMode = (launchMode == mTmpDisplayArea.getWindowingMode())
+ ? WINDOWING_MODE_UNDEFINED : launchMode;
if (hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow) {
outParams.mBounds.setEmpty();
getLayoutBounds(mTmpDisplayArea, root, layout, outParams.mBounds);
hasInitialBounds = !outParams.mBounds.isEmpty();
- } else if (hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay) {
+ } else if (hasInitialBoundsForSuggestedDisplayAreaInFreeformMode) {
outParams.mBounds.setEmpty();
getTaskBounds(root, mTmpDisplayArea, layout, launchMode,
hasInitialBounds, outParams.mBounds);
@@ -533,7 +538,7 @@
}
private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display,
- @Nullable ActivityRecord source) {
+ TaskDisplayArea suggestedDisplayArea, @Nullable ActivityRecord source) {
if (source == null) {
return false;
}
@@ -541,7 +546,7 @@
// There is not really any strong reason to tie the launching windowing mode and the source
// on freeform displays. The launching windowing mode is more tied to the content of the new
// activities.
- if (display.inFreeformWindowingMode()) {
+ if (suggestedDisplayArea.inFreeformWindowingMode()) {
return false;
}
@@ -557,9 +562,11 @@
return display.getDisplayId() == source.getDisplayId();
}
- private boolean canApplyFreeformWindowPolicy(@NonNull DisplayContent display, int launchMode) {
+ private boolean canApplyFreeformWindowPolicy(@NonNull TaskDisplayArea suggestedDisplayArea,
+ int launchMode) {
return mSupervisor.mService.mSupportsFreeformWindowManagement
- && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
+ && (suggestedDisplayArea.inFreeformWindowingMode()
+ || launchMode == WINDOWING_MODE_FREEFORM);
}
private boolean canApplyPipWindowPolicy(int launchMode) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 221e186..77d0f37 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -67,7 +67,7 @@
/** Which sync method to use for transition syncs. */
static final int SYNC_METHOD =
- android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", true)
+ android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", false)
? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE;
/** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 72e7e65..8055590 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -631,12 +630,6 @@
getResolvedOverrideConfiguration().updateFrom(
mFixedRotationTransformState.mRotatedOverrideConfiguration);
}
- if (getTaskDisplayArea() == null) {
- // We only defined behaviors of system windows in fullscreen mode, i.e. windows not
- // contained in a task display area.
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
- WINDOWING_MODE_FULLSCREEN);
- }
}
@Override
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6b05d8f..267cff6 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -97,6 +97,16 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <!-- Set of thresholds that dictate the change needed for screen brightness
+ adaptations while in idle mode -->
+ <xs:element type="thresholds" name="displayBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Set of thresholds that dictate the change needed for ambient brightness
+ adaptations while in idle mode -->
+ <xs:element type="thresholds" name="ambientBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -319,13 +329,13 @@
<!-- Thresholds for brightness changes. -->
<xs:complexType name="thresholds">
<xs:sequence>
- <!-- Brightening thresholds. -->
+ <!-- Brightening thresholds for active screen brightness mode. -->
<xs:element name="brighteningThresholds" type="brightnessThresholds" minOccurs="0"
maxOccurs="1" >
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <!-- Darkening thresholds. -->
+ <!-- Darkening thresholds for active screen brightness mode. -->
<xs:element name="darkeningThresholds" type="brightnessThresholds" minOccurs="0"
maxOccurs="1" >
<xs:annotation name="nonnull"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index fb7a920..f8bff75 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -61,11 +61,13 @@
public class DisplayConfiguration {
ctor public DisplayConfiguration();
method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
+ method public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholdsIdle();
method public final java.math.BigInteger getAmbientLightHorizonLong();
method public final java.math.BigInteger getAmbientLightHorizonShort();
method public com.android.server.display.config.AutoBrightness getAutoBrightness();
method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
+ method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -80,11 +82,13 @@
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+ method public final void setAmbientBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
method public final void setAmbientLightHorizonShort(java.math.BigInteger);
method public void setAutoBrightness(com.android.server.display.config.AutoBrightness);
method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+ method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3c68662..f05b1d4 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -218,6 +218,9 @@
BackgroundThread.get().getThreadHandler().post(
() -> {
try {
+ if (sSelfService.mIProfcollect == null) {
+ return;
+ }
sSelfService.mIProfcollect.process();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to process profiles in background: "
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 6a18dc1..9a4bb22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -233,4 +233,4 @@
});
when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
}
-}
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
index 538adb2..6f0efb0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
@@ -342,7 +342,6 @@
addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
.that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
@@ -584,11 +583,19 @@
}
protected final void startDefaultProfile() {
- setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+ startUser(PROFILE_USER_ID);
}
protected final void stopDefaultProfile() {
- setUserState(PROFILE_USER_ID, UserState.STATE_STOPPING);
+ stopUser(PROFILE_USER_ID);
+ }
+
+ protected final void startUser(@UserIdInt int userId) {
+ setUserState(userId, UserState.STATE_RUNNING_UNLOCKED);
+ }
+
+ protected final void stopUser(@UserIdInt int userId) {
+ setUserState(userId, UserState.STATE_STOPPING);
}
// NOTE: should only called by tests that indirectly needs to check user assignments (like
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 8388a70..8b5921c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -88,6 +88,42 @@
.that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse();
}
+ @Test
+ public void testIsUserRunning_StartedUserShouldReturnTrue() {
+ addUser(USER_ID);
+ startUser(USER_ID);
+
+ assertWithMessage("isUserRunning(%s)", USER_ID)
+ .that(mUms.isUserRunning(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserRunning_StoppedUserShouldReturnFalse() {
+ addUser(USER_ID);
+ stopUser(USER_ID);
+
+ assertWithMessage("isUserRunning(%s)", USER_ID)
+ .that(mUms.isUserRunning(USER_ID)).isFalse();
+ }
+
+ @Test
+ public void testIsUserRunning_CurrentUserStartedWorkProfileShouldReturnTrue() {
+ addDefaultProfileAndParent();
+ startDefaultProfile();
+
+ assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+ .that(mUms.isUserRunning(PROFILE_USER_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserRunning_CurrentUserStoppedWorkProfileShouldReturnFalse() {
+ addDefaultProfileAndParent();
+ stopDefaultProfile();
+
+ assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+ .that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
+ }
+
@Override
protected boolean isUserVisible(int userId) {
return mUms.isUserVisibleUnchecked(userId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java
new file mode 100644
index 0000000..29bddfc
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EconomicPolicyTest {
+
+ @Test
+ public void testMasksDisjoint() {
+ assertEquals(-1,
+ (-1 & EconomicPolicy.MASK_TYPE)
+ + (-1 & EconomicPolicy.MASK_POLICY)
+ + (-1 & EconomicPolicy.MASK_EVENT));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
index 3e8cef9..ae25c1b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
@@ -103,6 +103,24 @@
}
@Test
+ public void testV_msgFormattedWithException_enabled() {
+ enableLogging(Log.VERBOSE);
+
+ Slogf.v(TAG, mException, "msg in a %s", "bottle");
+
+ verify(()-> Slog.v(TAG, "msg in a bottle", mException));
+ }
+
+ @Test
+ public void testV_msgFormattedWithException_disabled() {
+ disableLogging(Log.VERBOSE);
+
+ Slogf.v(TAG, "msg in a %s", "bottle");
+
+ verify(()-> Slog.v(eq(TAG), any(String.class), any(Throwable.class)), never());
+ }
+
+ @Test
public void testD_msg() {
Slogf.d(TAG, "msg");
@@ -135,6 +153,24 @@
}
@Test
+ public void testD_msgFormattedWithException_enabled() {
+ enableLogging(Log.DEBUG);
+
+ Slogf.d(TAG, mException, "msg in a %s", "bottle");
+
+ verify(()-> Slog.d(TAG, "msg in a bottle", mException));
+ }
+
+ @Test
+ public void testD_msgFormattedWithException_disabled() {
+ disableLogging(Log.DEBUG);
+
+ Slogf.d(TAG, mException, "msg in a %s", "bottle");
+
+ verify(()-> Slog.d(eq(TAG), any(String.class), any(Throwable.class)), never());
+ }
+
+ @Test
public void testI_msg() {
Slogf.i(TAG, "msg");
@@ -167,6 +203,24 @@
}
@Test
+ public void testI_msgFormattedWithException_enabled() {
+ enableLogging(Log.INFO);
+
+ Slogf.i(TAG, mException, "msg in a %s", "bottle");
+
+ verify(()-> Slog.i(TAG, "msg in a bottle", mException));
+ }
+
+ @Test
+ public void testI_msgFormattedWithException_disabled() {
+ disableLogging(Log.INFO);
+
+ Slogf.i(TAG, mException, "msg in a %s", "bottle");
+
+ verify(()-> Slog.i(eq(TAG), any(String.class), any(Throwable.class)), never());
+ }
+
+ @Test
public void testW_msg() {
Slogf.w(TAG, "msg");
@@ -218,7 +272,7 @@
public void testW_msgFormattedWithException_disabled() {
disableLogging(Log.WARN);
- Slogf.w(TAG, "msg in a %s", "bottle");
+ Slogf.w(TAG, mException, "msg in a %s", "bottle");
verify(()-> Slog.w(eq(TAG), any(String.class), any(Throwable.class)), never());
}
@@ -268,7 +322,7 @@
public void testE_msgFormattedWithException_disabled() {
disableLogging(Log.ERROR);
- Slogf.e(TAG, "msg in a %s", "bottle");
+ Slogf.e(TAG, mException, "msg in a %s", "bottle");
verify(()-> Slog.e(eq(TAG), any(String.class), any(Throwable.class)), never());
}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index fe079f4..81f899c 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -181,6 +181,11 @@
mockIsUsersOnSecondaryDisplaysEnabled(false);
// All UserController params are set to default.
mUserController = new UserController(mInjector);
+
+ // TODO(b/232452368): need to explicitly call setAllowUserUnlocking(), otherwise most
+ // tests would fail. But we might need to disable it for the onBootComplete() test (i.e,
+ // to make sure the users are unlocked at the right time)
+ mUserController.setAllowUserUnlocking(true);
setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null);
});
@@ -628,6 +633,16 @@
}
@Test
+ public void testUserNotUnlockedBeforeAllowed() throws Exception {
+ mUserController.setAllowUserUnlocking(false);
+
+ mUserController.startUser(TEST_USER_ID, /* foreground= */ false);
+
+ verify(mInjector.mStorageManagerMock, never())
+ .unlockUserKey(eq(TEST_USER_ID), anyInt(), any());
+ }
+
+ @Test
public void testStartProfile_fullUserFails() {
setUpUser(TEST_USER_ID1, 0);
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 92e1f27a..837b553 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -253,6 +255,16 @@
showHideOverlay(c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
}
+ @Test
+ public void testPowerPressForwardsAcquireMessage() throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+ client.start(mCallback);
+ client.onPowerPressed();
+
+ verify(mClientMonitorCallbackConverter).onAcquired(anyInt(),
+ eq(FINGERPRINT_ACQUIRED_POWER_PRESSED), anyInt());
+ }
+
private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
throws RemoteException {
final FingerprintEnrollClient client = createClient();
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 9d82f1a..8280fc6 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -82,6 +82,8 @@
@Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
@Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
+ @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
+ @Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
@Mock Handler mNoOpHandler;
@Mock HighBrightnessModeController mHbmController;
@Mock BrightnessThrottler mBrightnessThrottler;
@@ -129,6 +131,7 @@
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
+ mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
);
@@ -314,8 +317,9 @@
// Now let's do the same for idle mode
mController.switchToIdleMode();
- // Called once for init, and once when switching
- verify(mBrightnessMappingStrategy, times(2)).isForIdleMode();
+ // Called once for init, and once when switching,
+ // setAmbientLux() is called twice and once in updateAutoBrightness()
+ verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
// Ensure, after switching, original BMS is not used anymore
verifyNoMoreInteractions(mBrightnessMappingStrategy);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
index fd40880..cff3bf8 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
@@ -38,6 +38,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,6 +79,7 @@
batteryOnScreenOff();
}
+ @Ignore("b/244349060")
@Test
public void testNoCpuDataForRemovedUser() throws Exception {
mIam.startUserInBackground(mTestUserId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index b5764f5..4d71b30 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -134,9 +134,9 @@
@Test
public void testNoChangeOnOldDisplayWhenMoveDisplay() {
- mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mDisplayContent.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
- dc1.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ dc1.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FREEFORM);
setUpOnDisplay(dc1);
assertEquals(WINDOWING_MODE_FREEFORM, mTask.getWindowingMode());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7244d94..44471f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1067,6 +1067,7 @@
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
+ dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int newOrientation = getRotatedOrientation(dc);
final Task task = new TaskBuilder(mSupervisor)
@@ -1126,6 +1127,7 @@
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0);
+ dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int newOrientation = getRotatedOrientation(dc);
final Task task = new TaskBuilder(mSupervisor)
@@ -2032,23 +2034,6 @@
}
@Test
- public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
- final DisplayContent dc = createNewDisplay();
- final Task rootTask = new TaskBuilder(mSupervisor)
- .setDisplay(dc)
- .build();
- doAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- final Configuration config = ((Configuration) args[0]);
- assertEquals(config.windowConfiguration.getWindowingMode(),
- config.windowConfiguration.getDisplayWindowingMode());
- return null;
- }).when(rootTask).onConfigurationChanged(any());
- dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
- dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- }
-
- @Test
public void testForceDesktopMode() {
mWm.mForceDesktopModeOnExternalDisplays = true;
// Not applicable for default display
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 093be82..c398a0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -110,7 +110,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mPrimaryDisplay.getWindowingMode());
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -120,7 +120,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mPrimaryDisplay.getWindowingMode());
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -131,7 +131,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mPrimaryDisplay.getWindowingMode());
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -141,7 +141,8 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
- assertEquals(WINDOWING_MODE_FREEFORM, mPrimaryDisplay.getWindowingMode());
+ assertEquals(WINDOWING_MODE_FREEFORM,
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -154,7 +155,7 @@
mDisplayWindowSettings.updateSettingsForDisplay(mPrimaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
- mPrimaryDisplay.getWindowingMode());
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -162,7 +163,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mSecondaryDisplay.getWindowingMode());
+ mSecondaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -172,7 +173,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mSecondaryDisplay.getWindowingMode());
+ mSecondaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -183,7 +184,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
assertEquals(WINDOWING_MODE_FREEFORM,
- mSecondaryDisplay.getWindowingMode());
+ mSecondaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
@@ -194,7 +195,7 @@
mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
assertEquals(WINDOWING_MODE_FREEFORM,
- mSecondaryDisplay.getWindowingMode());
+ mSecondaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 28fc352..4526d18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -467,8 +467,7 @@
public void onAnimatorScaleChanged(float scale) {}
});
try {
- session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
- TEST_UID);
+ session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
fail("Expected failure without permission");
} catch (SecurityException e) {
// Expected failure
@@ -484,8 +483,7 @@
public void onAnimatorScaleChanged(float scale) {}
});
try {
- session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
- TEST_UID);
+ session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
// Expected pass
} catch (SecurityException e) {
fail("Expected no failure with permission");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9e658e0..4f03f54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -108,9 +108,10 @@
}
@Test
- public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() {
+ public void testUpdateDefaultTaskDisplayAreaWindowingModeOnSettingsRetrieved() {
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mWm.getDefaultDisplayContentLocked().getWindowingMode());
+ mWm.getDefaultDisplayContentLocked().getDefaultTaskDisplayArea()
+ .getWindowingMode());
mWm.mIsPc = true;
mWm.mAtmService.mSupportsFreeformWindowManagement = true;
@@ -118,7 +119,8 @@
mWm.mRoot.onSettingsRetrieved();
assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
- mWm.getDefaultDisplayContentLocked().getWindowingMode());
+ mWm.getDefaultDisplayContentLocked().getDefaultTaskDisplayArea()
+ .getWindowingMode());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 646f43c..9f7f341 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -676,7 +676,8 @@
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
- mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldCreateCompatDisplayInsets());
// Activity should not be sandboxed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 9e6c4c5..f5fc5c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -345,7 +345,7 @@
// Set default display to be in fullscreen mode. Devices with PC feature may start their
// default display in freeform mode but some of tests in WmTests have implicit assumption on
// that the default display is in fullscreen mode.
- display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ display.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(display);
final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 22101c2..aaf855f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1843,7 +1843,7 @@
private TestDisplayContent createNewDisplayContent(int windowingMode) {
final TestDisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- display.setWindowingMode(windowingMode);
+ display.getDefaultTaskDisplayArea().setWindowingMode(windowingMode);
display.setBounds(DISPLAY_BOUNDS);
display.getConfiguration().densityDpi = DENSITY_DEFAULT;
display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 8e89d6f..e352d76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1257,7 +1257,8 @@
final Task task = getTestTask();
task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getDisplayContent().getDefaultTaskDisplayArea()
+ .setWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setHasBeenVisible(true);
@@ -1273,7 +1274,9 @@
final Task task = getTestTask();
task.setHasBeenVisible(false);
- task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ task.getDisplayContent()
+ .getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final DisplayContent oldDisplay = task.getDisplayContent();
@@ -1313,7 +1316,8 @@
final Task task = getTestTask();
task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getDisplayContent().getDefaultTaskDisplayArea()
+ .setWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
task.setHasBeenVisible(true);
@@ -1330,7 +1334,8 @@
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setCreateParentTask(true).build().getRootTask();
task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getDisplayContent().getDefaultTaskDisplayArea()
+ .setWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final Task leafTask = createTaskInRootTask(task, 0 /* userId */);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
new file mode 100644
index 0000000..ea5a5f8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class GameAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.GAME_ACTIVITY_LAUNCHER_NAME,
+ component: ComponentNameMatcher =
+ ActivityOptions.GAME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy,
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ /**
+ * Swipes down in the mock game app.
+ *
+ * @return true if the swipe operation is successful.
+ */
+ fun swipeDown(): Boolean {
+ val gameView = uiDevice.wait(
+ Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS)
+ require(gameView != null) { "Mock game app view not found." }
+
+ val bound = gameView.getVisibleBounds()
+ return uiDevice.swipe(
+ bound.centerX(), bound.top, bound.centerX(), bound.centerY(), SWIPE_STEPS)
+ }
+
+ /**
+ * Switches to a recent app by quick switch gesture. This function can be used in both portrait
+ * and landscape mode.
+ *
+ * @param wmHelper Helper used to get window region.
+ * @param direction UiAutomator Direction enum to indicate the swipe direction.
+ *
+ * @return true if the swipe operation is successful.
+ */
+ fun switchToPreviousAppByQuickSwitchGesture(
+ wmHelper: WindowManagerStateHelper,
+ direction: Direction
+ ): Boolean {
+ val ratioForScreenBottom = 0.97
+ val fullView = wmHelper.getWindowRegion(componentMatcher)
+ require(!fullView.isEmpty) { "Target $componentMatcher view not found." }
+
+ val bound = fullView.bounds
+ val targetYPos = bound.bottom * ratioForScreenBottom
+ val endX = when (direction) {
+ Direction.LEFT -> bound.left
+ Direction.RIGHT -> bound.right
+ else -> {
+ throw IllegalStateException("Only left or right direction is allowed.")
+ }
+ }
+ return uiDevice.swipe(
+ bound.centerX(), targetYPos.toInt(), endX, targetYPos.toInt(), SWIPE_STEPS)
+ }
+
+ /**
+ * Waits for view idel with timeout, then checkes the target object whether visible on screen.
+ *
+ * @param packageName The targe application's package name.
+ * @param identifier The resource id of the target object.
+ * @param timeout The timeout duration in milliseconds.
+ *
+ * @return true if the target object exists.
+ */
+ @JvmOverloads
+ fun isTargetObjVisible(
+ packageName: String,
+ identifier: String,
+ timeout: Long = WAIT_TIME_MS
+ ): Boolean {
+ uiDevice.waitForIdle(timeout)
+ return uiDevice.hasObject(By.res(packageName, identifier))
+ }
+
+ companion object {
+ private const val GAME_APP_VIEW_RES = "container"
+ private const val WAIT_TIME_MS = 3_000L
+ private const val SWIPE_STEPS = 30
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
new file mode 100644
index 0000000..807e672
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+class MailAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.MAIL_ACTIVITY_LAUNCHER_NAME,
+ component: ComponentNameMatcher =
+ ActivityOptions.MAIL_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ fun openMail(rowIdx: Int) {
+ val rowSel = By.res(getPackage(), "mail_row_item_text")
+ .textEndsWith(String.format("%04d", rowIdx))
+ var row: UiObject2? = null
+ for (i in 1..1000) {
+ row = uiDevice.wait(Until.findObject(rowSel), SHORT_WAIT_TIME_MS)
+ if (row != null) break
+ scrollDown()
+ }
+ require(row != null) {""}
+ row.click()
+ uiDevice.wait(Until.gone(By.res(getPackage(), MAIL_LIST_RES_ID)), FIND_TIMEOUT)
+ }
+
+ fun scrollDown() {
+ val listView = waitForMailList()
+ listView.scroll(Direction.DOWN, 1.0f)
+ }
+
+ fun waitForMailList(): UiObject2 {
+ var sel = By.res(getPackage(), MAIL_LIST_RES_ID).scrollable(true)
+ val ret = uiDevice.wait(Until.findObject(sel), FIND_TIMEOUT)
+ require(ret != null) {""}
+ return ret
+ }
+
+ companion object {
+ private const val SHORT_WAIT_TIME_MS = 5000L
+ private const val MAIL_LIST_RES_ID = "mail_recycle_view"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 354964d..9b1c541 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -62,19 +62,23 @@
get() = {
super.transition(this)
setup {
- tapl.setExpectedRotation(Surface.ROTATION_0)
+ if (testSpec.isTablet) {
+ tapl.setExpectedRotation(testSpec.startRotation)
+ } else {
+ tapl.setExpectedRotation(Surface.ROTATION_0)
+ }
RemoveAllTasksButHomeRule.removeAllTasksButHome()
this.setRotation(testSpec.startRotation)
}
- teardown {
- testApp.exit(wmHelper)
- }
transitions {
tapl.goHome()
.switchToAllApps()
.getAppIcon(testApp.launcherName)
.launch(testApp.`package`)
}
+ teardown {
+ testApp.exit(wmHelper)
+ }
}
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 72a02f2..cae3df4 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -101,6 +101,10 @@
FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
+ public static final String MAIL_ACTIVITY_LAUNCHER_NAME = "MailActivity";
+ public static final ComponentName MAIL_ACTIVITY_COMPONENT_NAME = new ComponentName(
+ FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".MailActivity");
+
public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/TouchLatency/OWNERS b/tests/TouchLatency/OWNERS
new file mode 100644
index 0000000..2b7de25
--- /dev/null
+++ b/tests/TouchLatency/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
\ No newline at end of file