Merge "Connect shortcuts to transitionInteractor and clean up KeyguardPreviewRenderer" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 158d914..e6c94d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -32,6 +32,7 @@
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -41,6 +42,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -81,6 +83,7 @@
import android.os.UserHandle;
import android.os.WearModeManagerInternal;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
@@ -109,6 +112,7 @@
import com.android.server.deviceidle.IDeviceIdleConstraint;
import com.android.server.deviceidle.TvConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
@@ -1020,7 +1024,8 @@
* global Settings. Any access to this class or its fields should be done while
* holding the DeviceIdleController lock.
*/
- public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
+ public final class Constants extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
@@ -1396,6 +1401,7 @@
/**
* 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 = mDefaultNotificationAllowlistDurationMs;
@@ -1413,9 +1419,14 @@
*/
public boolean USE_MODE_MANAGER = mDefaultUseModeManager;
+ private final ContentResolver mResolver;
private final boolean mSmallBatteryDevice;
+ private final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
- public Constants() {
+ public Constants(Handler handler, ContentResolver resolver) {
+ super(handler);
+ mResolver = resolver;
initDefault();
mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
if (mSmallBatteryDevice) {
@@ -1424,8 +1435,14 @@
}
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DEVICE_IDLE,
AppSchedulingModuleThread.getExecutor(), this);
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
+ false, this);
// Load all the constants.
- onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateSettingsConstantLocked();
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateConstantsLocked();
}
private void initDefault() {
@@ -1574,188 +1591,166 @@
return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
}
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (DeviceIdleController.this) {
+ updateSettingsConstantLocked();
+ updateConstantsLocked();
+ }
+ }
+
+ private void updateSettingsConstantLocked() {
+ try {
+ mUserSettingDeviceConfigMediator.setSettingsString(
+ Settings.Global.getString(mResolver,
+ Settings.Global.DEVICE_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on with previous values.
+ Slog.e(TAG, "Bad device idle settings", e);
+ }
+ }
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
synchronized (DeviceIdleController.this) {
- for (String name : properties.getKeyset()) {
- if (name == null) {
- continue;
- }
- switch (name) {
- case KEY_FLEX_TIME_SHORT:
- FLEX_TIME_SHORT = properties.getLong(
- 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,
- mDefaultLightIdleAfterInactiveTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT:
- LIGHT_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
- LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
- mDefaultLightIdleTimeoutInitialFlex);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX:
- LIGHT_IDLE_TIMEOUT_MAX_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
- mDefaultLightIdleTimeoutMaxFlex);
- break;
- case KEY_LIGHT_IDLE_FACTOR:
- LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
- break;
- case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
- LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
- KEY_LIGHT_IDLE_INCREASE_LINEARLY,
- mDefaultLightIdleIncreaseLinearly);
- break;
- case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleFlexLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_MAX_IDLE_TIMEOUT:
- LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
- 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,
- mDefaultLightIdleMaintenanceMinBudget);
- break;
- case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
- LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
- KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
- mDefaultLightIdleMaintenanceMaxBudget);
- break;
- case KEY_MIN_LIGHT_MAINTENANCE_TIME:
- MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_LIGHT_MAINTENANCE_TIME,
- mDefaultMinLightMaintenanceTime);
- break;
- case KEY_MIN_DEEP_MAINTENANCE_TIME:
- MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_DEEP_MAINTENANCE_TIME,
- mDefaultMinDeepMaintenanceTime);
- break;
- case KEY_INACTIVE_TIMEOUT:
- final long defaultInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
- : mDefaultInactiveTimeout;
- INACTIVE_TIMEOUT = properties.getLong(
- KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
- break;
- case KEY_SENSING_TIMEOUT:
- SENSING_TIMEOUT = properties.getLong(
- KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
- break;
- case KEY_LOCATING_TIMEOUT:
- LOCATING_TIMEOUT = properties.getLong(
- KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
- break;
- case KEY_LOCATION_ACCURACY:
- LOCATION_ACCURACY = properties.getFloat(
- KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT:
- MOTION_INACTIVE_TIMEOUT = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
- MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
- mDefaultMotionInactiveTimeoutFlex);
- break;
- case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
- final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
- : 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, mDefaultIdlePendingTimeout);
- break;
- case KEY_MAX_IDLE_PENDING_TIMEOUT:
- MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
- break;
- case KEY_IDLE_PENDING_FACTOR:
- IDLE_PENDING_FACTOR = properties.getFloat(
- KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
- break;
- case KEY_QUICK_DOZE_DELAY_TIMEOUT:
- QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
- KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
- break;
- case KEY_IDLE_TIMEOUT:
- IDLE_TIMEOUT = properties.getLong(
- KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
- break;
- case KEY_MAX_IDLE_TIMEOUT:
- MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
- break;
- case KEY_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, 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,
- 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,
- 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,
- mDefaultSmsTempAppAllowlistDurationMs);
- break;
- case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
- NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
- mDefaultNotificationAllowlistDurationMs);
- break;
- case KEY_WAIT_FOR_UNLOCK:
- WAIT_FOR_UNLOCK = properties.getBoolean(
- KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
- break;
- case KEY_USE_WINDOW_ALARMS:
- USE_WINDOW_ALARMS = properties.getBoolean(
- KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
- break;
- case KEY_USE_MODE_MANAGER:
- USE_MODE_MANAGER = properties.getBoolean(
- KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
- break;
- default:
- Slog.e(TAG, "Unknown configuration key: " + name);
- break;
- }
- }
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
+ updateConstantsLocked();
}
}
+ private void updateConstantsLocked() {
+ if (mSmallBatteryDevice) return;
+ FLEX_TIME_SHORT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
+
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ mDefaultLightIdleAfterInactiveTimeout);
+
+ LIGHT_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
+
+ LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+ mDefaultLightIdleTimeoutInitialFlex);
+
+ LIGHT_IDLE_TIMEOUT_MAX_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+ mDefaultLightIdleTimeoutMaxFlex);
+
+ LIGHT_IDLE_FACTOR = Math.max(1, mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
+
+ LIGHT_IDLE_INCREASE_LINEARLY = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+ mDefaultLightIdleIncreaseLinearly);
+
+ LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleLinearIncreaseFactorMs);
+
+ LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleFlexLinearIncreaseFactorMs);
+
+ LIGHT_MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
+
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
+ mDefaultLightIdleMaintenanceMinBudget);
+
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+ mDefaultLightIdleMaintenanceMaxBudget);
+
+ MIN_LIGHT_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_LIGHT_MAINTENANCE_TIME,
+ mDefaultMinLightMaintenanceTime);
+
+ MIN_DEEP_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_DEEP_MAINTENANCE_TIME,
+ mDefaultMinDeepMaintenanceTime);
+
+ final long defaultInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultInactiveTimeout;
+ INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
+
+ SENSING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
+
+ LOCATING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
+
+ LOCATION_ACCURACY = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
+
+ MOTION_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
+
+ MOTION_INACTIVE_TIMEOUT_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+ mDefaultMotionInactiveTimeoutFlex);
+
+ final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultIdleAfterInactiveTimeout;
+ IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+ defaultIdleAfterInactiveTimeout);
+
+ IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
+
+ MAX_IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
+
+ IDLE_PENDING_FACTOR = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
+
+ QUICK_DOZE_DELAY_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
+
+ IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
+
+ MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
+
+ IDLE_FACTOR = mUserSettingDeviceConfigMediator.getFloat(KEY_IDLE_FACTOR,
+ mDefaultIdleFactor);
+
+ MIN_TIME_TO_ALARM = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
+
+ MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMaxTempAppAllowlistDurationMs);
+
+ MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMmsTempAppAllowlistDurationMs);
+
+ SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultSmsTempAppAllowlistDurationMs);
+
+ NOTIFICATION_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
+ mDefaultNotificationAllowlistDurationMs);
+
+ WAIT_FOR_UNLOCK = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
+
+ USE_WINDOW_ALARMS = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
+
+ USE_MODE_MANAGER = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
+ }
+
void dump(PrintWriter pw) {
pw.println(" Settings:");
@@ -2490,9 +2485,10 @@
return mConnectivityManager;
}
- Constants getConstants(DeviceIdleController controller) {
+ Constants getConstants(DeviceIdleController controller, Handler handler,
+ ContentResolver resolver) {
if (mConstants == null) {
- mConstants = controller.new Constants();
+ mConstants = controller.new Constants(handler, resolver);
}
return mConstants;
}
@@ -2650,7 +2646,7 @@
}
}
- mConstants = mInjector.getConstants(this);
+ mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
readConfigFileLocked();
updateWhitelistAppIdsLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d2150b8..8381d1a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -109,7 +109,6 @@
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -154,7 +153,6 @@
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -241,35 +239,36 @@
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
- final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES);
@@ -279,49 +278,49 @@
// run out of credits, and not when the system has run out of stock.
mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES),
exactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_EXACT,
new Action(ACTION_ALARM_WAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
final long inexactAllowWhileIdleWakeupBasePrice =
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES),
inexactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
new Action(ACTION_ALARM_WAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES),
exactAllowWhileIdleNonWakeupBasePrice,
@@ -329,18 +328,18 @@
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
new Action(ACTION_ALARM_NONWAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
- final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
@@ -350,72 +349,72 @@
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES)));
mActions.put(ACTION_ALARM_CLOCK,
new Action(ACTION_ALARM_CLOCK,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
}
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 a4043dd..61096b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -33,9 +33,9 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -197,10 +197,17 @@
}
protected final InternalResourceService mIrs;
+ protected final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator;
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
mIrs = irs;
+ // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
+ // config can cause issues since the scales may be different, so use one or the other.
+ // If user settings exist, then just stick with the Settings constants, even if there
+ // are invalid values.
+ mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
for (int mId : getCostModifiers()) {
initModifier(mId, irs);
}
@@ -464,28 +471,14 @@
return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) {
- return getConstantAsCake(parser, properties, key, defaultValCake, 0);
+ protected long getConstantAsCake(String key, long defaultValCake) {
+ return getConstantAsCake(key, defaultValCake, 0);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake,
- long minValCake) {
- // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
- // config can cause issues since the scales may be different, so use one or the other.
- if (parser.size() > 0) {
- // User settings take precedence. Just stick with the Settings constants, even if there
- // are invalid values. It's not worth the time to evaluate all the key/value pairs to
- // make sure there are valid ones before deciding.
- return Math.max(minValCake,
- parseCreditValue(parser.getString(key, null), defaultValCake));
- }
- if (properties != null) {
- return Math.max(minValCake,
- parseCreditValue(properties.getString(key, null), defaultValCake));
- }
- return Math.max(minValCake, defaultValCake);
+ protected long getConstantAsCake(String key, long defaultValCake, long minValCake) {
+ return Math.max(minValCake,
+ parseCreditValue(
+ mUserSettingDeviceConfigMediator.getString(key, null), defaultValCake));
}
@VisibleForTesting
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 91a291f..69e5736 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -127,7 +127,6 @@
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -168,7 +167,6 @@
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -274,176 +272,177 @@
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_CTP,
DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_CTP,
DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_CTP,
DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES)));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
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,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_INSTANT,
DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_ONGOING,
DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_MAX,
DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9d13d8a..805ecd4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9342,6 +9342,7 @@
method public String getClassName();
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
+ method @FlaggedApi("android.app.usage.user_interaction_type_api") @NonNull public android.os.PersistableBundle getExtras();
method public String getPackageName();
method public String getShortcutId();
method public long getTimeStamp();
@@ -9407,6 +9408,8 @@
method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_ACTION = "android.app.usage.extra.EVENT_ACTION";
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_CATEGORY = "android.app.usage.extra.EVENT_CATEGORY";
field public static final int INTERVAL_BEST = 4; // 0x4
field public static final int INTERVAL_DAILY = 0; // 0x0
field public static final int INTERVAL_MONTHLY = 2; // 0x2
@@ -9851,7 +9854,7 @@
method @NonNull public android.content.AttributionSource build();
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
- method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
method @NonNull public android.content.AttributionSource.Builder setPid(int);
@@ -10598,6 +10601,7 @@
field public static final String RESTRICTIONS_SERVICE = "restrictions";
field public static final String ROLE_SERVICE = "role";
field public static final String SEARCH_SERVICE = "search";
+ field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
field public static final String SENSOR_SERVICE = "sensor";
field public static final String SHORTCUT_SERVICE = "shortcut";
field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -10610,6 +10614,7 @@
field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
+ field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String TV_AD_SERVICE = "tv_ad";
field public static final String TV_INPUT_SERVICE = "tv_input";
field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
field public static final String UI_MODE_SERVICE = "uimode";
@@ -11098,6 +11103,7 @@
field public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
field @Deprecated public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
field @Deprecated public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+ field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field @Deprecated public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
field public static final String ACTION_USER_BACKGROUND = "android.intent.action.USER_BACKGROUND";
field public static final String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
@@ -12363,6 +12369,7 @@
public class PackageInfo implements android.os.Parcelable {
ctor public PackageInfo();
method public int describeContents();
+ method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
method public long getLongVersionCode();
method public void setLongVersionCode(long);
method public void writeToParcel(android.os.Parcel, int);
@@ -12419,6 +12426,9 @@
method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull String, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, int, @NonNull android.content.IntentSender);
@@ -12440,6 +12450,10 @@
field public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
field public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
field public static final int PACKAGE_SOURCE_DOWNLOADED_FILE = 4; // 0x4
field public static final int PACKAGE_SOURCE_LOCAL_FILE = 3; // 0x3
field public static final int PACKAGE_SOURCE_OTHER = 1; // 0x1
@@ -12455,6 +12469,13 @@
field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
field public static final int STATUS_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
}
public static final class PackageInstaller.InstallConstraints implements android.os.Parcelable {
@@ -12618,6 +12639,7 @@
method @RequiresPermission(android.Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) public void setRequestUpdateOwnership(boolean);
method public void setRequireUserAction(int);
method public void setSize(long);
+ method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR;
@@ -12647,6 +12669,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public int banner;
field public int icon;
+ field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
field public int labelRes;
field public int logo;
field public android.os.Bundle metaData;
@@ -12769,6 +12792,7 @@
method public boolean hasSigningCertificate(int, @NonNull byte[], int);
method public abstract boolean hasSystemFeature(@NonNull String);
method public abstract boolean hasSystemFeature(@NonNull String, int);
+ method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean isAutoRevokeWhitelisted(@NonNull String);
method public boolean isAutoRevokeWhitelisted();
method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable);
@@ -13010,6 +13034,7 @@
field public static final int INSTALL_SCENARIO_FAST = 1; // 0x1
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_APEX = 1073741824; // 0x40000000
+ field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -22031,6 +22056,19 @@
method public void onJetUserIdUpdate(android.media.JetPlayer, int, int);
}
+ @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void removeMediaCodec(@NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void setAudioTrack(@Nullable android.media.AudioTrack);
+ }
+
+ @FlaggedApi("android.media.audio.loudness_configurator_api") public static interface LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener {
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public default android.os.Bundle onLoudnessCodecUpdate(@NonNull android.media.MediaCodec, @NonNull android.os.Bundle);
+ }
+
public class MediaActionSound {
ctor public MediaActionSound();
method public void load(int);
@@ -27299,6 +27337,13 @@
}
+package android.media.tv.ad {
+
+ @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdManager {
+ }
+
+}
+
package android.media.tv.interactive {
public final class AppLinkInfo implements android.os.Parcelable {
@@ -32837,7 +32882,7 @@
method public static android.os.Message obtain(android.os.Handler, int, Object);
method public static android.os.Message obtain(android.os.Handler, int, int, int);
method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
- method public android.os.Bundle peekData();
+ method @Nullable public android.os.Bundle peekData();
method public void recycle();
method public void sendToTarget();
method public void setAsynchronous(boolean);
@@ -33376,6 +33421,13 @@
field @NonNull public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ @FlaggedApi("android.os.security_state_service") public class SecurityStateManager {
+ method @FlaggedApi("android.os.security_state_service") @NonNull public android.os.Bundle getGlobalSecurityState();
+ field public static final String KEY_KERNEL_VERSION = "kernel_version";
+ field public static final String KEY_SYSTEM_SPL = "system_spl";
+ field public static final String KEY_VENDOR_SPL = "vendor_spl";
+ }
+
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
method public void close();
method @NonNull public static android.os.SharedMemory create(@Nullable String, int) throws android.system.ErrnoException;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ce5752f..e2b2879 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -258,6 +258,7 @@
field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
+ field @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis") public static final String PREPARE_FACTORY_RESET = "android.permission.PREPARE_FACTORY_RESET";
field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
@@ -3613,7 +3614,6 @@
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
- field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -3861,16 +3861,9 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
- public class PackageInfo implements android.os.Parcelable {
- method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
- }
-
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3881,23 +3874,12 @@
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
}
public static class PackageInstaller.InstallInfo {
@@ -3947,14 +3929,12 @@
method public void setRequestDowngrade(boolean);
method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
- method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
}
public class PackageItemInfo {
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
- field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
}
public abstract class PackageManager {
@@ -3986,7 +3966,6 @@
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
@@ -4104,7 +4083,6 @@
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4f8e8dd..014ddd41 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3484,9 +3484,20 @@
// only do this if the user already has more than one preferred locale
if (android.content.res.Flags.defaultLocale()
&& r.getConfiguration().getLocales().size() > 1) {
- LocaleConfig lc = getUserId() < 0
- ? LocaleConfig.fromContextIgnoringOverride(this)
- : new LocaleConfig(this);
+ LocaleConfig lc;
+ if (getUserId() < 0) {
+ lc = LocaleConfig.fromContextIgnoringOverride(this);
+ } else {
+ // This is needed because the app might have locale config overrides that need to
+ // be read from disk in order for resources to correctly choose which values to
+ // load.
+ StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+ try {
+ lc = new LocaleConfig(this);
+ } finally {
+ StrictMode.setThreadPolicy(policy);
+ }
+ }
mResourcesManager.setLocaleConfig(lc);
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 6009c29..24a5157 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -159,7 +159,8 @@
* Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
* instance is alive and reachable.
*/
- private class ApkAssetsSupplier {
+ @VisibleForTesting
+ protected class ApkAssetsSupplier {
final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
/**
@@ -544,7 +545,10 @@
* from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
* {@link #loadApkAssets(ApkKey)}.
*/
- private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+
+ @VisibleForTesting
+ @UnsupportedAppUsage
+ protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager.Builder builder = new AssetManager.Builder();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 79a5879..9cf732a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -137,6 +137,8 @@
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
+import android.media.tv.ad.ITvAdManager;
+import android.media.tv.ad.TvAdManager;
import android.media.tv.interactive.ITvInteractiveAppManager;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
@@ -174,6 +176,7 @@
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
+import android.os.ISecurityStateManager;
import android.os.ISystemUpdateManager;
import android.os.IThermalService;
import android.os.IUserManager;
@@ -182,6 +185,7 @@
import android.os.PermissionEnforcer;
import android.os.PowerManager;
import android.os.RecoverySystem;
+import android.os.SecurityStateManager;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -628,6 +632,17 @@
ctx.mMainThread.getHandler());
}});
+ registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,
+ new CachedServiceFetcher<SecurityStateManager>() {
+ @Override
+ public SecurityStateManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.SECURITY_STATE_SERVICE);
+ ISecurityStateManager service = ISecurityStateManager.Stub.asInterface(b);
+ return new SecurityStateManager(service);
+ }});
+
registerService(Context.SENSOR_SERVICE, SensorManager.class,
new CachedServiceFetcher<SensorManager>() {
@Override
@@ -960,6 +975,18 @@
return new TvInteractiveAppManager(service, ctx.getUserId());
}});
+ registerService(Context.TV_AD_SERVICE, TvAdManager.class,
+ new CachedServiceFetcher<TvAdManager>() {
+ @Override
+ public TvAdManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder =
+ ServiceManager.getServiceOrThrow(Context.TV_AD_SERVICE);
+ ITvAdManager service =
+ ITvAdManager.Stub.asInterface(iBinder);
+ return new TvAdManager(service, ctx.getUserId());
+ }});
+
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
new CachedServiceFetcher<TvInputManager>() {
@Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6a03c17..ce1d43d 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -180,6 +180,11 @@
@Override
public void injectInputEventToInputFilter(InputEvent event) throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
mAccessibilityManager.injectInputEventToInputFilter(event);
}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index ebd5d64..cf19178 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -22,6 +22,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageEventsQuery;
import android.content.pm.ParceledListSlice;
+import android.os.PersistableBundle;
/**
* System private API for talking with the UsageStatsManagerService.
@@ -77,6 +78,8 @@
String callingPackage);
void reportUsageStop(in IBinder activity, String token, String callingPackage);
void reportUserInteraction(String packageName, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)")
+ void reportUserInteractionWithBundle(String packageName, int userId, in PersistableBundle eventExtras);
int getUsageSource();
void forceUsageSourceSettingRead();
long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
index aa32392..7bc6cb9 100644
--- a/core/java/android/app/usage/ParcelableUsageEventList.java
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -223,6 +223,7 @@
event.mContentAnnotations = null;
event.mNotificationChannelId = null;
event.mLocusId = null;
+ event.mExtras = null;
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE -> {
@@ -237,6 +238,11 @@
case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
+ case Event.USER_INTERACTION -> {
+ if (in.readInt() != 0) {
+ event.mExtras = in.readPersistableBundle(getClass().getClassLoader());
+ }
+ }
}
event.mFlags = in.readInt();
@@ -263,6 +269,14 @@
case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
+ case Event.USER_INTERACTION -> {
+ if (event.mExtras != null) {
+ dest.writeInt(1);
+ dest.writePersistableBundle(event.mExtras);
+ } else {
+ dest.writeInt(0);
+ }
+ }
}
dest.writeInt(event.mFlags);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6c7eba0..9eb73b3 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,7 +16,9 @@
package android.app.usage;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +26,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -551,6 +554,22 @@
public int mLocusIdToken = UNASSIGNED_TOKEN;
/** @hide */
+ public PersistableBundle mExtras = null;
+
+ /** @hide */
+ public static class UserInteractionEventExtrasToken {
+ public int mCategoryToken = UNASSIGNED_TOKEN;
+ public int mActionToken = UNASSIGNED_TOKEN;
+
+ public UserInteractionEventExtrasToken() {
+ // Do nothing.
+ }
+ }
+
+ /** @hide */
+ public UserInteractionEventExtrasToken mUserInteractionExtrasToken = null;
+
+ /** @hide */
@EventFlags
public int mFlags;
@@ -650,6 +669,21 @@
}
/**
+ * Retrieves a map of extended data from the event if the event is of type
+ * {@link #USER_INTERACTION}.
+ *
+ * @return the map of all extras that associated with the reported user interaction
+ * event. The returned {@link PersistableBundle} will contain the extras
+ * {@link UsageStatsManager#EXTRA_EVENT_CATEGORY} and
+ * {@link UsageStatsManager#EXTRA_EVENT_ACTION}. {@link PersistableBundle#EMPTY}
+ * will be returned if the details are not available.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public @NonNull PersistableBundle getExtras() {
+ return mExtras == null ? PersistableBundle.EMPTY : mExtras;
+ }
+
+ /**
* Returns a {@link Configuration} for this event if the event is of type
* {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
*/
@@ -747,6 +781,7 @@
mBucketAndReason = orig.mBucketAndReason;
mNotificationChannelId = orig.mNotificationChannelId;
mLocusId = orig.mLocusId;
+ mExtras = orig.mExtras;
}
}
@@ -802,6 +837,7 @@
if (mEventCount != mEventsToWrite.size()) {
Log.w(TAG, "Partial usage event list received: " + mEventCount + " != "
+ mEventsToWrite.size());
+ mEventCount = mEventsToWrite.size();
}
}
@@ -987,6 +1023,14 @@
case Event.LOCUS_ID_SET:
p.writeString(event.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ if (event.mExtras != null) {
+ p.writeInt(1);
+ p.writePersistableBundle(event.mExtras);
+ } else {
+ p.writeInt(0);
+ }
+ break;
}
p.writeInt(event.mFlags);
}
@@ -1036,6 +1080,7 @@
eventOut.mContentAnnotations = null;
eventOut.mNotificationChannelId = null;
eventOut.mLocusId = null;
+ eventOut.mExtras = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -1059,6 +1104,11 @@
case Event.LOCUS_ID_SET:
eventOut.mLocusId = p.readString();
break;
+ case Event.USER_INTERACTION:
+ if (p.readInt() != 0) {
+ eventOut.mExtras = p.readPersistableBundle(getClass().getClassLoader());
+ }
+ break;
}
eventOut.mFlags = p.readInt();
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4f1c993..85d223d 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -28,6 +28,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -35,6 +36,7 @@
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Build;
+import android.os.PersistableBundle;
import android.os.PowerWhitelistManager;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -392,6 +394,23 @@
@SystemApi
public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the category of the user interaction associated with the event. The category cannot
+ * be more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_CATEGORY =
+ "android.app.usage.extra.EVENT_CATEGORY";
+
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the action of the user interaction associated with the event. The action cannot be
+ * more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_ACTION =
+ "android.app.usage.extra.EVENT_ACTION";
/**
* App usage observers will consider the task root package the source of usage.
@@ -562,10 +581,10 @@
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents}.
*/
public UsageEvents queryEvents(long beginTime, long endTime) {
@@ -611,10 +630,10 @@
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents} object.
*
* @see #queryEvents(long, long)
@@ -1128,6 +1147,7 @@
* Reports user interaction with a given package in the given user.
*
* <p><em>This method is only for use by the system</em>
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
@@ -1140,6 +1160,38 @@
}
/**
+ * Reports user interaction with given package and a particular {@code extras}
+ * in the given user.
+ *
+ * <p>
+ * Note: The structure of {@code extras} is a {@link PersistableBundle} with the
+ * category {@link #EXTRA_EVENT_CATEGORY} and the action {@link #EXTRA_EVENT_ACTION}.
+ * Category provides additional detail about the user interaction, the value
+ * is defined in namespace based. Example: android.app.notification could be used to
+ * indicate that the reported user interaction is related to notification. Action
+ * indicates the general action that performed.
+ * </p>
+ *
+ * @param packageName The package name of the app
+ * @param userId The user id who triggers the user interaction
+ * @param extras The {@link PersistableBundle} that will be used to specify the
+ * extra details for the user interaction event. The {@link PersistableBundle}
+ * must contain the extras {@link #EXTRA_EVENT_CATEGORY},
+ * {@link #EXTRA_EVENT_ACTION}. Cannot be empty.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
+ public void reportUserInteraction(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ try {
+ mService.reportUserInteractionWithBundle(packageName, userId, extras);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report usage associated with a particular {@code token} has started. Tokens are app defined
* strings used to represent usage of in-app features. Apps with the {@link
* android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index c6012bb..6e45147 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -52,6 +52,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FunctionalUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -557,11 +558,45 @@
}
}).toArray(ComponentName[]::new));
} catch (Exception e) {
- Log.e(TAG, "Nofity service of inheritance info", e);
+ Log.e(TAG, "Notify service of inheritance info", e);
}
});
}
+ private void tryAdapterConversion(
+ FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
+ RemoteViews original, String failureMsg) {
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (original != null && original.hasLegacyLists()));
+
+ if (isConvertingAdapter) {
+ final RemoteViews viewsCopy = new RemoteViews(original);
+ Runnable updateWidgetWithTask = () -> {
+ try {
+ viewsCopy.collectAllIntents().get();
+ action.acceptOrThrow(viewsCopy);
+ } catch (Exception e) {
+ Log.e(TAG, failureMsg, e);
+ }
+ };
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ createUpdateExecutorIfNull().execute(updateWidgetWithTask);
+ return;
+ }
+
+ updateWidgetWithTask.run();
+ return;
+ }
+
+ try {
+ action.acceptOrThrow(original);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -586,32 +621,8 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds,
+ view), views, "Error updating app widget views in background");
}
/**
@@ -716,32 +727,9 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error partially updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName,
+ appWidgetIds, view), views,
+ "Error partially updating app widget views in background");
}
/**
@@ -793,33 +781,8 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetProvider(provider, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget view using provider in background",
- e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetProvider(provider, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views,
+ "Error updating app widget view using provider in background");
}
/**
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 102cbf3..3520c0b 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -246,4 +246,10 @@
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterVirtualCamera(in VirtualCameraConfig camera);
+
+ /**
+ * Returns the id of the virtual camera with given config.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ int getVirtualCameraId(in VirtualCameraConfig camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index da8277c..9492a62 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.virtual.audio.VirtualAudioDevice;
@@ -340,12 +341,17 @@
return mVirtualAudioDevice;
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
- return new VirtualCamera(mVirtualDevice, config);
+ try {
+ mVirtualDevice.registerVirtualCamera(config);
+ return new VirtualCamera(mVirtualDevice, config);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
- @NonNull
void setShowPointerIcon(boolean showPointerIcon) {
try {
mVirtualDevice.setShowPointerIcon(showPointerIcon);
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index beee86f..52afa4e 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -66,15 +66,8 @@
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public VirtualCamera(
@NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
- mVirtualDevice = virtualDevice;
+ mVirtualDevice = Objects.requireNonNull(virtualDevice);
mConfig = Objects.requireNonNull(config);
- Objects.requireNonNull(virtualDevice);
- // TODO(b/310857519): Avoid registration inside constructor.
- try {
- mVirtualDevice.registerVirtualCamera(config);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
}
/** Returns the configuration of this virtual camera instance. */
@@ -83,6 +76,20 @@
return mConfig;
}
+ /**
+ * Returns the id of this virtual camera instance.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public String getId() {
+ try {
+ return Integer.toString(mVirtualDevice.getVirtualCameraId(mConfig));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void close() {
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 4b2cee6..697c25c 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -730,10 +730,7 @@
/**
* The next app to receive the permission protected data.
- *
- * @deprecated Use {@link #setNextAttributionSource} instead.
*/
- @Deprecated
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c6c7b5..b75c64d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -72,6 +72,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Flags;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -4214,6 +4215,7 @@
DEVICE_LOCK_SERVICE,
VIRTUALIZATION_SERVICE,
GRAMMATICAL_INFLECTION_SERVICE,
+ SECURITY_STATE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
@@ -5818,6 +5820,17 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.tv.ad.TvAdManager} for interacting with TV client-side advertisement
+ * services on the device.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.tv.ad.TvAdManager
+ */
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public static final String TV_AD_SERVICE = "tv_ad";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.tv.TunerResourceManager} for interacting with TV
* tuner resources on the device.
*
@@ -6478,6 +6491,16 @@
public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.SecurityStateManager} for accessing the security state manager service.
+ *
+ * @see #getSystemService(String)
+ * @see android.os.SecurityStateManager
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ public static final String SECURITY_STATE_SERVICE = "security_state";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c7a86fb..38bcfa2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5353,11 +5353,11 @@
* Broadcast Action: Sent to the responsible installer of an archived package when unarchival
* is requested.
*
- * @see android.content.pm.PackageInstaller#requestUnarchive(String)
- * @hide
+ * @see android.content.pm.PackageInstaller#requestUnarchive
*/
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
+ @BroadcastBehavior(explicitOnly = true)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1cfdb8b..5736a6d 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -20,7 +20,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -522,9 +521,7 @@
/**
* Returns the time at which the app was archived for the user. Units are as
* per {@link System#currentTimeMillis()}.
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public @CurrentTimeMillisLong long getArchiveTimeMillis() {
return mArchiveTimeMillis;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6df1f60..d35c392 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -354,10 +354,7 @@
/**
* Extra field for the package name of a package that is requested to be unarchived. Sent as
* part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
"android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
@@ -366,22 +363,16 @@
* Extra field for the unarchive ID. Sent as
* part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
*
- * @see Session#setUnarchiveId(int)
- *
- * @hide
+ * @see SessionParams#setUnarchiveId
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_ID =
"android.content.pm.extra.UNARCHIVE_ID";
/**
* If true, the requestor of the unarchival has specified that the app should be unarchived
- * for {@link android.os.UserHandle#ALL}.
- *
- * @hide
+ * for all users.
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_ALL_USERS =
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
@@ -398,9 +389,7 @@
* failure dialog.
*
* @see #requestUnarchive
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
@@ -675,10 +664,7 @@
*
* <p> Note that this does not mean that the unarchival has completed. This status should be
* sent before any longer asynchronous action (e.g. app download) is started.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_OK = 0;
@@ -687,10 +673,7 @@
*
* <p> An example use case for this could be that the user needs to login to allow the
* download for a paid app.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1;
@@ -700,19 +683,13 @@
* <p> The installer can optionally provide a {@code userActionIntent} for a space-clearing
* dialog. If no action is provided, then a generic intent
* {@link android.os.storage.StorageManager#ACTION_MANAGE_STORAGE} is started instead.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2;
/**
* The device is not connected to the internet
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3;
@@ -720,10 +697,7 @@
* The installer responsible for the unarchival is disabled.
*
* <p> Should only be used by the system.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4;
@@ -731,19 +705,13 @@
* The installer responsible for the unarchival has been uninstalled
*
* <p> Should only be used by the system.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5;
/**
* Generic error: The app cannot be unarchived.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_GENERIC_ERROR = 100;
@@ -2364,12 +2332,10 @@
* @param statusReceiver Callback used to notify when the operation is completed.
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* available to the caller or isn't archived.
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.REQUEST_DELETE_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
throws PackageManager.NameNotFoundException {
@@ -2395,19 +2361,16 @@
*
* @param statusReceiver Callback used to notify whether the installer has accepted the
* unarchival request or an error has occurred. The status update will be
- * sent though {@link EXTRA_UNARCHIVE_STATUS}. Only one status will be
+ * sent though {@link #EXTRA_UNARCHIVE_STATUS}. Only one status will be
* sent.
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* visible to the caller or if the package has no
* installer on the device anymore to unarchive it.
* @throws IOException If parameters were unsatisfiable, such as lack of disk space.
- *
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.REQUEST_INSTALL_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void requestUnarchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
throws IOException, PackageManager.NameNotFoundException {
@@ -2435,12 +2398,10 @@
* @param userActionIntent Optional intent to start a follow up action required to
* facilitate the unarchival flow (e.g. user needs to log in).
* @throws PackageManager.NameNotFoundException if no unarchival with {@code unarchiveId} exists
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.REQUEST_INSTALL_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void reportUnarchivalStatus(int unarchiveId, @UnarchivalStatus int status,
long requiredStorageBytes, @Nullable PendingIntent userActionIntent)
@@ -3454,11 +3415,8 @@
* <p> The ID should be retrieved from the unarchive intent and passed into the
* session that's being created to unarchive the app in question. Used to link the unarchive
* intent and the install session to disambiguate.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
- @SystemApi
public void setUnarchiveId(int unarchiveId) {
this.unarchiveId = unarchiveId;
}
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index c7091ad..70e6f98 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -177,12 +177,10 @@
/**
* Whether the package is currently in an archived state.
*
- * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
- * on the device, but do keep the data directory.
- * @hide
+ * <p>Packages can be archived through {@link PackageInstaller#requestArchive} and do not have
+ * any APKs stored on the device, but do keep the data directory.
+ *
*/
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public boolean isArchived;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6775f9b..a22fe3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1263,18 +1263,14 @@
/**
* Flag parameter to also retrieve some information about archived packages.
- * Packages can be archived through
- * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
- * on the device, but do keep the data directory.
+ * Packages can be archived through {@link PackageInstaller#requestArchive} and do not have any
+ * APKs stored on the device, but do keep the data directory.
* <p> Note: Archived apps are a subset of apps returned by {@link #MATCH_UNINSTALLED_PACKAGES}.
* <p> Note: this flag may cause less information about currently installed
* applications to be returned.
* <p> Note: use of this flag requires the android.permission.QUERY_ALL_PACKAGES
* permission to see uninstalled packages.
- * @hide
*/
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
@@ -8969,10 +8965,7 @@
*
* @throws NameNotFoundException if the given package name is not available to the caller.
* @see PackageInstaller#requestArchive(String, IntentSender)
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isAppArchivable not implemented");
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index a565f68..d21b818 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -94,3 +94,10 @@
description: "Feature flag to read install related information from an APK."
bug: "275658500"
}
+
+flag {
+ name: "use_pia_v2"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the refactored Package Installer app with updated UI."
+ bug: "182205982"
+}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 1b8eb07..40592a1 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -15,3 +15,12 @@
description: "Feature flag for passing in an AssetFileDescriptor to create an frro"
bug: "304478666"
}
+
+flag {
+ name: "manifest_flagging"
+ namespace: "resource_manager"
+ description: "Feature flag for flagging manifest entries"
+ bug: "297373084"
+ # This flag is read in PackageParser at boot time, and in aapt2 which is a build tool.
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index d786d9a..18c95bfb 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -66,6 +66,13 @@
+ ".extra.sensor";
/**
+ * An extra containing the notification id that triggered the intent
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_ID = SensorPrivacyManager.class.getName()
+ + ".extra.notification_id";
+
+ /**
* An extra indicating if all sensors are affected
* @hide
*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index dfc27ca..507e814 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +25,8 @@
import android.hardware.camera2.impl.SyntheticKey;
import android.util.Log;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index d4ce0eb..5cbb0bb 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,8 +16,6 @@
package android.hardware.camera2.params;
-import static com.android.internal.R.string.hardware;
-
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index ed31002..eabe13b 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -123,6 +123,12 @@
private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+ private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+ BatteryConsumer.PROCESS_STATE_FOREGROUND,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+ };
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
@@ -516,6 +522,22 @@
proto.write(
BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
bgMs);
+ for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+ final long timeInStateMillis = consumer.getTimeInProcessStateMs(processState);
+ if (timeInStateMillis <= 0) {
+ continue;
+ }
+ final long timeInStateToken = proto.start(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_STATE);
+ proto.write(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState.PROCESS_STATE,
+ processState);
+ proto.write(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState
+ .TIME_IN_STATE_MILLIS,
+ timeInStateMillis);
+ proto.end(timeInStateToken);
+ }
proto.end(token);
if (proto.getRawSize() >= maxRawSize) {
diff --git a/core/java/android/os/ISecurityStateManager.aidl b/core/java/android/os/ISecurityStateManager.aidl
new file mode 100644
index 0000000..8ae624d
--- /dev/null
+++ b/core/java/android/os/ISecurityStateManager.aidl
@@ -0,0 +1,26 @@
+/* //device/java/android/android/os/ISecurityStateManager.aidl
+**
+** Copyright 2023, 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.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISecurityStateManager {
+ Bundle getGlobalSecurityState();
+}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index da647e2..161951e 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -437,6 +438,7 @@
* @see #getData()
* @see #setData(Bundle)
*/
+ @Nullable
public Bundle peekData() {
return data;
}
diff --git a/core/java/android/os/SecurityStateManager.java b/core/java/android/os/SecurityStateManager.java
new file mode 100644
index 0000000..4fa61e0
--- /dev/null
+++ b/core/java/android/os/SecurityStateManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * SecurityStateManager provides the functionality to query the security status of the system and
+ * platform components. For example, this includes the system and vendor security patch level.
+ */
+@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+@SystemService(Context.SECURITY_STATE_SERVICE)
+public class SecurityStateManager {
+
+ /**
+ * The system SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_SYSTEM_SPL = "system_spl";
+
+ /**
+ * The vendor SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_VENDOR_SPL = "vendor_spl";
+
+ /**
+ * The kernel version key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_KERNEL_VERSION = "kernel_version";
+
+ private final ISecurityStateManager mService;
+
+ /**
+ * @hide
+ */
+ public SecurityStateManager(ISecurityStateManager service) {
+ mService = requireNonNull(service, "missing ISecurityStateManager");
+ }
+
+ /**
+ * Returns the current global security state. Each key-value pair is a mapping of a component
+ * of the global security state to its current version/SPL (security patch level). For example,
+ * the {@code KEY_SYSTEM_SPL} key will map to the SPL of the system as defined in
+ * {@link android.os.Build.VERSION}. The bundle will also include mappings from WebView packages
+ * and packages listed under config {@code config_securityStatePackages} to their respective
+ * versions as defined in {@link android.content.pm.PackageInfo#versionName}.
+ *
+ * @return A {@code Bundle} that contains the global security state information as
+ * string-to-string key-value pairs.
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ @NonNull
+ public Bundle getGlobalSecurityState() {
+ try {
+ return mService.getGlobalSecurityState();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a78f221..c085f33 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -57,6 +57,13 @@
}
flag {
+ name: "security_state_service"
+ namespace: "dynamic_spl"
+ description: "Guards the Security State API."
+ bug: "302189431"
+}
+
+flag {
name: "battery_saver_supported_check_api"
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6853892..78a12f7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1738,23 +1738,6 @@
return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
}
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Deprecated
- public static boolean isFileEncryptedNativeOnly() {
- return isFileEncrypted();
- }
-
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @Deprecated
- public static boolean isFileEncryptedNativeOrEmulated() {
- return isFileEncrypted();
- }
-
/** {@hide} */
public static boolean hasAdoptable() {
switch (SystemProperties.get(PROP_ADOPTABLE)) {
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index dc86e3f5..5cbc18e 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -56,4 +56,11 @@
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
bug: "222650148"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "factory_reset_prep_permission_apis"
+ namespace: "wallet_integration"
+ description: "enable Permission PREPARE_FACTORY_RESET."
+ bug: "302016478"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ff6ec29..8f18c5f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14975,6 +14975,38 @@
public static final String APP_OPS_CONSTANTS = "app_ops_constants";
/**
+ * Device Idle (Doze) specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "inactive_to=60000,sensing_to=400000"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * inactive_to (long)
+ * sensing_to (long)
+ * motion_inactive_to (long)
+ * idle_after_inactive_to (long)
+ * idle_pending_to (long)
+ * max_idle_pending_to (long)
+ * idle_pending_factor (float)
+ * quick_doze_delay_to (long)
+ * idle_to (long)
+ * max_idle_to (long)
+ * idle_factor (float)
+ * min_time_to_alarm (long)
+ * max_temp_app_whitelist_duration (long)
+ * notification_whitelist_duration (long)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.DeviceIdleController.Constants
+ */
+ public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
+
+ /**
* Battery Saver specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0063d13..886727e 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -490,16 +490,32 @@
/**
* Notify changes to activity state changes on certain subscription.
*
+ * @param subId for which data activity state changed.
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
+ * TelephonyManager#DATA_ACTIVITY_IN}
+ */
+ public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
+ try {
+ sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify changes to activity state changes on certain subscription.
+ *
* @param slotIndex for which data activity changed. Can be derived from subId except
* when subId is invalid.
* @param subId for which data activity state changed.
- * @param dataActivityType indicates the latest data activity type e.g, {@link
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
* TelephonyManager#DATA_ACTIVITY_IN}
*/
public void notifyDataActivityChanged(int slotIndex, int subId,
@DataActivityType int dataActivityType) {
try {
- sRegistry.notifyDataActivityForSubscriber(slotIndex, subId, dataActivityType);
+ sRegistry.notifyDataActivityForSubscriberWithSlot(slotIndex, subId, dataActivityType);
} catch (RemoteException ex) {
// system process is dead
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 23d0de3..cbbe785 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -503,9 +503,6 @@
// be dumped as additional context
private static volatile boolean sDebugUsageAfterRelease = false;
- static GlobalTransactionWrapper sGlobalTransaction;
- static long sTransactionNestCount = 0;
-
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(SurfaceControl.class.getClassLoader(),
nativeGetNativeSurfaceControlFinalizer());
@@ -1590,54 +1587,30 @@
return mNativeObject != 0;
}
- /*
- * set surface parameters.
- * needs to be inside open/closeTransaction block
- */
-
/** start a transaction
* @hide
- */
- @UnsupportedAppUsage
- public static void openTransaction() {
- synchronized (SurfaceControl.class) {
- if (sGlobalTransaction == null) {
- sGlobalTransaction = new GlobalTransactionWrapper();
- }
- synchronized(SurfaceControl.class) {
- sTransactionNestCount++;
- }
- }
- }
-
- /**
- * Merge the supplied transaction in to the deprecated "global" transaction.
- * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
- * <p>
- * This is a utility for interop with legacy-code and will go away with the Global Transaction.
- * @hide
+ * @deprecated Use regular Transaction instead.
*/
@Deprecated
- public static void mergeToGlobalTransaction(Transaction t) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.merge(t);
- }
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
+ public static void openTransaction() {
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/** end a transaction
* @hide
+ * @deprecated Use regular Transaction instead.
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
public static void closeTransaction() {
- synchronized(SurfaceControl.class) {
- if (sTransactionNestCount == 0) {
- Log.e(TAG,
- "Call to SurfaceControl.closeTransaction without matching openTransaction");
- } else if (--sTransactionNestCount > 0) {
- return;
- }
- sGlobalTransaction.applyGlobalTransaction(false);
- }
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/**
@@ -4513,39 +4486,6 @@
}
/**
- * As part of eliminating usage of the global Transaction we expose
- * a SurfaceControl.getGlobalTransaction function. However calling
- * apply on this global transaction (rather than using closeTransaction)
- * would be very dangerous. So for the global transaction we use this
- * subclass of Transaction where the normal apply throws an exception.
- */
- private static class GlobalTransactionWrapper extends SurfaceControl.Transaction {
- void applyGlobalTransaction(boolean sync) {
- applyResizedSurfaces();
- notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync, /*oneWay*/ false);
- }
-
- @Override
- public void apply(boolean sync) {
- throw new RuntimeException("Global transaction must be applied from closeTransaction");
- }
- }
-
- /**
- * This is a refactoring utility function to enable lower levels of code to be refactored
- * from using the global transaction (and instead use a passed in Transaction) without
- * having to refactor the higher levels at the same time.
- * The returned global transaction can't be applied, it must be applied from closeTransaction
- * Unless you are working on removing Global Transaction usage in the WindowManager, this
- * probably isn't a good function to use.
- * @hide
- */
- public static Transaction getGlobalTransaction() {
- return sGlobalTransaction;
- }
-
- /**
* @hide
*/
public void resize(int w, int h) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d591f89..d58c07d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -33018,7 +33018,7 @@
}
private float getSizePercentage() {
- if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
+ if (mResources == null || getVisibility() != VISIBLE) {
return 0;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7a6c292..cac5387 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -991,7 +991,7 @@
// for idleness handling.
private boolean mHasIdledMessage = false;
// time for touch boost period.
- private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+ private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
// time for checking idle status periodically.
private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
// time for revaluating the idle status before lowering the frame rate.
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index a11c6d0..a404bd6 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -54,7 +54,7 @@
* @param displayId the logical display id.
* @param scale magnification scale.
*/
- void setScale(int displayId, float scale);
+ void setScaleForWindowMagnification(int displayId, float scale);
/**
* Disables window magnification on specified display with animation.
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index e177731..c6bd20c 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -79,4 +79,9 @@
* Used by Settings to enable/disable multiprocess.
*/
void enableMultiProcess(boolean enable);
+
+ /**
+ * Used by Settings to get the default WebView package.
+ */
+ WebViewProviderInfo getDefaultWebViewPackage();
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a919c00..1acebf4 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1054,8 +1054,7 @@
}
private class SetRemoteCollectionItemListAdapterAction extends Action {
- @NonNull
- private CompletableFuture<RemoteCollectionItems> mItemsFuture;
+ private @Nullable RemoteCollectionItems mItems;
final Intent mServiceIntent;
int mIntentId = -1;
boolean mIsReplacedIntoAction = false;
@@ -1064,92 +1063,46 @@
@NonNull RemoteCollectionItems items) {
mViewId = id;
items.setHierarchyRootData(getHierarchyRootData());
- mItemsFuture = CompletableFuture.completedFuture(items);
+ mItems = items;
mServiceIntent = null;
}
SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
mViewId = id;
- mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
- setHierarchyRootData(getHierarchyRootData());
+ mItems = null;
mServiceIntent = intent;
}
- private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent) {
- if (intent == null) {
- Log.e(LOG_TAG, "Null intent received when generating adapter future");
- return CompletableFuture.completedFuture(new RemoteCollectionItems
- .Builder().build());
- }
-
- final Context context = ActivityThread.currentApplication();
- final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
-
- context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
- result.defaultExecutor(), new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName,
- IBinder iBinder) {
- RemoteCollectionItems items;
- try {
- items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems();
- } catch (RemoteException re) {
- items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the factory",
- re);
- } finally {
- context.unbindService(this);
- }
-
- result.complete(items);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
- });
-
- result.completeOnTimeout(
- new RemoteCollectionItems.Builder().build(),
- MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
-
- return result;
- }
-
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
mIntentId = parcel.readInt();
- mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
- ? null
- : new RemoteCollectionItems(parcel, getHierarchyRootData()));
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
+ mItems = mServiceIntent != null
+ ? null
+ : new RemoteCollectionItems(parcel, getHierarchyRootData());
}
@Override
public void setHierarchyRootData(HierarchyRootData rootData) {
- if (mIntentId == -1) {
- mItemsFuture = mItemsFuture
- .thenApply(rc -> {
- rc.setHierarchyRootData(rootData);
- return rc;
- });
+ if (mItems != null) {
+ mItems.setHierarchyRootData(rootData);
return;
}
- // Set the root data for items in the cache instead
- mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ if (mIntentId != -1) {
+ // Set the root data for items in the cache instead
+ mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ }
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
dest.writeInt(mIntentId);
- if (mIntentId == -1) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.writeToParcel(dest, flags, /* attached= */ true);
- }
dest.writeTypedObject(mServiceIntent, flags);
+ if (mItems != null) {
+ mItems.writeToParcel(dest, flags, /* attached= */ true);
+ }
}
@Override
@@ -1159,7 +1112,9 @@
if (target == null) return;
RemoteCollectionItems items = mIntentId == -1
- ? getCollectionItemsFromFuture(mItemsFuture)
+ ? mItems == null
+ ? new RemoteCollectionItems.Builder().build()
+ : mItems
: mCollectionCache.getItemsForId(mIntentId);
// Ensure that we are applying to an AppWidget root
@@ -1216,51 +1171,32 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.visitUris(visitor);
- }
- }
+ if (mIntentId != -1 || mItems == null) {
+ return;
+ }
- private static RemoteCollectionItems getCollectionItemsFromFuture(
- CompletableFuture<RemoteCollectionItems> itemsFuture) {
- RemoteCollectionItems items;
- try {
- items = itemsFuture.get();
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error getting collection items from future", e);
- items = new RemoteCollectionItems.Builder().build();
+ mItems.visitUris(visitor);
}
-
- return items;
}
/**
* @hide
*/
- public void collectAllIntents() {
- mCollectionCache.collectAllIntentsNoComplete(this);
+ public CompletableFuture<Void> collectAllIntents() {
+ return mCollectionCache.collectAllIntentsNoComplete(this);
}
private class RemoteCollectionCache {
private SparseArray<String> mIdToUriMapping = new SparseArray<>();
private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
- // We don't put this into the parcel
- private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
- new HashMap<>();
-
RemoteCollectionCache() { }
RemoteCollectionCache(RemoteCollectionCache src) {
- boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
String uri = src.mIdToUriMapping.valueAt(i);
mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
- if (isWaitingCache) {
- mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
- } else {
- mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
- }
+ mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
}
}
@@ -1281,14 +1217,8 @@
void setHierarchyDataForId(int intentId, HierarchyRootData data) {
String uri = mIdToUriMapping.get(intentId);
- if (mTempUriToFutureMapping.get(uri) != null) {
- CompletableFuture<RemoteCollectionItems> itemsFuture =
- mTempUriToFutureMapping.get(uri);
- mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
- rc.setHierarchyRootData(data);
- return rc;
- }));
-
+ if (mUriToCollectionMapping.get(uri) == null) {
+ Log.e(LOG_TAG, "Error setting hierarchy data for id=" + intentId);
return;
}
@@ -1301,14 +1231,17 @@
return mUriToCollectionMapping.get(uri);
}
- void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
if (inViews.hasSizedRemoteViews()) {
for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
- remoteViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(remoteViews));
}
} else if (inViews.hasLandscapeAndPortraitLayouts()) {
- inViews.mLandscape.collectAllIntents();
- inViews.mPortrait.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(
+ collectAllIntentsNoComplete(inViews.mLandscape),
+ collectAllIntentsNoComplete(inViews.mPortrait));
} else if (inViews.mActions != null) {
for (Action action : inViews.mActions) {
if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1318,40 +1251,95 @@
}
if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
- String uri = mIdToUriMapping.get(rca.mIntentId);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ final String uri = mIdToUriMapping.get(rca.mIntentId);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
continue;
}
// Differentiate between the normal collection actions and the ones with
// intents.
if (rca.mServiceIntent != null) {
- String uri = rca.mServiceIntent.toUri(0);
+ final String uri = rca.mServiceIntent.toUri(0);
int index = mIdToUriMapping.indexOfValue(uri);
if (index == -1) {
int newIntentId = mIdToUriMapping.size();
rca.mIntentId = newIntentId;
mIdToUriMapping.put(newIntentId, uri);
- // mUriToIntentMapping.put(uri, mServiceIntent);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
} else {
rca.mIntentId = mIdToUriMapping.keyAt(index);
+ rca.mItems = null;
+ continue;
}
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
} else {
- RemoteCollectionItems items = getCollectionItemsFromFuture(
- rca.mItemsFuture);
- for (RemoteViews views : items.mViews) {
- views.collectAllIntents();
+ for (RemoteViews views : rca.mItems.mViews) {
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(views));
}
}
} else if (action instanceof ViewGroupActionAdd vgaa
&& vgaa.mNestedViews != null) {
- vgaa.mNestedViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(vgaa.mNestedViews));
}
}
}
+
+ return collectionFuture;
+ }
+
+ private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
+ Intent intent) {
+ if (intent == null) {
+ Log.e(LOG_TAG, "Null intent received when generating adapter future");
+ return CompletableFuture.completedFuture(new RemoteCollectionItems
+ .Builder().build());
+ }
+
+ final Context context = ActivityThread.currentApplication();
+ final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
+
+ context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
+ result.defaultExecutor(), new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName,
+ IBinder iBinder) {
+ RemoteCollectionItems items;
+ try {
+ items = IRemoteViewsFactory.Stub.asInterface(iBinder)
+ .getRemoteCollectionItems();
+ } catch (RemoteException re) {
+ items = new RemoteCollectionItems.Builder().build();
+ Log.e(LOG_TAG, "Error getting collection items from the factory",
+ re);
+ } finally {
+ context.unbindService(this);
+ }
+
+ result.complete(items);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+ });
+
+ result.completeOnTimeout(
+ new RemoteCollectionItems.Builder().build(),
+ MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
+
+ return result;
}
public void writeToParcel(Parcel out, int flags) {
@@ -1360,10 +1348,7 @@
out.writeInt(mIdToUriMapping.keyAt(i));
String intentUri = mIdToUriMapping.valueAt(i);
out.writeString8(intentUri);
- RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
- ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
- : mUriToCollectionMapping.get(intentUri);
- items.writeToParcel(out, flags, true);
+ mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
}
}
}
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 4e0f9a5..0ec9ffe 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -108,6 +108,18 @@
*/
public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
+ /**
+ * Creates a decor surface in the parent Task of the TaskFragment. The created decor surface
+ * will be provided in {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED}
+ * event callback.
+ */
+ public static final int OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE = 14;
+
+ /**
+ * Removes the decor surface in the parent Task of the TaskFragment.
+ */
+ public static final int OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE = 15;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -124,6 +136,8 @@
OP_TYPE_SET_ISOLATED_NAVIGATION,
OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
OP_TYPE_REORDER_TO_TOP_OF_TASK,
+ OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE,
+ OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index e6eeca4..a77c234 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -22,6 +22,9 @@
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+import java.util.Objects;
/**
* The information about the parent Task of a particular TaskFragment
@@ -37,12 +40,15 @@
private final boolean mHasDirectActivity;
+ @Nullable private final SurfaceControl mDecorSurface;
+
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
- boolean visible, boolean hasDirectActivity) {
+ boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
mVisible = visible;
mHasDirectActivity = hasDirectActivity;
+ mDecorSurface = decorSurface;
}
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
@@ -50,6 +56,7 @@
mDisplayId = info.mDisplayId;
mVisible = info.mVisible;
mHasDirectActivity = info.mHasDirectActivity;
+ mDecorSurface = info.mDecorSurface;
}
/** The {@link Configuration} of the parent Task */
@@ -92,7 +99,13 @@
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
- && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity;
+ && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+ && mDecorSurface == that.mDecorSurface;
+ }
+
+ @Nullable
+ public SurfaceControl getDecorSurface() {
+ return mDecorSurface;
}
@WindowConfiguration.WindowingMode
@@ -107,6 +120,7 @@
+ ", displayId=" + mDisplayId
+ ", visible=" + mVisible
+ ", hasDirectActivity=" + mHasDirectActivity
+ + ", decorSurface=" + mDecorSurface
+ "}";
}
@@ -128,7 +142,8 @@
return mConfiguration.equals(that.mConfiguration)
&& mDisplayId == that.mDisplayId
&& mVisible == that.mVisible
- && mHasDirectActivity == that.mHasDirectActivity;
+ && mHasDirectActivity == that.mHasDirectActivity
+ && mDecorSurface == that.mDecorSurface;
}
@Override
@@ -137,6 +152,7 @@
result = 31 * result + mDisplayId;
result = 31 * result + (mVisible ? 1 : 0);
result = 31 * result + (mHasDirectActivity ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mDecorSurface);
return result;
}
@@ -146,6 +162,7 @@
dest.writeInt(mDisplayId);
dest.writeBoolean(mVisible);
dest.writeBoolean(mHasDirectActivity);
+ dest.writeTypedObject(mDecorSurface, flags);
}
private TaskFragmentParentInfo(Parcel in) {
@@ -153,6 +170,7 @@
mDisplayId = in.readInt();
mVisible = in.readBoolean();
mHasDirectActivity = in.readBoolean();
+ mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
}
public static final Creator<TaskFragmentParentInfo> CREATOR =
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7c4252e..6b074a6 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -33,6 +33,7 @@
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
@@ -131,6 +132,18 @@
}
/**
+ * Logs magnification that is assigned to the two finger triple tap shortcut. Calls this when
+ * triggering the magnification two finger triple tap shortcut.
+ */
+ public static void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED,
+ MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+ // jean update
+ ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP,
+ convertToLoggingServiceStatus(enabled));
+ }
+
+ /**
* Logs accessibility feature name that is assigned to the long pressed accessibility button
* shortcut. Calls this when clicking the long pressed accessibility button shortcut.
*
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
index f5fe12e..e55c641 100644
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -37,22 +37,11 @@
* @return The highest refresh rate
*/
public static float findHighestRefreshRateForDefaultDisplay(Context context) {
- return findHighestRefreshRate(context, Display.DEFAULT_DISPLAY);
- }
-
- /**
- * Find the highest refresh rate among all the modes of the specified display.
- *
- * @param context The context
- * @param displayId The display ID
- * @return The highest refresh rate
- */
- public static float findHighestRefreshRate(Context context, int displayId) {
final DisplayManager dm = context.getSystemService(DisplayManager.class);
- final Display display = dm.getDisplay(displayId);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
if (display == null) {
- Log.w(TAG, "No valid display device with ID = " + displayId);
+ Log.w(TAG, "No valid default display device");
return DEFAULT_REFRESH_RATE;
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index dadeb2b..aab2242 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -60,7 +60,8 @@
@UnsupportedAppUsage(maxTargetSdk = 28)
void notifyCallForwardingChanged(boolean cfi);
void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
- void notifyDataActivityForSubscriber(int phoneId, int subId, int state);
+ void notifyDataActivityForSubscriber(int subId, int state);
+ void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state);
void notifyDataConnectionForSubscriber(
int phoneId, int subId, in PreciseDataConnectionState preciseState);
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 440a332..f365dbb 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -462,9 +462,4 @@
],
},
},
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index 2b74220..11b367b 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -92,8 +92,24 @@
message UidBatteryConsumer {
optional int32 uid = 1;
optional BatteryConsumerData battery_consumer_data = 2;
- optional int64 time_in_foreground_millis = 3;
- optional int64 time_in_background_millis = 4;
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_foreground_millis = 3 [deprecated = true];
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_background_millis = 4 [deprecated = true];
+
+ message TimeInState {
+ enum ProcessState {
+ UNSPECIFIED = 0;
+ FOREGROUND = 1;
+ BACKGROUND = 2;
+ FOREGROUND_SERVICE = 3;
+ }
+
+ optional ProcessState process_state = 1;
+ optional int64 time_in_state_millis = 2;
+ }
+
+ repeated TimeInState time_in_state = 5;
}
repeated UidBatteryConsumer uid_battery_consumers = 5;
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index d5cf60d..c242c9c 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -110,6 +110,7 @@
optional int32 task_root_package_token = 11;
optional int32 task_root_class_token = 12;
optional int32 locus_id_token = 13;
+ optional ObfuscatedUserInteractionExtrasProto interaction_extras = 14;
}
/**
@@ -129,6 +130,7 @@
optional string task_root_package = 11;
optional string task_root_class = 12;
optional string locus_id = 13 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional bytes extras = 14;
}
/**
@@ -145,3 +147,11 @@
// Stores the mappings for every package
repeated PackagesMap packages_map = 2;
}
+
+/**
+ * Store the relevant information from extra details for user interaction event.
+ */
+message ObfuscatedUserInteractionExtrasProto {
+ optional int32 category_token = 1;
+ optional int32 action_token = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d208c6..0021640 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7817,6 +7817,13 @@
<permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an app to track all preparations for a complete factory reset.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.PREPARE_FACTORY_RESET"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 98897d8..1d4e01a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1522,6 +1522,10 @@
config_screenBrightnessSettingDefaultFloat instead -->
<integer name="config_screenBrightnessSettingDefault">102</integer>
+ <!-- Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ The value must be in the range [0, 255]. -->
+ <integer name="config_screenBrightnessCapForWearBedtimeMode">20</integer>
+
<!-- Minimum screen brightness setting allowed by power manager.
-2 is invalid so setting will resort to int value specified above.
Set this to 0.0 to allow screen to go to minimal brightness.
@@ -4989,6 +4993,11 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
+ <!-- Packages that contain a security state.
+ {@link SecurityStateManager#getGlobalSecurityState} will read and report the state/version
+ of these packages. -->
+ <string-array name="config_securityStatePackages" translatable="false" />
+
<!-- Package name for the default Health Connect app.
OEMs can set this with their own health app package name to define a default app with high
priority for the app to store the health data. If set the app always has priority of 1
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 017688a..4b0fa4b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1274,6 +1274,7 @@
<java-symbol type="array" name="policy_exempt_apps" />
<java-symbol type="array" name="vendor_policy_exempt_apps" />
<java-symbol type="array" name="cloneable_apps" />
+ <java-symbol type="array" name="config_securityStatePackages" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -2078,6 +2079,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
+ <java-symbol type="integer" name="config_screenBrightnessCapForWearBedtimeMode" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index aaaa3c7..ac1f7d0 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -48,6 +48,11 @@
private static final int UID_1 = 2000;
private static final int UID_2 = 3000;
private static final int UID_3 = 4000;
+ private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+ BatteryConsumer.PROCESS_STATE_FOREGROUND,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+ };
@Test
public void testGetStatsProto() {
@@ -195,6 +200,20 @@
assertEquals("For uid " + uid,
uidConsumer.getTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND),
uidConsumerProto.timeInBackgroundMillis);
+ for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+ final long timeInStateMillis = uidConsumer.getTimeInProcessStateMs(processState);
+ if (timeInStateMillis <= 0) {
+ continue;
+ }
+ assertEquals("For uid " + uid + ", process state " + processState,
+ timeInStateMillis,
+ Arrays.stream(uidConsumerProto.timeInState)
+ .filter(timeInState -> timeInState.processState == processState)
+ .findFirst()
+ .orElseThrow()
+ .timeInStateMillis);
+ }
+
if (expectNullBatteryConsumerData) {
assertNull("For uid " + uid, uidConsumerProto.batteryConsumerData);
} else {
@@ -250,8 +269,8 @@
final UidBatteryConsumer.Builder uidBuilder = builder
.getOrCreateUidBatteryConsumerBuilder(UID_0)
.setPackageWithHighestDrain("myPackage0")
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 2000)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(
@@ -285,7 +304,7 @@
builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
.setPackageWithHighestDrain("myPackage1")
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234);
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
builder.getOrCreateUidBatteryConsumerBuilder(UID_2)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
@@ -331,8 +350,10 @@
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
builder.getOrCreateUidBatteryConsumerBuilder(i)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2 * 60 * 1000)
+ .setTimeInProcessStateMs(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 1 * 60 * 1000)
+ .setTimeInProcessStateMs(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 2 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
}
@@ -340,16 +361,16 @@
// Add a UID with much larger battery footprint
final int largeConsumerUid = 3001;
builder.getOrCreateUidBatteryConsumerBuilder(largeConsumerUid)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 10 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 20 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 10 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 20 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
// Add a UID with much larger usage duration
final int highUsageUid = 3002;
builder.getOrCreateUidBatteryConsumerBuilder(highUsageUid)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 60 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 120 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 60 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 120 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 5a202c5..9dce899 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -28,6 +28,7 @@
import android.app.usage.UsageEvents.Event;
import android.content.res.Configuration;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -140,6 +141,12 @@
case Event.LOCUS_ID_SET:
event.mLocusId = anyString();
break;
+ case Event.USER_INTERACTION:
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, anyString());
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, anyString());
+ event.mExtras = extras;
+ break;
}
event.mFlags = anyInt();
@@ -176,6 +183,14 @@
case Event.LOCUS_ID_SET:
assertEquals(ue1.mLocusId, ue2.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ final PersistableBundle extras1 = ue1.getExtras();
+ final PersistableBundle extras2 = ue2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY));
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION));
+ break;
}
assertEquals(ue1.mFlags, ue2.mFlags);
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 083e37a..fae7148 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -72,7 +72,7 @@
"mShortcutId", "mShortcutIdToken", "mBucketAndReason", "mInstanceId",
"mNotificationChannelId", "mNotificationChannelIdToken", "mTaskRootPackage",
"mTaskRootPackageToken", "mTaskRootClass", "mTaskRootClassToken", "mLocusId",
- "mLocusIdToken"};
+ "mLocusIdToken", "mExtras", "mUserInteractionExtrasToken"};
// All fields in this list are defined in UsageEvents.Event but not persisted
private static final String[] USAGEEVENTS_IGNORED_FIELDS = {"mAction", "mContentAnnotations",
"mContentType", "DEVICE_EVENT_PACKAGE_NAME", "FLAG_IS_PACKAGE_INSTANT_APP",
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 87c167c..4a9cb71 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -83,6 +83,12 @@
}
@Override
+ protected AssetManager createAssetManager(@NonNull final ResourcesKey key,
+ ResourcesManager.ApkAssetsSupplier apkSupplier) {
+ return createAssetManager(key);
+ }
+
+ @Override
protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
return mDisplayMetricsMap.get(displayId);
}
@@ -100,7 +106,7 @@
null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
- assertSame(resources, newResources);
+ assertSame(resources.getImpl(), newResources.getImpl());
}
@SmallTest
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index d36ac39..2237ba1 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -529,6 +529,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1582845629": {
+ "message": "Starting animation on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1575977269": {
"message": "Skipping %s: mismatch root %s",
"level": "DEBUG",
@@ -925,6 +931,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-1243510456": {
+ "message": "Dim animation requested: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1237827119": {
"message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s",
"level": "VERBOSE",
@@ -1171,6 +1183,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
+ "-1028213464": {
+ "message": "%s skipping animation and directly setting alpha=%f, blur=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1022146708": {
"message": "Skipping %s: mismatch activity type",
"level": "DEBUG",
@@ -1795,12 +1813,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-504637678": {
- "message": "Starting animation on dim layer %s, requested by %s, alpha: %f -> %f, blur: %d -> %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"-503656156": {
"message": "Update process config of %s to new config %s",
"level": "VERBOSE",
@@ -4027,12 +4039,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1620751818": {
- "message": "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"1621562070": {
"message": " startWCT=%s",
"level": "VERBOSE",
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bce..dd703f5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@
import android.util.Log;
import java.util.Calendar;
-import java.util.Objects;
/**
* @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@
return new KeyStore2();
}
+ /**
+ * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+ *
+ * @throws IllegalStateException if the KeystoreService is not available or has not
+ * been initialized when called. This is a state that should not happen and indicates
+ * and error somewhere in the stack or with the calling processes access permissions.
+ */
@NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
- .getService(KEYSTORE2_SERVICE_NAME));
- Binder.allowBlocking(mBinder.asBinder());
+ .getService(KEYSTORE2_SERVICE_NAME));
}
- return Objects.requireNonNull(mBinder);
+ if (mBinder == null) {
+ throw new IllegalStateException(
+ "Could not connect to Keystore service. Keystore may have crashed or not been"
+ + " initialized");
+ }
+ Binder.allowBlocking(mBinder.asBinder());
+ return mBinder;
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 50cfd94..4c2433f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -443,7 +443,8 @@
assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 02031a6..8c274a2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1139,7 +1139,8 @@
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */);
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(TASK_ID)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index e56c8ab..7b77235 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -79,14 +79,16 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -106,13 +108,15 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertFalse(taskContainer.isInPictureInPicture());
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertTrue(taskContainer.isInPictureInPicture());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8f904c4..45c948b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -537,8 +537,8 @@
return;
}
- final boolean clickedBubbleIsCurrentlyExpandedBubble =
- clickedBubble.getKey().equals(mExpandedBubble.getKey());
+ final boolean clickedBubbleIsCurrentlyExpandedBubble = mExpandedBubble != null
+ && clickedBubble.getKey().equals(mExpandedBubble.getKey());
if (isExpanded()) {
mExpandedAnimationController.onGestureFinished();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 271a3b2..63afd3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -627,7 +627,8 @@
&& mRecentsTask.equals(change.getContainer());
hasTaskChange = hasTaskChange || isRootTask;
final boolean isLeafTask = leafTaskFilter.test(change);
- if (TransitionUtil.isOpeningType(change.getMode())) {
+ if (TransitionUtil.isOpeningType(change.getMode())
+ || TransitionUtil.isOrderOnly(change)) {
if (isRecentsTask) {
recentsOpening = change;
} else if (isRootTask || isLeafTask) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index ef8393c..35a1fa0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -151,7 +151,14 @@
@Override
public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
- runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ if (mHandler.getLooper().isCurrentThread()) {
+ // We can only use the transaction if it can updated synchronously, otherwise the tx
+ // will be applied immediately after but also used/updated on the view thread which
+ // will lead to a race and/or crash
+ runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ } else {
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8cbcde3..53ec201 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -49,7 +49,6 @@
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
-import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
@@ -70,7 +69,9 @@
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
private final int mDisplayId;
- private final BaseIWindow mFakeWindow;
+
+ private final IBinder mClientToken;
+
private final IBinder mFocusGrantToken;
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
@@ -78,7 +79,7 @@
private final DragPositioningCallback mCallback;
private final SurfaceControl mInputSinkSurface;
- private final BaseIWindow mFakeSinkWindow;
+ private final IBinder mSinkClientToken;
private final InputChannel mSinkInputChannel;
private final DisplayController mDisplayController;
@@ -116,17 +117,14 @@
mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
mDisplayController = displayController;
- // Use a fake window as the backing surface is a container layer, and we don't want to
- // create a buffer layer for it, so we can't use ViewRootImpl.
- mFakeWindow = new BaseIWindow();
- mFakeWindow.setSession(mWindowSession);
+ mClientToken = new Binder();
mFocusGrantToken = new Binder();
mInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mDecorationSurface,
- mFakeWindow.asBinder(),
+ mClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
@@ -155,13 +153,13 @@
.setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
.show(mInputSinkSurface)
.apply();
- mFakeSinkWindow = new BaseIWindow();
+ mSinkClientToken = new Binder();
mSinkInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mInputSinkSurface,
- mFakeSinkWindow.asBinder(),
+ mSinkClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
0 /* privateFlags */,
@@ -324,14 +322,14 @@
mInputEventReceiver.dispose();
mInputChannel.dispose();
try {
- mWindowSession.remove(mFakeWindow.asBinder());
+ mWindowSession.remove(mClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
mSinkInputChannel.dispose();
try {
- mWindowSession.remove(mFakeSinkWindow.asBinder());
+ mWindowSession.remove(mSinkClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 6429b00..fafd37b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -45,12 +45,15 @@
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="run-command" value="settings put global package_verifier_user_consent -1"/>
<option name="teardown-command"
value="settings delete secure show_ime_with_hard_keyboard"/>
<option name="teardown-command" value="settings delete system show_touches"/>
<option name="teardown-command" value="settings delete system pointer_location"/>
<option name="teardown-command"
value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ <option name="teardown-command"
+ value="settings put global package_verifier_user_consent 1"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
@@ -76,6 +79,8 @@
value="appops set com.android.shell android:mock_location deny"/>
</target_preparer>
+ <target_preparer class="com.android.csuite.core.AppCrawlTesterHostPreparer"/>
+
<!-- Use app crawler to log into Netflix -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
new file mode 100644
index 0000000..b91d6f9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+
+/**
+ * Basic test handler that immediately executes anything that is posted on it.
+ */
+public class TestHandler extends Handler {
+ public TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ dispatchMessage(msg);
+ return true;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 4afb29e..d7c4610 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -40,6 +40,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -58,6 +59,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestHandler;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
@@ -92,8 +94,7 @@
Transitions mTransitions;
@Mock
Looper mViewLooper;
- @Mock
- Handler mViewHandler;
+ TestHandler mViewHandler;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -112,7 +113,7 @@
mContext = getContext();
doReturn(true).when(mViewLooper).isCurrentThread();
- doReturn(mViewLooper).when(mViewHandler).getLooper();
+ mViewHandler = spy(new TestHandler(mViewLooper));
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
@@ -668,4 +669,24 @@
mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
verify(mViewHandler).post(any());
}
+
+ @Test
+ public void testSetResizeBgOnSameUiThread_expectUsesTransaction() {
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler, never()).post(any());
+ verify(mTaskView, never()).setResizeBackgroundColor(eq(Color.BLUE));
+ verify(mTaskView).setResizeBackgroundColor(eq(tx), eq(Color.BLUE));
+ }
+
+ @Test
+ public void testSetResizeBgOnDifferentUiThread_expectDoesNotUseTransaction() {
+ doReturn(false).when(mViewLooper).isCurrentThread();
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler).post(any());
+ verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
+ }
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index a1d6ab5..b1cf96d 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -113,13 +113,13 @@
}
public final class GnssMeasurementRequest implements android.os.Parcelable {
- method @NonNull public android.os.WorkSource getWorkSource();
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull public android.os.WorkSource getWorkSource();
method public boolean isCorrelationVectorOutputsEnabled();
}
public static final class GnssMeasurementRequest.Builder {
method @NonNull public android.location.GnssMeasurementRequest.Builder setCorrelationVectorOutputsEnabled(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
public final class GnssReflectingPlane implements android.os.Parcelable {
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index 65af392..2f0835a 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -17,11 +17,13 @@
package android.location;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
@@ -121,6 +123,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
public @NonNull WorkSource getWorkSource() {
return mWorkSource;
@@ -298,6 +301,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
@RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index c471a27..b6055e8 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -5,4 +5,18 @@
namespace: "location"
description: "Flag for GNSS API for NavIC L1"
bug: "302199306"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "gnss_call_stop_before_set_position_mode"
+ namespace: "location"
+ description: "Flag for calling stop() before setPositionMode()"
+ bug: "306874828"
+}
+
+flag {
+ name: "gnss_api_measurement_request_work_source"
+ namespace: "location"
+ description: "Flag for GnssMeasurementRequest WorkSource API"
+ bug: "295235160"
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d14775f..5c8758a 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -739,7 +739,7 @@
oneway void stopLoudnessCodecUpdates(int piid);
- oneway void addLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
+ oneway void addLoudnessCodecInfo(int piid, int mediaCodecHash, in LoudnessCodecInfo codecInfo);
oneway void removeLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index 92f3372..de9d87c0 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -30,10 +30,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -47,9 +47,6 @@
* parameter updates are defined by the CTA-2075 standard.
* <p>A new object should be instantiated for each {@link AudioTrack} with the help
* of {@link #create()} or {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public class LoudnessCodecConfigurator {
@@ -57,9 +54,6 @@
/**
* Listener used for receiving asynchronous loudness metadata updates.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public interface OnLoudnessCodecUpdateListener {
@@ -76,9 +70,6 @@
* @return a Bundle which contains the original computed codecValues
* aggregated with user edits. The platform will configure the associated
* MediaCodecs with the returned Bundle params.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
@NonNull
@@ -112,9 +103,6 @@
* Otherwise, use {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
*
* @return the {@link LoudnessCodecConfigurator} instance
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public static @NonNull LoudnessCodecConfigurator create() {
@@ -133,9 +121,6 @@
* @param listener used for receiving updates
*
* @return the {@link LoudnessCodecConfigurator} instance
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public static @NonNull LoudnessCodecConfigurator create(
@@ -200,12 +185,9 @@
* method will have the effect of clearing the existing set
* {@link AudioTrack} and will stop receiving asynchronous
* loudness updates
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
- public void setAudioTrack(AudioTrack audioTrack) {
+ public void setAudioTrack(@Nullable AudioTrack audioTrack) {
List<LoudnessCodecInfo> codecInfos;
int piid = PLAYER_PIID_INVALID;
int oldPiid = PLAYER_PIID_INVALID;
@@ -250,10 +232,11 @@
* previously added.
*
* @param mediaCodec the codec to start receiving asynchronous loudness
- * updates
- *
- * TODO: remove hide once API is final
- * @hide
+ * updates. The codec has to be in a configured or started
+ * state in order to add it for loudness updates.
+ * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+ * does not contain loudness metadata or if it
+ * was already added before
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
@@ -262,30 +245,31 @@
int piid = PLAYER_PIID_INVALID;
final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
- if (mcInfo != null) {
- synchronized (mConfiguratorLock) {
- final AtomicBoolean containsCodec = new AtomicBoolean(false);
- Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
- containsCodec.set(!codecSet.add(mc));
- return codecSet;
- });
- if (newSet == null) {
- newSet = new HashSet<>();
- newSet.add(mc);
- mMediaCodecs.put(mcInfo, newSet);
- }
- if (containsCodec.get()) {
- Log.v(TAG, "Loudness configurator already added media codec " + mediaCodec);
- return;
- }
- if (mAudioTrack != null) {
- piid = mAudioTrack.getPlayerIId();
- }
+ if (mcInfo == null) {
+ throw new IllegalArgumentException("Could not extract codec loudness information");
+ }
+ synchronized (mConfiguratorLock) {
+ final AtomicBoolean containsCodec = new AtomicBoolean(false);
+ Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
+ containsCodec.set(!codecSet.add(mc));
+ return codecSet;
+ });
+ if (newSet == null) {
+ newSet = new HashSet<>();
+ newSet.add(mc);
+ mMediaCodecs.put(mcInfo, newSet);
}
+ if (containsCodec.get()) {
+ throw new IllegalArgumentException(
+ "Loudness configurator already added " + mediaCodec);
+ }
+ if (mAudioTrack != null) {
+ piid = mAudioTrack.getPlayerIId();
+ }
+ }
- if (piid != PLAYER_PIID_INVALID) {
- mLcDispatcher.addLoudnessCodecInfo(piid, mcInfo);
- }
+ if (piid != PLAYER_PIID_INVALID) {
+ mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
}
}
@@ -297,37 +281,44 @@
* <p>No elements will be removed if the passed mediaCodec was not added before.
*
* @param mediaCodec the element to remove for receiving asynchronous updates
- *
- * TODO: remove hide once API is final
- * @hide
+ * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+ * does not contain loudness metadata or if it
+ * was not added before
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public void removeMediaCodec(@NonNull MediaCodec mediaCodec) {
int piid = PLAYER_PIID_INVALID;
LoudnessCodecInfo mcInfo;
- AtomicBoolean removed = new AtomicBoolean(false);
+ AtomicBoolean removedMc = new AtomicBoolean(false);
+ AtomicBoolean removeInfo = new AtomicBoolean(false);
mcInfo = getCodecInfo(Objects.requireNonNull(mediaCodec,
"MediaCodec for removeMediaCodec cannot be null"));
- if (mcInfo != null) {
- synchronized (mConfiguratorLock) {
- if (mAudioTrack != null) {
- piid = mAudioTrack.getPlayerIId();
+ if (mcInfo == null) {
+ throw new IllegalArgumentException("Could not extract codec loudness information");
+ }
+ synchronized (mConfiguratorLock) {
+ if (mAudioTrack != null) {
+ piid = mAudioTrack.getPlayerIId();
+ }
+ mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
+ removedMc.set(mcs.remove(mediaCodec));
+ if (mcs.isEmpty()) {
+ // remove the entry
+ removeInfo.set(true);
+ return null;
}
- mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
- removed.set(mcs.remove(mediaCodec));
- if (mcs.isEmpty()) {
- // remove the entry
- return null;
- }
- return mcs;
- });
+ return mcs;
+ });
+ if (!removedMc.get()) {
+ throw new IllegalArgumentException(
+ "Loudness configurator does not contain " + mediaCodec);
}
+ }
- if (piid != PLAYER_PIID_INVALID && removed.get()) {
- mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
- }
+ if (piid != PLAYER_PIID_INVALID && removeInfo.get()) {
+ mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
}
}
@@ -342,9 +333,6 @@
*
* @return the {@link Bundle} containing the current loudness parameters. Caller is
* responsible to update the {@link MediaCodec}
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
@NonNull
@@ -375,9 +363,9 @@
}
/** @hide */
- /*package*/ List<MediaCodec> getRegisteredMediaCodecList() {
+ /*package*/ Map<LoudnessCodecInfo, Set<MediaCodec>> getRegisteredMediaCodecs() {
synchronized (mConfiguratorLock) {
- return mMediaCodecs.values().stream().flatMap(Collection::stream).toList();
+ return mMediaCodecs;
}
}
@@ -397,40 +385,43 @@
return null;
}
- final MediaFormat inputFormat = mediaCodec.getInputFormat();
- final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
- if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
- // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize one of
- // these two keys
- int aacProfile = -1;
- int profile = -1;
- try {
- aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
- } catch (NullPointerException e) {
- // does not contain KEY_AAC_PROFILE. do nothing
- }
- try {
- profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
- } catch (NullPointerException e) {
- // does not contain KEY_PROFILE. do nothing
- }
- if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
- || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
- lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+ try {
+ final MediaFormat inputFormat = mediaCodec.getInputFormat();
+ final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
+ if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
+ // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize
+ // one of these two keys
+ int aacProfile = -1;
+ int profile = -1;
+ try {
+ aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
+ } catch (NullPointerException e) {
+ // does not contain KEY_AAC_PROFILE. do nothing
+ }
+ try {
+ profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
+ } catch (NullPointerException e) {
+ // does not contain KEY_PROFILE. do nothing
+ }
+ if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
+ || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
+ lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+ } else {
+ lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+ }
} else {
- lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+ Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+ return null;
}
- } else {
- Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+
+ final MediaFormat outputFormat = mediaCodec.getOutputFormat();
+ lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
+ < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "MediaCodec is not configured", e);
return null;
}
- final MediaFormat outputFormat = mediaCodec.getOutputFormat();
- lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
- < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
-
- lci.mediaCodecHashCode = mediaCodec.hashCode();
-
return lci;
}
}
diff --git a/media/java/android/media/LoudnessCodecDispatcher.java b/media/java/android/media/LoudnessCodecDispatcher.java
index be881b1..b546a81 100644
--- a/media/java/android/media/LoudnessCodecDispatcher.java
+++ b/media/java/android/media/LoudnessCodecDispatcher.java
@@ -27,12 +27,16 @@
import android.os.RemoteException;
import android.util.Log;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -52,6 +56,9 @@
private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>
mConfiguratorListener = new HashMap<>();
@@ -66,38 +73,56 @@
@Override
public void dispatchLoudnessCodecParameterChange(int piid, PersistableBundle params) {
- mLoudnessListenerMgr.callListeners(listener ->
+ if (DEBUG) {
+ Log.d(TAG, "dispatchLoudnessCodecParameterChange for piid " + piid
+ + " persistable bundle: " + params);
+ }
+ mLoudnessListenerMgr.callListeners(listener -> {
+ synchronized (mLock) {
mConfiguratorListener.computeIfPresent(listener, (l, lcConfig) -> {
// send the appropriate bundle for the user to update
if (lcConfig.getAssignedTrackPiid() == piid) {
- final List<MediaCodec> mediaCodecs =
- lcConfig.getRegisteredMediaCodecList();
- for (MediaCodec mediaCodec : mediaCodecs) {
- final String infoKey = Integer.toString(mediaCodec.hashCode());
+ final Map<LoudnessCodecInfo, Set<MediaCodec>> mediaCodecsMap =
+ lcConfig.getRegisteredMediaCodecs();
+ for (LoudnessCodecInfo codecInfo : mediaCodecsMap.keySet()) {
+ final String infoKey = Integer.toString(codecInfo.hashCode());
+ Bundle bundle = null;
if (params.containsKey(infoKey)) {
- Bundle bundle = new Bundle(
- params.getPersistableBundle(infoKey));
- if (DEBUG) {
- Log.d(TAG,
- "Received for piid " + piid + " bundle: " + bundle);
+ bundle = new Bundle(params.getPersistableBundle(infoKey));
+ }
+
+ final Set<MediaCodec> mediaCodecs = mediaCodecsMap.get(codecInfo);
+ for (MediaCodec mediaCodec : mediaCodecs) {
+ final String mediaCodecKey = Integer.toString(
+ mediaCodec.hashCode());
+ if (bundle == null && !params.containsKey(mediaCodecKey)) {
+ continue;
+ }
+ boolean canBreak = false;
+ if (bundle == null) {
+ // key was set by media codec hash to update single codec
+ bundle = new Bundle(
+ params.getPersistableBundle(mediaCodecKey));
+ canBreak = true;
}
bundle =
LoudnessCodecUpdatesDispatcherStub.filterLoudnessParams(
- l.onLoudnessCodecUpdate(mediaCodec, bundle));
- if (DEBUG) {
- Log.d(TAG, "User changed for piid " + piid
- + " to filtered bundle: " + bundle);
- }
+ l.onLoudnessCodecUpdate(mediaCodec,
+ bundle));
if (!bundle.isDefinitelyEmpty()) {
mediaCodec.setParameters(bundle);
}
+ if (canBreak) {
+ break;
+ }
}
}
}
-
return lcConfig;
- }));
+ });
+ }
+ });
}
private static Bundle filterLoudnessParams(Bundle bundle) {
@@ -130,21 +155,33 @@
mLoudnessListenerMgr.addListener(
executor, listener, "addLoudnessCodecListener",
() -> dispatcher);
- mConfiguratorListener.put(listener, configurator);
+ synchronized (mLock) {
+ mConfiguratorListener.put(listener, configurator);
+ }
}
void removeLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator) {
Objects.requireNonNull(configurator);
- for (Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e :
- mConfiguratorListener.entrySet()) {
- if (e.getValue() == configurator) {
- final OnLoudnessCodecUpdateListener listener = e.getKey();
- mConfiguratorListener.remove(listener);
- mLoudnessListenerMgr.removeListener(listener, "removeLoudnessCodecListener");
- break;
+ OnLoudnessCodecUpdateListener listenerToRemove = null;
+ synchronized (mLock) {
+ Iterator<Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>> iterator =
+ mConfiguratorListener.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e =
+ iterator.next();
+ if (e.getValue() == configurator) {
+ final OnLoudnessCodecUpdateListener listener = e.getKey();
+ iterator.remove();
+ listenerToRemove = listener;
+ break;
+ }
}
}
+ if (listenerToRemove != null) {
+ mLoudnessListenerMgr.removeListener(listenerToRemove,
+ "removeLoudnessCodecListener");
+ }
}
}
@@ -202,9 +239,10 @@
}
/** @hide */
- public void addLoudnessCodecInfo(int piid, @NonNull LoudnessCodecInfo mcInfo) {
+ public void addLoudnessCodecInfo(int piid, int mediaCodecHash,
+ @NonNull LoudnessCodecInfo mcInfo) {
try {
- mAudioService.addLoudnessCodecInfo(piid, mcInfo);
+ mAudioService.addLoudnessCodecInfo(piid, mediaCodecHash, mcInfo);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/LoudnessCodecInfo.aidl b/media/java/android/media/LoudnessCodecInfo.aidl
index fd69517..0ac5646 100644
--- a/media/java/android/media/LoudnessCodecInfo.aidl
+++ b/media/java/android/media/LoudnessCodecInfo.aidl
@@ -23,7 +23,7 @@
*
* {@hide}
*/
-@JavaDerive(equals = true)
+@JavaDerive(equals = true, toString = true)
parcelable LoudnessCodecInfo {
/** Supported codec metadata types for loudness updates. */
@Backing(type="int")
@@ -37,7 +37,6 @@
CODEC_METADATA_TYPE_DTS_UHD = 6
}
- int mediaCodecHashCode;
CodecMetadataType metadataType;
boolean isDownmixing;
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
index aa5a290..2b52c4b 100644
--- a/media/java/android/media/tv/ad/TvAdManager.java
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -16,6 +16,10 @@
package android.media.tv.ad;
+import android.annotation.FlaggedApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.tv.flags.Flags;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -23,14 +27,17 @@
/**
* Central system API to the overall client-side TV AD architecture, which arbitrates interaction
* between applications and AD services.
- * @hide
*/
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+@SystemService(Context.TV_AD_SERVICE)
public class TvAdManager {
+ // TODO: implement more methods and unhide APIs.
private static final String TAG = "TvAdManager";
private final ITvAdManager mService;
private final int mUserId;
+ /** @hide */
public TvAdManager(ITvAdManager service, int userId) {
mService = service;
mUserId = userId;
@@ -38,6 +45,7 @@
/**
* The Session provides the per-session functionality of AD service.
+ * @hide
*/
public static final class Session {
private final IBinder mToken;
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index a73d1ff..018eaf6 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -5,4 +5,11 @@
namespace: "media_tv"
description: "Constants for standardizing broadcast visibility types."
bug: "222402395"
+}
+
+flag {
+ name: "enable_ad_service_fw"
+ namespace: "media_tv"
+ description: "Enable the TV client-side AD framework."
+ bug: "303506816"
}
\ No newline at end of file
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 65a9799..c9e36b7 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -208,7 +208,7 @@
verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
mLcc.addMediaCodec(createAndConfigureMediaCodec());
- verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
}
@Test
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index fe26dc3..991fe41 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -16,10 +16,12 @@
dex_preopt: {
profile_guided: true,
+ //TODO: b/312357299 - Update baseline profile
profile: "profile.txt.prof",
},
static_libs: [
+ "CredentialManagerShared",
"PlatformComposeCore",
"androidx.activity_activity-compose",
"androidx.appcompat_appcompat",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 42f1207..325d3f8 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
@@ -27,10 +28,10 @@
import com.android.credentialmanager.model.Request
fun Intent.parse(
- packageManager: PackageManager,
+ context: Context,
): Request {
- return parseCancelUiRequest(packageManager)
- ?: parseRequestInfo()
+ return parseCancelUiRequest(context.packageManager)
+ ?: parseRequestInfo(context)
}
fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
@@ -51,11 +52,11 @@
}
}
-fun Intent.parseRequestInfo(): Request =
+fun Intent.parseRequestInfo(context: Context): Request =
requestInfo.let{ info ->
when (info?.type) {
RequestInfo.TYPE_CREATE -> Request.Create(info.token)
- RequestInfo.TYPE_GET -> toGet()
+ RequestInfo.TYPE_GET -> toGet(context)
else -> {
throw IllegalStateException("Unrecognized request type: ${info?.type}")
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 83183b5..3ef65b0 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -16,8 +16,8 @@
package com.android.credentialmanager.client.impl
+import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.UserSelectionDialogResult
import android.os.Bundle
@@ -26,12 +26,13 @@
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import com.android.credentialmanager.client.CredentialManagerClient
+import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
class CredentialManagerClientImpl @Inject constructor(
- private val packageManager: PackageManager,
+ @ApplicationContext private val context: Context,
) : CredentialManagerClient {
private val _requests = MutableStateFlow<Request?>(null)
@@ -40,7 +41,7 @@
override fun updateRequest(intent: Intent) {
val request = intent.parse(
- packageManager = packageManager,
+ context = context,
)
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
new file mode 100644
index 0000000..f063074
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.ktx
+
+import android.app.slice.Slice
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.credentials.Credential
+import android.credentials.flags.Flags
+import android.credentials.ui.AuthenticationEntry
+import android.credentials.ui.Entry
+import android.credentials.ui.GetCredentialProviderData
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
+import com.android.credentialmanager.TAG
+
+fun CredentialEntryInfo.getIntentSenderRequest(
+ isAutoSelected: Boolean = false
+): IntentSenderRequest? {
+ val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
+
+ return pendingIntent?.let{
+ IntentSenderRequest
+ .Builder(pendingIntent = it)
+ .setFillInIntent(entryIntent)
+ .build()
+ }
+}
+
+// Returns the list (potentially empty) of enabled provider.
+fun List<GetCredentialProviderData>.toProviderList(
+ context: Context,
+): List<ProviderInfo> {
+ val providerList: MutableList<ProviderInfo> = mutableListOf()
+ this.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(
+ ProviderInfo(
+ id = it.providerFlattenedComponentName,
+ icon = providerIcon,
+ displayName = providerLabel,
+ credentialEntryList = getCredentialOptionInfoList(
+ providerId = it.providerFlattenedComponentName,
+ providerLabel = providerLabel,
+ credentialEntries = it.credentialEntries,
+ context = context
+ ),
+ authenticationEntryList = getAuthenticationEntryList(
+ it.providerFlattenedComponentName,
+ providerLabel,
+ providerIcon,
+ it.authenticationEntries),
+ remoteEntry = getRemoteEntry(
+ it.providerFlattenedComponentName,
+ it.remoteEntry
+ ),
+ actionEntryList = getActionEntryList(
+ it.providerFlattenedComponentName, it.actionChips, providerIcon
+ ),
+ )
+ )
+ }
+ return providerList
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getCredentialOptionInfoList(
+ providerId: String,
+ providerLabel: String,
+ credentialEntries: List<Entry>,
+ context: Context,
+): List<CredentialEntryInfo> {
+ val result: MutableList<CredentialEntryInfo> = mutableListOf()
+ credentialEntries.forEach {
+ val credentialEntry = it.slice.credentialEntry
+ when (credentialEntry) {
+ is PasswordCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSWORD,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is PublicKeyCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSKEY,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is CustomCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.UNKNOWN,
+ credentialTypeDisplayName =
+ credentialEntry.typeDisplayName?.toString().orEmpty(),
+ userName = credentialEntry.title.toString(),
+ displayName = credentialEntry.subtitle?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ else -> Log.d(
+ TAG,
+ "Encountered unrecognized credential entry ${it.slice.spec?.type}"
+ )
+ }
+ }
+ return result
+}
+val Slice.credentialEntry: CredentialEntry?
+ get() =
+ try {
+ when (spec?.type) {
+ Credential.TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(this)!!
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+ PublicKeyCredentialEntry.fromSlice(this)!!
+
+ else -> CustomCredentialEntry.fromSlice(this)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ CustomCredentialEntry.fromSlice(this)
+ }
+
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getAuthenticationEntryList(
+ providerId: String,
+ providerDisplayName: String,
+ providerIcon: Drawable,
+ authEntryList: List<AuthenticationEntry>,
+): List<AuthenticationEntryInfo> {
+ val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
+ authEntryList.forEach { entry ->
+ val structuredAuthEntry =
+ AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
+
+ val title: String =
+ structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
+
+ result.add(
+ AuthenticationEntryInfo(
+ providerId = providerId,
+ entryKey = entry.key,
+ entrySubkey = entry.subkey,
+ pendingIntent = structuredAuthEntry.pendingIntent,
+ fillInIntent = entry.frameworkExtrasIntent,
+ title = title,
+ providerDisplayName = providerDisplayName,
+ icon = providerIcon,
+ isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
+ isLastUnlocked =
+ entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
+ )
+ )
+ }
+ return result
+}
+
+private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
+ if (remoteEntry == null) {
+ return null
+ }
+ val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
+ ?: return null
+ return RemoteEntryInfo(
+ providerId = providerId,
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ pendingIntent = structuredRemoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
+ )
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getActionEntryList(
+ providerId: String,
+ actionEntries: List<Entry>,
+ providerIcon: Drawable,
+): List<ActionEntryInfo> {
+ val result: MutableList<ActionEntryInfo> = mutableListOf()
+ actionEntries.forEach {
+ val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
+ result.add(
+ ActionEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = actionEntryUi.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ title = actionEntryUi.title.toString(),
+ icon = providerIcon,
+ subTitle = actionEntryUi.subtitle?.toString(),
+ )
+ )
+ }
+ return result
+}
+
+
+
+private fun getServiceLabelAndIcon(
+ pm: PackageManager,
+ providerFlattenedComponentName: String
+): Pair<String, Drawable>? {
+ var providerLabel: String? = null
+ var providerIcon: Drawable? = null
+ val component = ComponentName.unflattenFromString(providerFlattenedComponentName)
+ if (component == null) {
+ // Test data has only package name not component name.
+ // For test data usage only.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ } else {
+ try {
+ val si = pm.getServiceInfo(component, PackageManager.ComponentInfoFlags.of(0))
+ providerLabel = si.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = si.loadIcon(pm)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Provider service info not found", e)
+ // Added for mdoc use case where the provider may not need to register a service and
+ // instead only relies on the registration api.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ component.packageName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ }
+ }
+ return if (providerLabel == null || providerIcon == null) {
+ Log.d(
+ TAG,
+ "Failed to load provider label/icon for provider $providerFlattenedComponentName"
+ )
+ null
+ } else {
+ Pair(providerLabel, providerIcon)
+ }
+}
+
+private fun getPackageInfo(
+ pm: PackageManager,
+ packageName: String
+): PackageInfo {
+ val packageManagerFlags = PackageManager.MATCH_INSTANT
+
+ return pm.getPackageInfo(
+ packageName,
+ PackageManager.PackageInfoFlags.of(
+ (packageManagerFlags).toLong())
+ )
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
deleted file mode 100644
index 3471070..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 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.0N
- *
- * 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.credentialmanager.ktx
-
-import androidx.activity.result.IntentSenderRequest
-import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
-import com.android.credentialmanager.model.Password
-
-fun Password.getIntentSenderRequest(
- isAutoSelected: Boolean = false
-): IntentSenderRequest {
- val entryIntent = entry.frameworkExtrasIntent
- entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
-
- return IntentSenderRequest.Builder(
- pendingIntent = passwordCredentialEntry.pendingIntent
- ).setFillInIntent(entryIntent).build()
-}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index d4bca2a..f1f1f7c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -16,48 +16,18 @@
package com.android.credentialmanager.mapper
+import android.content.Context
import android.content.Intent
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-import com.android.credentialmanager.factory.fromSlice
import com.android.credentialmanager.ktx.getCredentialProviderDataList
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.ktx.resultReceiver
-import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.ktx.toProviderList
import com.android.credentialmanager.model.Request
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
-fun Intent.toGet(): Request.Get {
- val credentialEntries = mutableListOf<Pair<String, Entry>>()
- for (providerData in getCredentialProviderDataList) {
- for (credentialEntry in providerData.credentialEntries) {
- credentialEntries.add(
- Pair(providerData.providerFlattenedComponentName, credentialEntry)
- )
- }
- }
-
- val passwordEntries = mutableListOf<Password>()
- for ((providerId, entry) in credentialEntries) {
- val slice = fromSlice(entry.slice)
- if (slice is PasswordCredentialEntry) {
- passwordEntries.add(
- Password(
- providerId = providerId,
- entry = entry,
- passwordCredentialEntry = slice
- )
- )
- }
- }
-
+fun Intent.toGet(context: Context): Request.Get {
return Request.Get(
token = requestInfo?.token,
- resultReceiver = this.resultReceiver,
- providers = ImmutableMap.copyOf(
- getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
- ),
- passwordEntries = ImmutableList.copyOf(passwordEntries)
+ resultReceiver = resultReceiver,
+ providerInfos = getCredentialProviderDataList.toProviderList(context)
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
similarity index 93%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
index cc92f60..3f85192 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
enum class CredentialType {
UNKNOWN, PASSKEY, PASSWORD,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
similarity index 92%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
index ee36989..6d59f11 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
import android.app.PendingIntent
import android.content.Intent
-open class BaseEntry (
+open class EntryInfo (
val providerId: String,
val entryKey: String,
val entrySubkey: String,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
deleted file mode 100644
index 2fe4fd5..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2023 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.0N
- *
- * 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.credentialmanager.model
-
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-
-data class Password(
- val providerId: String,
- val entry: Entry,
- val passwordCredentialEntry: PasswordCredentialEntry,
-)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 2289ed7..7636462 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,11 +16,9 @@
package com.android.credentialmanager.model
-import android.credentials.ui.ProviderData
import android.os.IBinder
import android.os.ResultReceiver
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
+import com.android.credentialmanager.model.get.ProviderInfo
/**
* Represents the request made by the CredentialManager API.
@@ -51,8 +49,7 @@
data class Get(
override val token: IBinder?,
val resultReceiver: ResultReceiver?,
- val providers: ImmutableMap<String, ProviderData>,
- val passwordEntries: ImmutableList<Password>,
+ val providerInfos: List<ProviderInfo>,
) : Request(token)
/**
* Request to start the create credentials flow.
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
new file mode 100644
index 0000000..d6189eb
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.creation
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CreateOptionInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val userProviderDisplayName: String,
+ val profileIcon: Drawable?,
+ val passwordCount: Int?,
+ val passkeyCount: Int?,
+ val totalCredentialCount: Int?,
+ val lastUsedTime: Instant,
+ val footerDescription: String?,
+ val allowAutoSelect: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
index ee36989..7ee50d7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
@@ -14,16 +14,23 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.creation
import android.app.PendingIntent
import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
-open class BaseEntry (
- val providerId: String,
- val entryKey: String,
- val entrySubkey: String,
- val pendingIntent: PendingIntent?,
- val fillInIntent: Intent?,
- val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
new file mode 100644
index 0000000..d9eee86
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class ActionEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val icon: Drawable,
+ val subTitle: String?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
new file mode 100644
index 0000000..01c394f
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class AuthenticationEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val providerDisplayName: String,
+ val icon: Drawable,
+ // The entry had been unlocked and turned out to be empty. Used to determine whether to
+ // show "Tap to unlock" or "No sign-in info" for this entry.
+ val isUnlockedAndEmpty: Boolean,
+ // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
+ val isLastUnlocked: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey, entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = false,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
new file mode 100644
index 0000000..9725881
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CredentialEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
+ val credentialType: CredentialType,
+ /** Localized type value of this credential used for display purpose. */
+ val credentialTypeDisplayName: String,
+ val providerDisplayName: String,
+ val userName: String,
+ val displayName: String?,
+ val icon: Drawable?,
+ val shouldTintIcon: Boolean,
+ val lastUsedTimeMillis: Instant?,
+ val isAutoSelectable: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
new file mode 100644
index 0000000..6da4146
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+ /**
+ * Unique id (component name) of this provider.
+ * Not for display purpose - [displayName] should be used for ui rendering.
+ */
+ val id: String,
+ val icon: Drawable,
+ val displayName: String,
+ val credentialEntryList: List<CredentialEntryInfo>,
+ val authenticationEntryList: List<AuthenticationEntryInfo>,
+ val remoteEntry: RemoteEntryInfo?,
+ val actionEntryList: List<ActionEntryInfo>,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
index ee36989..a68bf74 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
@@ -14,16 +14,23 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.get
import android.app.PendingIntent
import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
-open class BaseEntry (
- val providerId: String,
- val entryKey: String,
- val entrySubkey: String,
- val pendingIntent: PendingIntent?,
- val fillInIntent: Intent?,
- val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bce86c4..6c5a984 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -28,7 +28,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
-import com.android.credentialmanager.common.BaseEntry
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
@@ -47,7 +47,7 @@
data class UiState(
val createCredentialUiState: CreateCredentialUiState?,
val getCredentialUiState: GetCredentialUiState?,
- val selectedEntry: BaseEntry? = null,
+ val selectedEntry: EntryInfo? = null,
val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
val dialogState: DialogState = DialogState.ACTIVE,
// True if the UI has one and only one auto selectable entry. Its provider activity will be
@@ -115,12 +115,13 @@
launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val entry = uiState.selectedEntry
- if (entry != null && entry.pendingIntent != null) {
+ val pendingIntent = entry?.pendingIntent
+ if (pendingIntent != null) {
Log.d(Constants.LOG_TAG, "Launching provider activity")
uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
val entryIntent = entry.fillInIntent
entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
- val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+ val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
.setFillInIntent(entryIntent).build()
try {
launcher.launch(intentSenderRequest)
@@ -201,7 +202,7 @@
/**************************************************************************/
/***** Get Flow Callbacks *****/
/**************************************************************************/
- fun getFlowOnEntrySelected(entry: BaseEntry) {
+ fun getFlowOnEntrySelected(entry: EntryInfo) {
Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
uiState = if (entry.pendingIntent != null) {
@@ -363,7 +364,7 @@
)
}
- fun createFlowOnEntrySelected(selectedEntry: BaseEntry) {
+ fun createFlowOnEntrySelected(selectedEntry: EntryInfo) {
val providerId = selectedEntry.providerId
val entryKey = selectedEntry.entryKey
val entrySubkey = selectedEntry.entrySubkey
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f0fa6c5..fc3970d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -16,13 +16,10 @@
package com.android.credentialmanager
-import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
-import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
-import android.credentials.ui.AuthenticationEntry
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
import android.credentials.ui.Entry
@@ -32,36 +29,26 @@
import android.text.TextUtils
import android.util.Log
import com.android.credentialmanager.common.Constants
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
-import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.model.creation.CreateOptionInfo
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
-import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
-import com.android.credentialmanager.getflow.ActionEntryInfo
-import com.android.credentialmanager.getflow.AuthenticationEntryInfo
-import com.android.credentialmanager.getflow.CredentialEntryInfo
-import com.android.credentialmanager.getflow.ProviderInfo
-import com.android.credentialmanager.getflow.RemoteEntryInfo
-import com.android.credentialmanager.getflow.TopBrandingContent
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.ktx.toProviderList
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import androidx.credentials.provider.Action
-import androidx.credentials.provider.AuthenticationAction
import androidx.credentials.provider.CreateEntry
-import androidx.credentials.provider.CredentialEntry
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
import android.credentials.flags.Flags
+import com.android.credentialmanager.getflow.TopBrandingContent
import java.time.Instant
@@ -179,43 +166,7 @@
fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
context: Context,
- ): List<ProviderInfo> {
- val providerList: MutableList<ProviderInfo> = mutableListOf()
- providerDataList.forEach {
- val providerLabelAndIcon = getServiceLabelAndIcon(
- context.packageManager,
- it.providerFlattenedComponentName
- ) ?: return@forEach
- val (providerLabel, providerIcon) = providerLabelAndIcon
- providerList.add(
- ProviderInfo(
- id = it.providerFlattenedComponentName,
- icon = providerIcon,
- displayName = providerLabel,
- credentialEntryList = getCredentialOptionInfoList(
- providerId = it.providerFlattenedComponentName,
- providerLabel = providerLabel,
- credentialEntries = it.credentialEntries,
- context = context
- ),
- authenticationEntryList = getAuthenticationEntryList(
- it.providerFlattenedComponentName,
- providerLabel,
- providerIcon,
- it.authenticationEntries),
- remoteEntry = getRemoteEntry(
- it.providerFlattenedComponentName,
- it.remoteEntry
- ),
- actionEntryList = getActionEntryList(
- it.providerFlattenedComponentName, it.actionChips, providerIcon
- ),
- )
- )
- }
- return providerList
- }
-
+ ): List<ProviderInfo> = providerDataList.toProviderList(context)
fun toRequestDisplayInfo(
requestInfo: RequestInfo?,
context: Context,
@@ -254,178 +205,6 @@
preferTopBrandingContent = preferTopBrandingContent,
)
}
-
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getCredentialOptionInfoList(
- providerId: String,
- providerLabel: String,
- credentialEntries: List<Entry>,
- context: Context,
- ): List<CredentialEntryInfo> {
- val result: MutableList<CredentialEntryInfo> = mutableListOf()
- credentialEntries.forEach {
- val credentialEntry = parseCredentialEntryFromSlice(it.slice)
- when (credentialEntry) {
- is PasswordCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSWORD,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is PublicKeyCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSKEY,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is CustomCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.UNKNOWN,
- credentialTypeDisplayName =
- credentialEntry.typeDisplayName?.toString().orEmpty(),
- userName = credentialEntry.title.toString(),
- displayName = credentialEntry.subtitle?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- else -> Log.d(
- Constants.LOG_TAG,
- "Encountered unrecognized credential entry ${it.slice.spec?.type}"
- )
- }
- }
- return result
- }
-
- /**
- * @hide
- */
- fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
- try {
- when (slice.spec?.type) {
- TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
- TYPE_PUBLIC_KEY_CREDENTIAL -> return PublicKeyCredentialEntry.fromSlice(slice)!!
- else -> return CustomCredentialEntry.fromSlice(slice)!!
- }
- } catch (e: Exception) {
- // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
- // password / passkey parsing attempt.
- return CustomCredentialEntry.fromSlice(slice)
- }
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getAuthenticationEntryList(
- providerId: String,
- providerDisplayName: String,
- providerIcon: Drawable,
- authEntryList: List<AuthenticationEntry>,
- ): List<AuthenticationEntryInfo> {
- val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
- authEntryList.forEach { entry ->
- val structuredAuthEntry =
- AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
-
- val title: String =
- structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
-
- result.add(AuthenticationEntryInfo(
- providerId = providerId,
- entryKey = entry.key,
- entrySubkey = entry.subkey,
- pendingIntent = structuredAuthEntry.pendingIntent,
- fillInIntent = entry.frameworkExtrasIntent,
- title = title,
- providerDisplayName = providerDisplayName,
- icon = providerIcon,
- isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
- isLastUnlocked =
- entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
- ))
- }
- return result
- }
-
- private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
- if (remoteEntry == null) {
- return null
- }
- val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
- ?: return null
- return RemoteEntryInfo(
- providerId = providerId,
- entryKey = remoteEntry.key,
- entrySubkey = remoteEntry.subkey,
- pendingIntent = structuredRemoteEntry.pendingIntent,
- fillInIntent = remoteEntry.frameworkExtrasIntent,
- )
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getActionEntryList(
- providerId: String,
- actionEntries: List<Entry>,
- providerIcon: Drawable,
- ): List<ActionEntryInfo> {
- val result: MutableList<ActionEntryInfo> = mutableListOf()
- actionEntries.forEach {
- val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
- result.add(ActionEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = actionEntryUi.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- title = actionEntryUi.title.toString(),
- icon = providerIcon,
- subTitle = actionEntryUi.subtitle?.toString(),
- ))
- }
- return result
- }
}
}
@@ -686,7 +465,8 @@
val result: MutableList<CreateOptionInfo> = mutableListOf()
creationEntries.forEach {
val createEntry = CreateEntry.fromSlice(it.slice) ?: return@forEach
- result.add(CreateOptionInfo(
+ result.add(
+ CreateOptionInfo(
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
@@ -705,7 +485,8 @@
it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
"SELECT_ALLOWED")
}?.text == "true",
- ))
+ )
+ )
}
return result.sortedWith(
compareByDescending { it.lastUsedTime }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 281696d..20d2f09 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -48,10 +48,11 @@
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.getflow.CredentialEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.getflow.ProviderInfo
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
+import com.android.credentialmanager.ktx.credentialEntry
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -122,8 +123,7 @@
val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
candidateProviderDataList.forEach { provider ->
provider.credentialEntries.forEach { entry ->
- val credentialEntry = GetFlowUtils.parseCredentialEntryFromSlice(entry.slice)
- when (credentialEntry) {
+ when (val credentialEntry = entry.slice.credentialEntry) {
is PasswordCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
@@ -172,11 +172,11 @@
}
private fun processProvidersForAutofillId(
- filLRequest: FillRequest,
- autofillId: AutofillId,
- providerList: List<ProviderInfo>,
- entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ filLRequest: FillRequest,
+ autofillId: AutofillId,
+ providerList: List<ProviderInfo>,
+ entryIconMap: Map<String, Icon>,
+ fillResponseBuilder: FillResponse.Builder
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -200,7 +200,8 @@
providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
- if (pendingIntent == null || primaryEntry.fillInIntent == null) {
+ val fillInIntent = primaryEntry.fillInIntent
+ if (pendingIntent == null || fillInIntent == null) {
// FillInIntent will not be null because autofillId was retrieved from it.
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
@@ -245,7 +246,7 @@
presentationBuilder.build())
.build())
.setAuthentication(pendingIntent.intentSender)
- .setAuthenticationExtras(primaryEntry.fillInIntent.extras)
+ .setAuthenticationExtras(fillInIntent.extras)
.build())
datasetAdded = true
}
@@ -322,8 +323,8 @@
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
+ providerInfo: ProviderInfo,
+ credentialList: List<CredentialEntryInfo>
): ProviderInfo {
return ProviderInfo(
providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index d45b6f6..14a9165 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -48,8 +48,8 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +68,8 @@
import com.android.credentialmanager.common.ui.PasskeyBenefitRow
import com.android.credentialmanager.common.ui.HeadlineText
import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -259,15 +261,15 @@
@Composable
fun MoreOptionsSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- disabledProviderList: List<DisabledProviderInfo>?,
- sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- onBackCreationSelectionButtonSelected: () -> Unit,
- onOptionSelected: (ActiveEntry) -> Unit,
- onDisabledProvidersSelected: () -> Unit,
- onRemoteEntrySelected: (BaseEntry) -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ disabledProviderList: List<DisabledProviderInfo>?,
+ sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ onBackCreationSelectionButtonSelected: () -> Unit,
+ onOptionSelected: (ActiveEntry) -> Unit,
+ onDisabledProvidersSelected: () -> Unit,
+ onRemoteEntrySelected: (EntryInfo) -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard(topAppBar = {
MoreOptionTopAppBar(
@@ -378,14 +380,14 @@
@Composable
fun CreationSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- providerInfo: EnabledProviderInfo,
- createOptionInfo: CreateOptionInfo,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onMoreOptionsSelected: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ providerInfo: EnabledProviderInfo,
+ createOptionInfo: CreateOptionInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onMoreOptionsSelected: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item {
@@ -474,11 +476,11 @@
@Composable
fun ExternalOnlySelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- activeRemoteEntry: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ activeRemoteEntry: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
@@ -575,17 +577,14 @@
@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
- entryInfo: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit
+ entryInfo: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit
) {
Entry(
onClick = { onOptionSelected(entryInfo) },
- iconImageBitmap =
- if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
- entryInfo.profileIcon.toBitmap().asImageBitmap()
- } else {
- requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
- },
+ iconImageBitmap = ((entryInfo as? CreateOptionInfo)?.profileIcon
+ ?: requestDisplayInfo.typeIcon)
+ .toBitmap().asImageBitmap(),
shouldApplyIconImageBitmapTint = !(entryInfo is CreateOptionInfo &&
entryInfo.profileIcon != null),
entryHeadlineText = requestDisplayInfo.title,
@@ -627,32 +626,33 @@
entryThirdLineText =
if (requestDisplayInfo.type == CredentialType.PASSKEY ||
requestDisplayInfo.type == CredentialType.PASSWORD) {
- if (createOptionInfo.passwordCount != null &&
- createOptionInfo.passkeyCount != null
- ) {
+ val passwordCount = createOptionInfo.passwordCount
+ val passkeyCount = createOptionInfo.passkeyCount
+ if (passwordCount != null && passkeyCount != null) {
stringResource(
R.string.more_options_usage_passwords_passkeys,
- createOptionInfo.passwordCount,
- createOptionInfo.passkeyCount
+ passwordCount,
+ passkeyCount
)
- } else if (createOptionInfo.passwordCount != null) {
+ } else if (passwordCount != null) {
stringResource(
R.string.more_options_usage_passwords,
- createOptionInfo.passwordCount
+ passwordCount
)
- } else if (createOptionInfo.passkeyCount != null) {
+ } else if (passkeyCount != null) {
stringResource(
R.string.more_options_usage_passkeys,
- createOptionInfo.passkeyCount
+ passkeyCount
)
} else {
null
}
} else {
- if (createOptionInfo.totalCredentialCount != null) {
+ val totalCredentialCount = createOptionInfo.totalCredentialCount
+ if (totalCredentialCount != null) {
stringResource(
R.string.more_options_usage_credentials,
- createOptionInfo.totalCredentialCount
+ totalCredentialCount
)
} else {
null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index e9e8c2e..8b0ba87 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -16,22 +16,21 @@
package com.android.credentialmanager.createflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
-import java.time.Instant
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
data class CreateCredentialUiState(
- val enabledProviders: List<EnabledProviderInfo>,
- val disabledProviders: List<DisabledProviderInfo>? = null,
- val currentScreenState: CreateScreenState,
- val requestDisplayInfo: RequestDisplayInfo,
- val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- val activeEntry: ActiveEntry? = null,
- val remoteEntry: RemoteInfo? = null,
- val foundCandidateFromUserDefaultProvider: Boolean,
+ val enabledProviders: List<EnabledProviderInfo>,
+ val disabledProviders: List<DisabledProviderInfo>? = null,
+ val currentScreenState: CreateScreenState,
+ val requestDisplayInfo: RequestDisplayInfo,
+ val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ val activeEntry: ActiveEntry? = null,
+ val remoteEntry: RemoteInfo? = null,
+ val foundCandidateFromUserDefaultProvider: Boolean,
)
internal fun isFlowAutoSelectable(
@@ -75,44 +74,6 @@
displayName: String,
) : ProviderInfo(icon, id, displayName)
-class CreateOptionInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val userProviderDisplayName: String,
- val profileIcon: Drawable?,
- val passwordCount: Int?,
- val passkeyCount: Int?,
- val totalCredentialCount: Int?,
- val lastUsedTime: Instant,
- val footerDescription: String?,
- val allowAutoSelect: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class RemoteInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val title: String,
val subtitle: String?,
@@ -131,8 +92,8 @@
* user selects a different entry on the more option page.
*/
data class ActiveEntry (
- val activeProvider: EnabledProviderInfo,
- val activeEntryInfo: BaseEntry,
+ val activeProvider: EnabledProviderInfo,
+ val activeEntryInfo: EntryInfo,
)
/** The name of the current screen. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 72d030b..4ed84b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -48,8 +48,9 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +69,10 @@
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.credentialmanager.userAndDisplayNameForPasskey
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -175,8 +180,8 @@
requestDisplayInfo: RequestDisplayInfo,
providerDisplayInfo: ProviderDisplayInfo,
providerInfoList: List<ProviderInfo>,
- activeEntry: BaseEntry?,
- onEntrySelected: (BaseEntry) -> Unit,
+ activeEntry: EntryInfo?,
+ onEntrySelected: (EntryInfo) -> Unit,
onConfirm: () -> Unit,
onMoreOptionSelected: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -358,7 +363,7 @@
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
onBackButtonClicked: () -> Unit,
onCancel: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -436,7 +441,7 @@
@Composable
fun ActionChips(
providerInfoList: List<ProviderInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
val actionChips = providerInfoList.flatMap { it.actionEntryList }
@@ -460,7 +465,7 @@
@Composable
fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -486,7 +491,7 @@
@Composable
fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -508,7 +513,7 @@
@Composable
fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -532,7 +537,7 @@
@Composable
fun CredentialEntryRow(
credentialEntryInfo: CredentialEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
) {
@@ -571,7 +576,7 @@
@Composable
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
) {
Entry(
@@ -596,7 +601,7 @@
@Composable
fun ActionEntryRow(
actionEntryInfo: ActionEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
) {
ActionEntry(
iconImageBitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 447a9d2..46bebc4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,21 +16,21 @@
package com.android.credentialmanager.getflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.internal.util.Preconditions
-import java.time.Instant
-
data class GetCredentialUiState(
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
- val activeEntry: BaseEntry? = toActiveEntry(providerDisplayInfo),
+ val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
val isNoAccount: Boolean = false,
)
@@ -58,20 +58,6 @@
return null
}
-data class ProviderInfo(
- /**
- * Unique id (component name) of this provider.
- * Not for display purpose - [displayName] should be used for ui rendering.
- */
- val id: String,
- val icon: Drawable,
- val displayName: String,
- val credentialEntryList: List<CredentialEntryInfo>,
- val authenticationEntryList: List<AuthenticationEntryInfo>,
- val remoteEntry: RemoteEntryInfo?,
- val actionEntryList: List<ActionEntryInfo>,
-)
-
/** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping
* by the provider id but instead focuses on structures convenient for display purposes. */
data class ProviderDisplayInfo(
@@ -84,87 +70,6 @@
val remoteEntry: RemoteEntryInfo?
)
-class CredentialEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
- val credentialType: CredentialType,
- /** Localized type value of this credential used for display purpose. */
- val credentialTypeDisplayName: String,
- val providerDisplayName: String,
- val userName: String,
- val displayName: String?,
- val icon: Drawable?,
- val shouldTintIcon: Boolean,
- val lastUsedTimeMillis: Instant?,
- val isAutoSelectable: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class AuthenticationEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val providerDisplayName: String,
- val icon: Drawable,
- // The entry had been unlocked and turned out to be empty. Used to determine whether to
- // show "Tap to unlock" or "No sign-in info" for this entry.
- val isUnlockedAndEmpty: Boolean,
- // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
- val isLastUnlocked: Boolean,
-) : BaseEntry(
- providerId,
- entryKey, entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = false,
-)
-
-class RemoteEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class ActionEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val icon: Drawable,
- val subTitle: String?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val appName: String,
val preferImmediatelyAvailableCredentials: Boolean,
@@ -218,8 +123,8 @@
val remoteEntryList = mutableListOf<RemoteEntryInfo>()
providerInfoList.forEach { providerInfo ->
authenticationEntryList.addAll(providerInfo.authenticationEntryList)
- if (providerInfo.remoteEntry != null) {
- remoteEntryList.add(providerInfo.remoteEntry)
+ providerInfo.remoteEntry?.let {
+ remoteEntryList.add(it)
}
// There can only be at most one remote entry
Preconditions.checkState(remoteEntryList.size <= 1)
@@ -260,11 +165,11 @@
private fun toActiveEntry(
providerDisplayInfo: ProviderDisplayInfo,
-): BaseEntry? {
+): EntryInfo? {
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- var activeEntry: BaseEntry? = null
+ var activeEntry: EntryInfo? = null
if (sortedUserNameToCredentialEntryList
.size == 1 && authenticationEntryList.isEmpty()
) {
@@ -302,17 +207,18 @@
return 1
}
}
-
+ val p0LastUsedTimeMillis = p0.lastUsedTimeMillis
+ val p1LastUsedTimeMillis = p1.lastUsedTimeMillis
// Then order by last used timestamp
- if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
- if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+ if (p0LastUsedTimeMillis != null && p1LastUsedTimeMillis != null) {
+ if (p0LastUsedTimeMillis < p1LastUsedTimeMillis) {
return 1
- } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+ } else if (p0LastUsedTimeMillis > p1LastUsedTimeMillis) {
return -1
}
- } else if (p0.lastUsedTimeMillis != null) {
+ } else if (p0LastUsedTimeMillis != null) {
return -1
- } else if (p1.lastUsedTimeMillis != null) {
+ } else if (p1LastUsedTimeMillis != null) {
return 1
}
return 0
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
index 6ededf3..d8a6019 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -1,29 +1,20 @@
package com.android.credentialmanager.di
-import android.content.Context
-import android.content.pm.PackageManager
import com.android.credentialmanager.client.CredentialManagerClient
import com.android.credentialmanager.client.impl.CredentialManagerClientImpl
+import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-internal object AppModule {
- @Provides
+abstract class AppModule {
+ @Binds
@Singleton
- @JvmStatic
- fun providePackageManager(@ApplicationContext context: Context): PackageManager =
- context.packageManager
-
- @Provides
- @Singleton
- @JvmStatic
- fun provideCredentialManagerClient(packageManager: PackageManager): CredentialManagerClient =
- CredentialManagerClientImpl(packageManager)
+ abstract fun provideCredentialManagerClient(
+ client: CredentialManagerClientImpl
+ ): CredentialManagerClient
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index f2f878e..14b992a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -23,8 +23,8 @@
// TODO: b/301206470 returning a hard coded state for MVP
if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
- return if (providers.size == 1) {
- if (passwordEntries.size == 1) {
+ return if (providerInfos.size == 1) {
+ if (providerInfos.first().credentialEntryList.size == 1) {
CredentialSelectorUiState.Get.SingleProviderSinglePassword
} else {
TODO() // b/301206470 - Implement other get flows
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c28df3e8..b64f581 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -76,7 +76,9 @@
}
SideEffect {
- launcher.launch(state.intentSenderRequest)
+ state.intentSenderRequest?.let {
+ launcher.launch(it)
+ }
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index fb72c54..26bee1f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -26,9 +26,9 @@
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.getIntentSenderRequest
-import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.model.PasswordUiModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -44,7 +44,7 @@
private var initializeCalled = false
private lateinit var requestGet: Request.Get
- private lateinit var password: Password
+ private lateinit var entryInfo: CredentialEntryInfo
private val _uiState =
MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
@@ -63,14 +63,15 @@
_uiState.value = SinglePasswordScreenUiState.Error
} else {
requestGet = request
- if (requestGet.passwordEntries.isEmpty()) {
+
+ if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
Log.d(TAG, "Empty passwordEntries")
_uiState.value = SinglePasswordScreenUiState.Error
} else {
- password = requestGet.passwordEntries.first()
+ entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
_uiState.value = SinglePasswordScreenUiState.Loaded(
PasswordUiModel(
- email = password.passwordCredentialEntry.username.toString(),
+ email = entryInfo.userName,
)
)
}
@@ -84,7 +85,7 @@
fun onOKClick() {
_uiState.value = SinglePasswordScreenUiState.PasswordSelected(
- intentSenderRequest = password.getIntentSenderRequest()
+ intentSenderRequest = entryInfo.getIntentSenderRequest()
)
}
@@ -94,9 +95,9 @@
) {
val userSelectionDialogResult = UserSelectionDialogResult(
requestGet.token,
- password.providerId,
- password.entry.key,
- password.entry.subkey,
+ entryInfo.providerId,
+ entryInfo.entryKey,
+ entryInfo.entrySubkey,
if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
)
credentialManagerClient.sendResult(userSelectionDialogResult)
@@ -108,7 +109,7 @@
data object Idle : SinglePasswordScreenUiState()
data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
data class PasswordSelected(
- val intentSenderRequest: IntentSenderRequest
+ val intentSenderRequest: IntentSenderRequest?
) : SinglePasswordScreenUiState()
data object Cancel : SinglePasswordScreenUiState()
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 38bd7d5..6213b34 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -49,6 +49,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
lint: {
@@ -75,6 +76,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tablet"],
@@ -103,6 +105,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tv"],
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
index 19d74b3..7b17cbd 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -16,8 +16,6 @@
package com.android.packageinstaller;
-import static android.content.Intent.CATEGORY_LAUNCHER;
-
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.Activity;
@@ -47,9 +45,6 @@
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(resultCode, data);
finish();
- if (data != null && data.hasCategory(CATEGORY_LAUNCHER)) {
- startActivity(data);
- }
}
@Override
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index e2107eb..4187058 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -16,12 +16,14 @@
package com.android.packageinstaller;
+import static android.content.pm.Flags.usePiaV2;
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -57,14 +59,21 @@
private final boolean mLocalLOGV = false;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (USE_PIA_V2) {
+ mPackageManager = getPackageManager();
+ if (usePiaV2()) {
+ Log.i(TAG, "Using Pia V2");
+
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.v2.model.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getCallingPackage());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
@@ -74,7 +83,6 @@
finish();
return;
}
- mPackageManager = getPackageManager();
mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index 9af88c3b..215ead3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -120,7 +121,12 @@
Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
- setResult(Activity.RESULT_OK, mLaunchIntent);
+ try {
+ startActivity(mLaunchIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP));
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Log.e(LOG_TAG, "Could not start activity", e);
+ }
finish();
});
} else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 34062a4..ba627e9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -17,8 +17,8 @@
package com.android.packageinstaller;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.content.pm.Flags.usePiaV2;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
@@ -81,9 +81,6 @@
private String mPackageName;
private DialogInfo mDialogInfo;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
public void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -92,7 +89,18 @@
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- if (USE_PIA_V2 && !isTv()) {
+ if (usePiaV2() && !isTv()) {
+ Log.i(TAG, "Using Pia V2");
+
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(
+ new ComponentName(this, com.android.packageinstaller.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ pm.setComponentEnabledSetting(
+ new ComponentName(this,
+ com.android.packageinstaller.v2.model.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(UninstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 4871ef3..010a6ce 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -17,7 +17,6 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
- "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 4b3acbf..e70114f 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -17,5 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget.mainswitch">
-
+ <uses-sdk android:minSdkVersion="21" />
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 2b5fcd8..e6f61a8 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -30,7 +32,6 @@
import androidx.annotation.ColorInt;
-import com.android.settingslib.utils.BuildCompatUtils;
import com.android.settingslib.widget.mainswitch.R;
import java.util.ArrayList;
@@ -72,11 +73,18 @@
LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
- if (!BuildCompatUtils.isAtLeastS()) {
- final TypedArray a = context.obtainStyledAttributes(
- new int[]{android.R.attr.colorAccent});
- mBackgroundActivatedColor = a.getColor(0, 0);
- mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
+ TypedArray a;
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ a = context.obtainStyledAttributes(
+ new int[]{android.R.attr.colorAccent});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ } else {
+ a = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = a.getColor(0, 0);
+ }
a.recycle();
}
@@ -148,7 +156,7 @@
* Set icon space reserved for title
*/
public void setIconSpaceReserved(boolean iconSpaceReserved) {
- if (mTextView != null && !BuildCompatUtils.isAtLeastS()) {
+ if (mTextView != null && (Build.VERSION.SDK_INT < VERSION_CODES.S)) {
LayoutParams params = (LayoutParams) mTextView.getLayoutParams();
int iconSpace = getContext().getResources().getDimensionPixelSize(
R.dimen.settingslib_switchbar_subsettings_margin_start);
@@ -207,7 +215,7 @@
mTextView.setEnabled(enabled);
mSwitch.setEnabled(enabled);
- if (BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.S) {
mFrameView.setEnabled(enabled);
mFrameView.setActivated(isChecked());
}
@@ -222,7 +230,7 @@
}
private void setBackground(boolean isChecked) {
- if (!BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor);
} else {
mFrameView.setActivated(isChecked);
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 80b944f..60bf48c 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -25,11 +25,11 @@
alias(libs.plugins.kotlin.android) apply false
}
-val androidTop : String = File(rootDir, "../../../../..").canonicalPath
+val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.6.0-alpha08"
+ extra["jetpackComposeVersion"] = "1.6.0-beta01"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 4335173..acd90f3 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-alpha10")
+ api("androidx.compose.material3:material3:1.2.0-alpha11")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.5")
+ api("androidx.navigation:navigation-compose:2.7.4")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index 26372b6..c395558 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -17,9 +17,7 @@
package com.android.settingslib.spa.framework.theme
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.ripple.LocalRippleTheme
-import androidx.compose.material.ripple.RippleAlpha
-import androidx.compose.material.ripple.RippleTheme
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -39,7 +37,7 @@
MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) {
CompositionLocalProvider(
LocalColorScheme provides settingsColorScheme(isDarkTheme),
- LocalRippleTheme provides SettingsRippleTheme,
+ LocalContentColor provides MaterialTheme.colorScheme.onSurface,
) {
content()
}
@@ -52,19 +50,3 @@
@ReadOnlyComposable
get() = LocalColorScheme.current
}
-
-private object SettingsRippleTheme : RippleTheme {
- @Composable
- override fun defaultColor() = MaterialTheme.colorScheme.onSurface
-
- @Composable
- override fun rippleAlpha() = RippleAlpha
-}
-
-/** Alpha levels for all content. */
-private val RippleAlpha = RippleAlpha(
- pressedAlpha = 0.48f,
- focusedAlpha = 0.48f,
- draggedAlpha = 0.32f,
- hoveredAlpha = 0.16f,
-)
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 0a2d9fc..e41126f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -140,12 +140,6 @@
}
}
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- setSwitchEnabled(enabled);
- }
-
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public boolean isSwitchEnabled() {
return mEnableSwitch;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 8b0f19d..29ea25e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -22,7 +22,6 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -237,15 +236,13 @@
}
private void updateDisabledState() {
- boolean isEnabled = !(mDisabledByAdmin || mDisabledByAppOps);
if (!(mPreference instanceof RestrictedTopLevelPreference)) {
- mPreference.setEnabled(isEnabled);
+ mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
}
- Drawable icon = mPreference.getIcon();
- if (!isEnabled && icon != null) {
- Utils.convertToGrayscale(icon);
- mPreference.setIcon(icon);
+ if (mPreference instanceof PrimarySwitchPreference) {
+ ((PrimarySwitchPreference) mPreference)
+ .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f03263b..107d5f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -728,14 +728,4 @@
return false;
}
- /**
- * Convert a drawable to grayscale drawable
- */
- public static void convertToGrayscale(@NonNull Drawable drawable) {
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0.0f);
-
- ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
- drawable.setColorFilter(filter);
- }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efed8c3..85e8769 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -235,6 +235,7 @@
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
+ Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.DISABLE_WINDOW_BLURS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.BATTERY_TIP_CONSTANTS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d78038e..7cf562f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -242,184 +242,10 @@
}
filegroup {
- name: "SystemUI-test-fakes",
- srcs: [
- /* Status bar fakes */
- "tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt",
-
- /* QS fakes */
- "tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
- ],
- path: "tests/src",
-}
-
-filegroup {
- name: "SystemUI-tests-robolectric-pilots",
- srcs: [
- /* Keyguard converted tests */
- // data
- "tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt",
- // domain
- "tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt",
- // ui
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
- // Keyguard helper
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt",
- "tests/src/com/android/systemui/dock/DockManagerFake.java",
- "tests/src/com/android/systemui/dump/LogBufferHelper.kt",
- "tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java",
-
- /* Biometric converted tests */
- "tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt",
- "tests/src/com/android/systemui/biometrics/AuthControllerTest.java",
- "tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java",
- "tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt",
- "tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt",
-
- /* Status bar wifi converted tests */
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt",
-
- /* Bouncer UI tests */
- "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java",
- "tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java",
- "tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java",
- "tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt",
-
- /* Communal tests */
- "tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt",
- "tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt",
- "tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt",
-
- /* Dream tests */
- "tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java",
- "tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java",
- "tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java",
- "tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java",
- "tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt",
- "tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt",
- "tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java",
-
- /* Smartspace tests */
- "tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt",
- "tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt",
- "tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt",
- "tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt",
-
- /* Quick Settings new pipeline converted tests */
- "tests/src/com/android/systemui/qs/pipeline/data/**/*Test.kt",
- "tests/src/com/android/systemui/qs/pipeline/domain/**/*Test.kt",
- "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
- "tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
- "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
- "tests/src/com/android/systemui/qs/tiles/impl/**/*.kt",
-
- /* Authentication */
- "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt",
- "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt",
-
- /* Device entry */
- "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt",
- "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt",
-
- /* Bouncer scene */
- "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt",
-
- /* Lockscreen scene */
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt",
-
- /* Shade scene */
- "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt",
- "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt",
-
- /* Quick Settings scene */
- "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt",
-
- /* Flexiglass / Scene framework tests */
- "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt",
- "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt",
- "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt",
- "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt",
- "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt",
- "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt",
- "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt",
-
- ],
- path: "tests/src",
-}
-
-filegroup {
name: "SystemUI-tests-multivalent",
srcs: [
"multivalentTests/src/**/*.kt",
+ "multivalentTests/src/**/*.java",
],
path: "multivalentTests/src",
}
@@ -597,8 +423,6 @@
"tests/robolectric/src/**/*.kt",
"tests/robolectric/src/**/*.java",
":SystemUI-tests-utils",
- ":SystemUI-test-fakes",
- ":SystemUI-tests-robolectric-pilots",
":SystemUI-tests-multivalent",
],
static_libs: [
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0480b9d..0c89a5d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -126,24 +126,6 @@
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
}
],
@@ -170,18 +152,6 @@
"include-annotation": "androidx.test.filters.FlakyTest"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
}
]
}
diff --git a/packages/SystemUI/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS
index e1a7a0f..902ba90 100644
--- a/packages/SystemUI/aconfig/OWNERS
+++ b/packages/SystemUI/aconfig/OWNERS
@@ -1 +1,2 @@
per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
+per-file biometrics_framework.aconfig = file:/services/core/java/com/android/server/biometrics/OWNERS
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
new file mode 100644
index 0000000..5fd3b48
--- /dev/null
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.systemui"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+ name: "bp_talkback"
+ namespace: "biometrics_framework"
+ description: "Adds talkback directional guidance when using UDFPS with biometric prompt"
+ bug: "310044658"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1762065..3e84597 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -164,3 +164,9 @@
bug: "296122467"
}
+flag {
+ name: "record_issue_qs_tile"
+ namespace: "systemui"
+ description: "Replace Record Trace QS Tile with expanded Record Issue QS Tile"
+ bug: "305049544"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 0b13383..eb06889 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -16,12 +16,10 @@
package com.android.systemui.bouncer.ui.composable
+import android.view.ViewTreeObserver
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
@@ -30,46 +28,56 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.core.view.WindowInsetsCompat
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
+ val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+ LaunchedEffect(isTextFieldFocusRequested) {
+ if (isTextFieldFocusRequested) {
+ focusRequester.requestFocus()
+ }
+ }
+ val (isTextFieldFocused, onTextFieldFocusChanged) = remember { mutableStateOf(false) }
+ LaunchedEffect(isTextFieldFocused) {
+ viewModel.onTextFieldFocusChanged(isFocused = isTextFieldFocused)
+ }
+
val password: String by viewModel.password.collectAsState()
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
- val density = LocalDensity.current
- val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
+ val isImeVisible by isSoftwareKeyboardVisible()
LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
DisposableEffect(Unit) {
viewModel.onShown()
-
- // When the UI comes up, request focus on the TextField to bring up the software keyboard.
- focusRequester.requestFocus()
-
onDispose { viewModel.onHidden() }
}
@@ -104,16 +112,39 @@
onDone = { viewModel.onAuthenticateKeyPressed() },
),
modifier =
- Modifier.focusRequester(focusRequester).drawBehind {
- drawLine(
- color = color,
- start = Offset(x = 0f, y = size.height - lineWidthPx),
- end = Offset(size.width, y = size.height - lineWidthPx),
- strokeWidth = lineWidthPx,
- )
- },
+ Modifier.focusRequester(focusRequester)
+ .onFocusChanged { onTextFieldFocusChanged(it.isFocused) }
+ .drawBehind {
+ drawLine(
+ color = color,
+ start = Offset(x = 0f, y = size.height - lineWidthPx),
+ end = Offset(size.width, y = size.height - lineWidthPx),
+ strokeWidth = lineWidthPx,
+ )
+ },
)
Spacer(Modifier.height(100.dp))
}
}
+
+/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
+@Composable
+fun isSoftwareKeyboardVisible(): State<Boolean> {
+ val view = LocalView.current
+ val viewTreeObserver = view.viewTreeObserver
+
+ return produceState(
+ initialValue = false,
+ key1 = viewTreeObserver,
+ ) {
+ val listener =
+ ViewTreeObserver.OnGlobalLayoutListener {
+ value = view.rootWindowInsets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
+ }
+
+ viewTreeObserver.addOnGlobalLayoutListener(listener)
+
+ awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 1e80732..83fb17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -319,10 +319,10 @@
@Test
fun imeHiddenEvent_isTriggered() =
testScope.runTest {
- val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+ val imeHiddenEvent by collectLastValue(underTest.onImeHiddenByUser)
runCurrent()
- underTest.onImeHidden()
+ underTest.onImeHiddenByUser()
runCurrent()
assertThat(imeHiddenEvent).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 63c992b..45c186d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -23,8 +23,6 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
@@ -76,18 +74,4 @@
underTest.onAuthenticateButtonClicked()
assertThat(animateFailure).isFalse()
}
-
- @Test
- fun onImeVisibilityChanged() =
- testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
-
- underTest.onImeVisibilityChanged(true)
- assertThat(onImeHidden).isNull()
-
- underTest.onImeVisibilityChanged(false)
- assertThat(onImeHidden).isNotNull()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
similarity index 65%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 9b1e958..937c703 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -20,7 +20,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -43,7 +45,11 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor = utils.authenticationInteractor()
+ private val authenticationRepository = utils.authenticationRepository
+ private val authenticationInteractor =
+ utils.authenticationInteractor(
+ repository = authenticationRepository,
+ )
private val sceneInteractor = utils.sceneInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
@@ -207,6 +213,101 @@
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ @Test
+ fun onImeVisibilityChanged_false_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_emitsOnImeHiddenByUserEvent() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(2)
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+ underTest.onImeVisibilityChanged(isVisible = true)
+ setThrottling(true)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_initiallyTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusGained_becomesFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLost_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ setThrottling(false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
@@ -226,6 +327,35 @@
switchToScene(SceneKey.Bouncer)
}
+ private suspend fun TestScope.setThrottling(
+ isThrottling: Boolean,
+ failedAttemptCount: Int = 5,
+ ) {
+ if (isThrottling) {
+ repeat(failedAttemptCount) {
+ authenticationRepository.reportAuthenticationAttempt(false)
+ }
+ val remainingTimeMs = 30_000
+ authenticationRepository.setThrottleDuration(remainingTimeMs)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = remainingTimeMs,
+ )
+ )
+ } else {
+ authenticationRepository.reportAuthenticationAttempt(true)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = 0,
+ )
+ )
+ }
+
+ runCurrent()
+ }
+
companion object {
private const val ENTER_YOUR_PASSWORD = "Enter your password"
private const val WRONG_PASSWORD = "Wrong password"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 0004f52..910097e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -56,6 +57,13 @@
)
@Test
+ fun canSwipeToEnter_startsNull() =
+ testScope.runTest {
+ val values by collectValues(underTest.canSwipeToEnter)
+ assertThat(values[0]).isNull()
+ }
+
+ @Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
new file mode 100644
index 0000000..cf076c5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.data.repository
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileRepositoryTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val persister = FakeCustomTileStatePersister()
+
+ private val underTest: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ TileSpec.create(TEST_COMPONENT),
+ persister,
+ testScope.testScheduler,
+ )
+
+ @Test
+ fun persistableTileIsRestoredForUser() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ persister.persistState(TEST_TILE_KEY_2, TEST_TILE_2)
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun notPersistableTileIsNotRestored() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, false)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun emptyPersistedStateIsHandled() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithPersistableTilePersists() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingWithNotPersistableTileDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithTileEmits() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingPeristableWithDefaultsPersists() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingNotPersistableWithDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updatingPeristableWithErrorDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithDefaultsEmits() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun getTileForAnotherUserReturnsNull() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isNull()
+ }
+
+ @Test
+ fun getTilesForAnotherUserEmpty() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithTileForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithTileForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ @Test
+ fun updatingWithDefaultsForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithDefaultsForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+
+ val TEST_USER_1 = UserHandle.of(1)!!
+ val TEST_TILE_1 =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_TILE_KEY_1 = TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier)
+ val TEST_DEFAULTS_1 = CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label)
+
+ val TEST_USER_2 = UserHandle.of(2)!!
+ val TEST_TILE_2 =
+ Tile().apply {
+ label = "test_tile_2"
+ icon = Icon.createWithContentUri("file://test_2")
+ }
+ val TEST_TILE_KEY_2 = TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier)
+ val TEST_DEFAULTS_2 = CustomTileDefaults.Result(TEST_TILE_2.icon, TEST_TILE_2.label)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
new file mode 100644
index 0000000..eebb145
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.domain.interactor
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.text.format.DateUtils
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+
+ private val testScope = TestScope()
+
+ private val defaultsRepository = FakeCustomTileDefaultsRepository()
+ private val customTileStatePersister = FakeCustomTileStatePersister()
+ private val customTileRepository =
+ FakeCustomTileRepository(
+ TEST_TILE_SPEC,
+ customTileStatePersister,
+ testScope.testScheduler,
+ )
+
+ private lateinit var underTest: CustomTileInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ CustomTileInteractor(
+ TEST_USER,
+ defaultsRepository,
+ customTileRepository,
+ tileServiceManager,
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ )
+ }
+
+ @Test
+ fun activeTileIsAvailableAfterRestored() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+
+ underTest.init()
+
+ assertThat(underTest.tile).isEqualTo(TEST_TILE)
+ assertThat(underTest.tiles.first()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ underTest.updateTile(TEST_TILE)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterDefaultsUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ defaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
+ defaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun getTileBeforeInitThrows() = testScope.runTest { underTest.tile }
+
+ @Test
+ fun initSuspendsForActiveTileNotRestoredAndNotUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun initSuspendedForNotActiveTileWithoutUpdates() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+ val TEST_TILE_SPEC = TileSpec.create(TEST_COMPONENT)
+ val TEST_USER = UserHandle.of(1)!!
+ val TEST_TILE =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_DEFAULTS = CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index fab290d..7b2ac90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
@@ -37,7 +35,7 @@
class FlashlightMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
- private val mapper by lazy { FlashlightMapper(context) }
+ private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -58,12 +56,7 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_on,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
@@ -73,12 +66,7 @@
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_off,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 820c056..8791877 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth
import junit.framework.Assert
import org.junit.Test
@@ -37,7 +35,8 @@
class LocationTileMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
- private val mapper by lazy { LocationTileMapper(context) }
+
+ private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -57,9 +56,7 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_on, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
@@ -69,9 +66,7 @@
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_off, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c3294ff..18b7168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -61,7 +62,6 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -273,6 +273,9 @@
}
@Test
+ fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) }
+
+ @Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
emulateUserDrivenTransition(SceneKey.Bouncer)
@@ -336,7 +339,7 @@
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
+ assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue()
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
@@ -775,11 +778,11 @@
private suspend fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
- bouncerViewModel.authMethodViewModel.value?.apply {
+ (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
if (showImeBeforeDismissing) {
- onImeVisibilityChanged(true)
+ it.onImeVisibilityChanged(true)
}
- onImeVisibilityChanged(false)
+ it.onImeVisibilityChanged(false)
runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c4ec56c..3cb97e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -145,6 +145,18 @@
}
@Test
+ fun startsInLockscreenScene() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState()
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceLocks() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
@@ -467,7 +479,7 @@
underTest.start()
runCurrent()
- bouncerInteractor.onImeHidden()
+ bouncerInteractor.onImeHiddenByUser()
runCurrent()
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 9c08f5e..355e75d 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -18,7 +18,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
- <item android:id="@+id/notification_background_color_layer">
+ <item>
<shape>
<solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
</shape>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index af29cad..50241cd 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -111,107 +111,57 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle"
- app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
+ app:layout_constraintBottom_toTopOf="@+id/see_all_button" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/see_all_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_arrow,see_all_text" />
-
- <View
- android:id="@+id/see_all_clickable_row"
+ <Button
+ android:id="@+id/see_all_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/device_list"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" />
-
- <ImageView
- android:id="@+id/ic_arrow"
- android:layout_marginStart="36dp"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:importantForAccessibility="no"
- android:gravity="center_vertical"
- android:src="@drawable/ic_arrow_forward"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/see_all_text"
- app:layout_constraintTop_toBottomOf="@id/device_list" />
-
- <TextView
- android:id="@+id/see_all_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_button"
+ android:drawableStart="@drawable/ic_arrow_forward"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/see_all_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toEndOf="@+id/ic_arrow"
- app:layout_constraintTop_toBottomOf="@id/device_list"
- app:layout_constraintEnd_toEndOf="parent" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/pair_new_device_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_add,pair_new_device_text" />
-
- <View
- android:id="@+id/pair_new_device_clickable_row"
+ <Button
+ android:id="@+id/pair_new_device_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/see_all_text"
- app:layout_constraintBottom_toTopOf="@+id/done_button" />
-
- <ImageView
- android:id="@+id/ic_add"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="36dp"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:src="@drawable/ic_add"
- app:layout_constraintBottom_toTopOf="@id/done_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- android:tint="?android:attr/textColorPrimary" />
-
- <TextView
- android:id="@+id/pair_new_device_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_button"
+ app:layout_constraintBottom_toTopOf="@+id/done_button"
+ android:drawableStart="@drawable/ic_add"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/pair_new_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintStart_toEndOf="@+id/ic_add"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- app:layout_constraintEnd_toEndOf="parent" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
<Button
android:id="@+id/done_button"
@@ -227,7 +177,7 @@
android:maxLines="1"
android:text="@string/inline_done_button"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/pair_new_device_button"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index b704f3c..1edb551 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -71,7 +71,7 @@
private final DisplayTracker mDisplayTracker;
private final AccessibilityLogger mA11yLogger;
- private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
+ private MagnificationConnectionImpl mMagnificationConnectionImpl;
private SysUiState mSysUiState;
@VisibleForTesting
@@ -220,7 +220,7 @@
}
@MainThread
- void setScale(int displayId, float scale) {
+ void setScaleForWindowMagnification(int displayId, float scale) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
@@ -321,37 +321,37 @@
final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
}
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
}
}
@Override
public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
}
}
@Override
public void onMove(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onMove(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onMove(displayId);
}
}
@@ -394,8 +394,8 @@
@Override
public void onMagnifierScale(int displayId, float scale,
boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
mA11yLogger.logThrottled(
@@ -454,8 +454,8 @@
if (magnificationSettingsController != null) {
magnificationSettingsController.closeMagnificationSettings();
}
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
}
}
}
@@ -500,12 +500,12 @@
}
private void setWindowMagnificationConnection() {
- if (mWindowMagnificationConnectionImpl == null) {
- mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
+ if (mMagnificationConnectionImpl == null) {
+ mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
mHandler);
}
mAccessibilityManager.setWindowMagnificationConnection(
- mWindowMagnificationConnectionImpl);
+ mMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index 5666851..5f0d496 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@
*
* @see IWindowMagnificationConnection
*/
-class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
@@ -40,7 +40,7 @@
private final Magnification mMagnification;
private final Handler mHandler;
- WindowMagnificationConnectionImpl(@NonNull Magnification magnification,
+ MagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
mMagnification = magnification;
mHandler = mainHandler;
@@ -57,8 +57,8 @@
}
@Override
- public void setScale(int displayId, float scale) {
- mHandler.post(() -> mMagnification.setScale(displayId, scale));
+ public void setScaleForWindowMagnification(int displayId, float scale) {
+ mHandler.post(() -> mMagnification.setScaleForWindowMagnification(displayId, scale));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 1ac4163..ab23564 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -30,6 +30,7 @@
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -168,7 +169,7 @@
// HAT received from LockSettingsService when credential is verified.
@Nullable private byte[] mCredentialAttestation;
- // TODO(b/287311775): remove when legacy prompt is replaced
+ // TODO(b/313469218): remove when legacy prompt is replaced
@Deprecated
static class Config {
Context mContext;
@@ -220,6 +221,9 @@
mHandler.postDelayed(() -> {
addCredentialView(false /* animatePanel */, true /* animateContents */);
}, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS);
+
+ // TODO(b/313469218): Remove Config
+ mConfig.mPromptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 11a5d8b..3defec5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -485,8 +485,7 @@
): Int =
if (isPendingConfirmation) {
when (sensorType) {
- FingerprintSensorType.POWER_BUTTON ->
- R.string.security_settings_sfps_enroll_find_sensor_message
+ FingerprintSensorType.POWER_BUTTON -> -1
else -> R.string.fingerprint_dialog_authenticated_confirmation
}
} else if (isAuthenticating || isAuthenticated) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b598631..7c46339 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -105,9 +105,9 @@
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
- private val _onImeHidden = MutableSharedFlow<Unit>()
- /** Provide the onImeHidden events from the bouncer */
- val onImeHidden: SharedFlow<Unit> = _onImeHidden
+ private val _onImeHiddenByUser = MutableSharedFlow<Unit>()
+ /** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
+ val onImeHiddenByUser: SharedFlow<Unit> = _onImeHiddenByUser
init {
if (flags.isEnabled()) {
@@ -230,9 +230,9 @@
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** Notifies the interactor that the input method editor has been hidden. */
- suspend fun onImeHidden() {
- _onImeHidden.emit(Unit)
+ /** Notifies that the input method editor (software keyboard) has been hidden by the user. */
+ suspend fun onImeHiddenByUser() {
+ _onImeHiddenByUser.emit(Unit)
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 8024874..e379dab 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -46,9 +46,6 @@
*/
val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
- /** Whether the input method editor (for example, the software keyboard) is visible. */
- private var isImeVisible: Boolean = false
-
/** The authentication method that corresponds to this view model. */
abstract val authenticationMethod: AuthenticationMethodModel
@@ -68,7 +65,7 @@
/**
* Notifies that the UI has been hidden from the user (after any transitions have completed).
*/
- fun onHidden() {
+ open fun onHidden() {
clearInput()
interactor.resetMessage()
}
@@ -79,18 +76,6 @@
}
/**
- * Notifies that the input method editor (for example, the software keyboard) has been shown or
- * hidden.
- */
- suspend fun onImeVisibilityChanged(isVisible: Boolean) {
- if (isImeVisible && !isVisible) {
- interactor.onImeHidden()
- }
-
- isImeVisible = isVisible
- }
-
- /**
* Notifies that the failure animation has been shown. This should be called to consume a `true`
* value in [animateFailure].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index a15698e..45d18128 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -21,8 +21,11 @@
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
@@ -45,6 +48,32 @@
override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+ /** Whether the input method editor (for example, the software keyboard) is visible. */
+ private var isImeVisible: Boolean = false
+
+ /** Whether the text field element currently has focus. */
+ private val isTextFieldFocused = MutableStateFlow(false)
+
+ /** Whether the UI should request focus on the text field element. */
+ val isTextFieldFocusRequested =
+ combine(
+ interactor.isThrottled,
+ isTextFieldFocused,
+ ) { isThrottled, hasFocus ->
+ !isThrottled && !hasFocus
+ }
+ .stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !interactor.isThrottled.value && !isTextFieldFocused.value,
+ )
+
+ override fun onHidden() {
+ super.onHidden()
+ isImeVisible = false
+ isTextFieldFocused.value = false
+ }
+
override fun clearInput() {
_password.value = ""
}
@@ -72,4 +101,21 @@
tryAuthenticate()
}
}
+
+ /**
+ * Notifies that the input method editor (for example, the software keyboard) has been shown or
+ * hidden.
+ */
+ suspend fun onImeVisibilityChanged(isVisible: Boolean) {
+ if (isImeVisible && !isVisible && !interactor.isThrottled.value) {
+ interactor.onImeHiddenByUser()
+ }
+
+ isImeVisible = isVisible
+ }
+
+ /** Notifies that the password text field has gained or lost focus. */
+ fun onTextFieldFocusChanged(isFocused: Boolean) {
+ isTextFieldFocused.value = isFocused
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 715fb17..4cddb9c 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -103,8 +103,11 @@
initialValue = false,
)
- // Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
- private val passivelyAuthenticated =
+ /**
+ * Whether the user is currently authenticated by a TrustAgent like trusted device, location,
+ * etc., or by face auth.
+ */
+ private val isPassivelyAuthenticated =
merge(
trustRepository.isCurrentUserTrusted,
deviceEntryFaceAuthRepository.isAuthenticated,
@@ -117,25 +120,31 @@
* mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
* dismissed.
*
+ * A value of `null` is meaningless and is used as placeholder while the actual value is still
+ * being loaded in the background.
+ *
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
- val canSwipeToEnter =
+ val canSwipeToEnter: StateFlow<Boolean?> =
combine(
// This is true when the user has chosen to show the lockscreen but has not made it
// secure.
authenticationInteractor.authenticationMethod.map {
it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
- passivelyAuthenticated,
+ isPassivelyAuthenticated,
isDeviceEntered
- ) { isSwipeAuthMethod, passivelyAuthenticated, isDeviceEntered ->
- (isSwipeAuthMethod || passivelyAuthenticated) && !isDeviceEntered
+ ) { isSwipeAuthMethod, isPassivelyAuthenticated, isDeviceEntered ->
+ (isSwipeAuthMethod || isPassivelyAuthenticated) && !isDeviceEntered
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ // Starts as null to prevent downstream collectors from falsely assuming that the
+ // user can or cannot swipe to enter the device while the real value is being loaded
+ // from upstream data sources.
+ initialValue = null,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2a0d6a8..0c24752 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -545,28 +545,12 @@
unreleasedFlag("clipboard_shared_transitions", teamfood = true)
/**
- * Whether the scene container (Flexiglass) is enabled. Note that [SCENE_CONTAINER] should be
- * checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can remove
- * unused code from our APK at compile time.
+ * Whether the scene container (Flexiglass) is enabled. Note that SceneContainerFlags#isEnabled
+ * should be checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can
+ * remove unused code from our APK at compile time.
*/
// TODO(b/283300105): Tracking Bug
@JvmField val SCENE_CONTAINER_ENABLED = false
- @Deprecated(
- message = """
- Do not use this flag directly. Please use
- [com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled].
-
- (Not really deprecated but using this as a simple way to bring attention to the above).
- """,
- replaceWith = ReplaceWith(
- "com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled",
- ),
- level = DeprecationLevel.WARNING,
- )
- @JvmField val SCENE_CONTAINER = resourceBooleanFlag(
- R.bool.config_sceneContainerFrameworkEnabled,
- "scene_container",
- )
// 1900
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 6f84d13..59c798b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -287,6 +287,10 @@
return
}
+ if (smartSpaceView != null) {
+ parentView.removeView(smartSpaceView)
+ }
+
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
val topPadding: Int =
@@ -363,12 +367,13 @@
),
)
+ setUpUdfps(previewContext, rootView)
+
disposables.add(
PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
if (keyguardBottomAreaRefactor()) {
setupShortcuts(keyguardRootView)
}
- setUpUdfps(previewContext, rootView)
if (!shouldHideClock) {
setUpClock(previewContext, rootView)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 11db69b..6c930b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -255,6 +255,10 @@
for (QSTile tile : tiles) {
addTile(tile, collapsedView);
}
+ } else {
+ for (QSPanelControllerBase.TileRecord record : mRecords) {
+ record.tile.addCallback(record.callback);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
index a321eef..6f5dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -18,17 +18,19 @@
import android.content.ComponentName
import android.content.Context
+import android.content.SharedPreferences
import android.service.quicksettings.Tile
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
+import javax.inject.Inject
import org.json.JSONException
import org.json.JSONObject
-import javax.inject.Inject
data class TileServiceKey(val componentName: ComponentName, val user: Int) {
private val string = "${componentName.flattenToString()}:$user"
override fun toString() = string
}
+
private const val STATE = "state"
private const val LABEL = "label"
private const val SUBTITLE = "subtitle"
@@ -44,12 +46,7 @@
* It persists the state from a [Tile] necessary to present the view in the same state when
* retrieved, with the exception of the icon.
*/
-class CustomTileStatePersister @Inject constructor(context: Context) {
- companion object {
- private const val FILE_NAME = "custom_tiles_state"
- }
-
- private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+interface CustomTileStatePersister {
/**
* Read the state from [SharedPreferences].
@@ -58,7 +55,31 @@
*
* Any fields that have not been saved will be set to `null`
*/
- fun readState(key: TileServiceKey): Tile? {
+ fun readState(key: TileServiceKey): Tile?
+ /**
+ * Persists the state into [SharedPreferences].
+ *
+ * The implementation does not store fields that are `null` or icons.
+ */
+ fun persistState(key: TileServiceKey, tile: Tile)
+ /**
+ * Removes the state for a given tile, user pair.
+ *
+ * Used when the tile is removed by the user.
+ */
+ fun removeState(key: TileServiceKey)
+}
+
+// TODO(b/299909989) Merge this class into into CustomTileRepository
+class CustomTileStatePersisterImpl @Inject constructor(context: Context) :
+ CustomTileStatePersister {
+ companion object {
+ private const val FILE_NAME = "custom_tiles_state"
+ }
+
+ private val sharedPreferences: SharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+
+ override fun readState(key: TileServiceKey): Tile? {
val state = sharedPreferences.getString(key.toString(), null) ?: return null
return try {
readTileFromString(state)
@@ -68,23 +89,13 @@
}
}
- /**
- * Persists the state into [SharedPreferences].
- *
- * The implementation does not store fields that are `null` or icons.
- */
- fun persistState(key: TileServiceKey, tile: Tile) {
+ override fun persistState(key: TileServiceKey, tile: Tile) {
val state = writeToString(tile)
sharedPreferences.edit().putString(key.toString(), state).apply()
}
- /**
- * Removes the state for a given tile, user pair.
- *
- * Used when the tile is removed by the user.
- */
- fun removeState(key: TileServiceKey) {
+ override fun removeState(key: TileServiceKey) {
sharedPreferences.edit().remove(key.toString()).apply()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 69fe46a..529d684 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -109,7 +109,7 @@
// Only read and modified in main thread (where click events come through).
private int mClickEventId = 0;
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final ArraySet<Callback> mCallbacks = new ArraySet<>();
private final Object mStaleListener = new Object();
protected TState mState;
private TState mTmpState;
@@ -444,9 +444,9 @@
}
private void handleStateChanged() {
- if (mCallbacks.size() != 0) {
+ if (!mCallbacks.isEmpty()) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onStateChanged(mState);
+ mCallbacks.valueAt(i).onStateChanged(mState);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 94137c8..4a34276 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.di
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.CustomTileStatePersisterImpl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
@@ -52,4 +54,7 @@
fun bindQSTileIntentUserInputHandler(
impl: QSTileIntentUserInputHandlerImpl
): QSTileIntentUserInputHandler
+
+ @Binds
+ fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 5bdb592..db3cf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -81,10 +81,8 @@
private lateinit var toggleView: Switch
private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
- private lateinit var seeAllViewGroup: View
- private lateinit var pairNewDeviceViewGroup: View
- private lateinit var seeAllRow: View
- private lateinit var pairNewDeviceRow: View
+ private lateinit var seeAllButton: View
+ private lateinit var pairNewDeviceButton: View
private lateinit var deviceListView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
@@ -99,10 +97,8 @@
toggleView = requireViewById(R.id.bluetooth_toggle)
subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
doneButton = requireViewById(R.id.done_button)
- seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
- pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
- seeAllRow = requireViewById(R.id.see_all_clickable_row)
- pairNewDeviceRow = requireViewById(R.id.pair_new_device_clickable_row)
+ seeAllButton = requireViewById(R.id.see_all_button)
+ pairNewDeviceButton = requireViewById(R.id.pair_new_device_button)
deviceListView = requireViewById<RecyclerView>(R.id.device_list)
setupToggle()
@@ -110,8 +106,8 @@
subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
- seeAllRow.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceRow.setOnClickListener {
+ seeAllButton.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceButton.setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
}
@@ -134,8 +130,8 @@
}
if (isActive) {
deviceItemAdapter.refreshDeviceItemList(deviceItem) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ seeAllButton.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceButton.visibility = if (showPairNewDevice) VISIBLE else GONE
lastUiUpdateMs = systemClock.elapsedRealtime()
lastItemRow = itemRow
logger.logDeviceUiUpdate(lastUiUpdateMs - start)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 34c2aba..5d5e747 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import android.os.Bundle
import android.view.View
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogCuj
@@ -40,6 +39,8 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -63,26 +64,25 @@
private var job: Job? = null
- @VisibleForTesting internal var dialog: BluetoothTileDialog? = null
-
/**
* Shows the dialog.
*
* @param context The context in which the dialog is displayed.
* @param view The view from which the dialog is shown.
*/
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
fun showDialog(context: Context, view: View?) {
- dismissDialog()
-
- var updateDeviceItemJob: Job? = null
- var updateDialogUiJob: Job? = null
+ cancelJob()
job =
coroutineScope.launch(mainDispatcher) {
- dialog = createBluetoothTileDialog(context)
+ var updateDeviceItemJob: Job?
+ var updateDialogUiJob: Job? = null
+ val dialog = createBluetoothTileDialog(context)
+
view?.let {
dialogLaunchAnimator.showFromView(
- dialog!!,
+ dialog,
it,
animateBackgroundBoundsChange = true,
cuj =
@@ -92,9 +92,8 @@
)
)
}
- ?: dialog!!.show()
+ ?: dialog.show()
- updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
@@ -102,7 +101,7 @@
bluetoothStateInteractor.bluetoothStateUpdate
.filterNotNull()
.onEach {
- dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it))
+ dialog.onBluetoothStateUpdated(it, getSubtitleResId(it))
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(
@@ -129,7 +128,7 @@
.onEach {
updateDialogUiJob?.cancel()
updateDialogUiJob = launch {
- dialog?.onDeviceItemUpdated(
+ dialog.onDeviceItemUpdated(
it.take(MAX_DEVICE_ITEM_ENTRY),
showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
@@ -138,15 +137,15 @@
}
.launchIn(this)
- dialog!!
- .bluetoothStateToggle
+ dialog.bluetoothStateToggle
.onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
.launchIn(this)
- dialog!!
- .deviceItemClick
+ dialog.deviceItemClick
.onEach { deviceItemInteractor.updateDeviceItemOnClick(it) }
.launchIn(this)
+
+ produce<Unit> { awaitClose { dialog.cancel() } }
}
}
@@ -161,7 +160,7 @@
logger,
context
)
- .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } }
+ .apply { SystemUIDialog.registerDismissListener(this) { cancelJob() } }
}
override fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) {
@@ -188,15 +187,13 @@
startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
}
- private fun dismissDialog() {
+ private fun cancelJob() {
job?.cancel()
job = null
- dialog?.dismiss()
- dialog = null
}
private fun startSettingsActivity(intent: Intent, view: View) {
- dialog?.run {
+ if (job?.isActive == true) {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
activityStarter.postStartActivityDismissingKeyguard(
intent,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index 76fbf8e..fcd45a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -168,26 +168,30 @@
)
}
- internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
- logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+ internal suspend fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
+ withContext(backgroundDispatcher) {
+ logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
- deviceItem.cachedBluetoothDevice.apply {
- when (deviceItem.type) {
- DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
- }
- DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
- setActive()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
- }
- DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT)
- }
- DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
- connect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ deviceItem.cachedBluetoothDevice.apply {
+ when (deviceItem.type) {
+ DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
+ }
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+ setActive()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
+ }
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(
+ BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT
+ )
+ }
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
+ connect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index c390695b..cfb5442 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.airplane.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,29 +27,27 @@
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(private val context: Context) :
+class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- }
- )!!,
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
new file mode 100644
index 0000000..869f6f32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.commons
+
+import android.service.quicksettings.Tile
+
+fun Tile.copy(): Tile =
+ Tile().also {
+ it.icon = icon
+ it.label = label
+ it.subtitle = subtitle
+ it.contentDescription = contentDescription
+ it.stateDescription = stateDescription
+ it.activityLaunchForClick = activityLaunchForClick
+ it.state = state
+ }
+
+fun Tile.setFrom(otherTile: Tile) {
+ if (otherTile.icon != null) {
+ icon = otherTile.icon
+ }
+ if (otherTile.customLabel != null) {
+ label = otherTile.customLabel
+ }
+ if (otherTile.subtitle != null) {
+ subtitle = otherTile.subtitle
+ }
+ if (otherTile.contentDescription != null) {
+ contentDescription = otherTile.contentDescription
+ }
+ if (otherTile.stateDescription != null) {
+ stateDescription = otherTile.stateDescription
+ }
+ activityLaunchForClick = otherTile.activityLaunchForClick
+ state = otherTile.state
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
new file mode 100644
index 0000000..ca5302e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.data.repository
+
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.commons.setFrom
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository store the [Tile] associated with the custom tile. It lives on [QSTileScope] which
+ * allows it to survive service rebinding. Given that, it provides the last received state when
+ * connected again.
+ */
+interface CustomTileRepository {
+
+ /**
+ * Restores the [Tile] if it's [isPersistable]. Restored [Tile] will be available via [getTile]
+ * (but there is no guarantee that restoration is synchronous) and emitted in [getTiles] for a
+ * corresponding [user].
+ */
+ suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean)
+
+ /** Returns [Tile] updates for a [user]. */
+ fun getTiles(user: UserHandle): Flow<Tile>
+
+ /**
+ * Return current [Tile] for a [user] or null if the [user] doesn't match currently cached one.
+ * Suspending until [getTiles] returns something is a way to wait for this to become available.
+ *
+ * @throws IllegalStateException when there is no current tile.
+ */
+ fun getTile(user: UserHandle): Tile?
+
+ /**
+ * Updates tile with the non-null values from [newTile]. Overwrites the current cache when
+ * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
+ * loaded when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ )
+
+ /**
+ * Updates tile with the values from [defaults]. Overwrites the current cache when [user]
+ * differs from the cached one. [isPersistable] tile will be persisted to be possibly loaded
+ * when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ )
+}
+
+@QSTileScope
+class CustomTileRepositoryImpl
+@Inject
+constructor(
+ private val tileSpec: TileSpec.CustomTileSpec,
+ private val customTileStatePersister: CustomTileStatePersister,
+ @Background private val backgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val tileUpdateMutex = Mutex()
+ private val tileWithUserState =
+ MutableSharedFlow<TileWithUser>(onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1)
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) {
+ if (isPersistable && getCurrentTileWithUser()?.user != user) {
+ withContext(backgroundContext) {
+ customTileStatePersister.readState(user.getKey())?.let {
+ updateWithTile(
+ user,
+ it,
+ true,
+ )
+ }
+ }
+ }
+ }
+
+ override fun getTiles(user: UserHandle): Flow<Tile> =
+ tileWithUserState.filter { it.user == user }.map { it.tile }
+
+ override fun getTile(user: UserHandle): Tile? {
+ val tileWithUser =
+ getCurrentTileWithUser() ?: throw IllegalStateException("Tile is not set")
+ return if (tileWithUser.user == user) {
+ tileWithUser.tile
+ } else {
+ null
+ }
+ }
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = updateTile(user, isPersistable) { setFrom(newTile) }
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) {
+ if (defaults is CustomTileDefaults.Result) {
+ updateTile(user, isPersistable) {
+ // Update the icon if it's not set or is the default icon.
+ val updateIcon = (icon == null || icon.isResourceEqual(defaults.icon))
+ if (updateIcon) {
+ icon = defaults.icon
+ }
+ setDefaultLabel(defaults.label)
+ }
+ }
+ }
+
+ private suspend fun updateTile(
+ user: UserHandle,
+ isPersistable: Boolean,
+ update: Tile.() -> Unit
+ ): Unit =
+ tileUpdateMutex.withLock {
+ val currentTileWithUser = getCurrentTileWithUser()
+ val tileToUpdate =
+ if (currentTileWithUser?.user == user) {
+ currentTileWithUser.tile.copy()
+ } else {
+ Tile()
+ }
+ tileToUpdate.update()
+ if (isPersistable) {
+ withContext(backgroundContext) {
+ customTileStatePersister.persistState(user.getKey(), tileToUpdate)
+ }
+ }
+ tileWithUserState.tryEmit(TileWithUser(user, tileToUpdate))
+ }
+
+ private fun getCurrentTileWithUser(): TileWithUser? = tileWithUserState.replayCache.lastOrNull()
+
+ /** Compare two icons, only works for resources. */
+ private fun Icon.isResourceEqual(icon2: Icon?): Boolean {
+ if (icon2 == null) {
+ return false
+ }
+ if (this === icon2) {
+ return true
+ }
+ if (type != Icon.TYPE_RESOURCE || icon2.type != Icon.TYPE_RESOURCE) {
+ return false
+ }
+ if (resId != icon2.resId) {
+ return false
+ }
+ return resPackage == icon2.resPackage
+ }
+
+ private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier)
+
+ private data class TileWithUser(val user: UserHandle, val tile: Tile)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 83767aa..d956fde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -24,6 +24,8 @@
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
@@ -50,4 +52,6 @@
fun bindCustomTileDefaultsRepository(
impl: CustomTileDefaultsRepositoryImpl
): CustomTileDefaultsRepository
+
+ @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
new file mode 100644
index 0000000..351bba5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.domain.interactor
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/** Manages updates of the [Tile] assigned for the current custom tile. */
+@CustomTileBoundScope
+class CustomTileInteractor
+@Inject
+constructor(
+ private val user: UserHandle,
+ private val defaultsRepository: CustomTileDefaultsRepository,
+ private val customTileRepository: CustomTileRepository,
+ private val tileServiceManager: TileServiceManager,
+ @CustomTileBoundScope private val boundScope: CoroutineScope,
+ @Background private val backgroundContext: CoroutineContext,
+) {
+
+ private val tileUpdates =
+ MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ /** [Tile] updates. [updateTile] to emit a new one. */
+ val tiles: Flow<Tile>
+ get() = customTileRepository.getTiles(user)
+
+ /**
+ * Current [Tile]
+ *
+ * @throws IllegalStateException when the repository stores a tile for another user. This means
+ * the tile hasn't been updated for the current user. Can happen when this is accessed before
+ * [init] returns.
+ */
+ val tile: Tile
+ get() =
+ customTileRepository.getTile(user)
+ ?: throw IllegalStateException("Attempt to get a tile for a wrong user")
+
+ /**
+ * Initializes the repository for the current user. Suspends until it's safe to call [tile]
+ * which needs at least one of the following:
+ * - defaults are loaded;
+ * - receive tile update in [updateTile];
+ * - restoration happened for a persisted tile.
+ */
+ suspend fun init() {
+ launchUpdates()
+ customTileRepository.restoreForTheUserIfNeeded(user, tileServiceManager.isActiveTile)
+ // Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
+ // tile update.
+ customTileRepository.getTiles(user).firstOrNull()
+ }
+
+ private fun launchUpdates() {
+ tileUpdates
+ .onEach {
+ customTileRepository.updateWithTile(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ defaultsRepository
+ .defaults(user)
+ .onEach {
+ customTileRepository.updateWithDefaults(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ }
+
+ /** Updates current [Tile]. Emits a new event in [tiles]. */
+ fun updateTile(newTile: Tile) {
+ tileUpdates.tryEmit(newTile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index b2b22646..881a6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,30 +27,28 @@
import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(private val context: Context) :
+class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 8e53723..7e7034d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,32 +27,30 @@
import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(private val context: Context) :
+class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
- this.label = context.resources.getString(R.string.quick_settings_location_label)
+ this.label = resources.getString(R.string.quick_settings_location_label)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index f9e0b16..23e0cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
+import android.content.res.Resources
import android.service.quicksettings.Tile
import android.view.View
import android.widget.Switch
@@ -46,13 +46,13 @@
companion object {
fun build(
- context: Context,
+ resources: Resources,
config: QSTileUIConfig,
build: Builder.() -> Unit
): QSTileState =
build(
{ Icon.Resource(config.iconRes, null) },
- context.getString(config.labelRes),
+ resources.getString(config.labelRes),
build,
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index f3f9c91..d42fde6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -128,7 +128,7 @@
private fun automaticallySwitchScenes() {
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
- bouncerInteractor.onImeHidden.collectLatest {
+ bouncerInteractor.onImeHiddenByUser.collectLatest {
if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
@@ -146,19 +146,21 @@
isAnySimLocked -> {
switchToScene(
targetSceneKey = SceneKey.Bouncer,
- loggingReason = "Need to authenticate locked sim card."
+ loggingReason = "Need to authenticate locked SIM card."
)
}
- isUnlocked && !canSwipeToEnter -> {
+ isUnlocked && canSwipeToEnter == false -> {
switchToScene(
targetSceneKey = SceneKey.Gone,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device already" +
+ " unlocked and lockscreen doesn't require a swipe to dismiss."
)
}
else -> {
switchToScene(
targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device still locked" +
+ " or lockscreen still requires a swipe to dismiss."
)
}
}
@@ -205,11 +207,17 @@
// when the user is passively authenticated, the false value here
// when the unlock state changes indicates this is an active
// authentication attempt.
- if (isBypassEnabled || !canSwipeToEnter)
- SceneKey.Gone to
- "device has been unlocked on lockscreen with either " +
- "bypass enabled or using an active authentication mechanism"
- else null
+ when {
+ isBypassEnabled ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with bypass" +
+ " enabled"
+ canSwipeToEnter == false ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen using an active" +
+ " authentication mechanism"
+ else -> null
+ }
// Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
@@ -232,7 +240,7 @@
} else {
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- if (isUnlocked && !canSwipeToEnter) {
+ if (isUnlocked && canSwipeToEnter == false) {
switchToScene(
targetSceneKey = SceneKey.Gone,
loggingReason =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index d14ef35..dbb58a3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -16,12 +16,14 @@
package com.android.systemui.scene.shared.flag
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flag
import com.android.systemui.flags.Flags
@@ -29,6 +31,7 @@
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -51,6 +54,7 @@
class SceneContainerFlagsImpl
@AssistedInject
constructor(
+ @Application private val context: Context,
private val featureFlagsClassic: FeatureFlagsClassic,
@Assisted private val isComposeAvailable: Boolean,
) : SceneContainerFlags {
@@ -80,7 +84,11 @@
),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
- listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
+ listOf(
+ ComposeMustBeAvailable(),
+ CompileTimeFlagMustBeEnabled(),
+ ResourceConfigMustBeEnabled()
+ )
override fun isEnabled(): Boolean {
// SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
@@ -146,6 +154,14 @@
}
}
+ private inner class ResourceConfigMustBeEnabled : Requirement {
+ override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"
+
+ override fun isMet(): Boolean {
+ return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index e7481cc..b98093e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -32,13 +32,16 @@
import android.os.Looper;
import android.os.ResultReceiver;
import android.view.Gravity;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
@@ -115,6 +118,17 @@
mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
mAudioSwitch.setChecked(true);
});
+
+ // disable redundant Touch & Hold accessibility action for Switch Access
+ mOptions.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+ mOptions.setLongClickable(false);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 9f4ea27..d13edf0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -58,18 +59,21 @@
private final DelayableExecutor mMainExecutor;
private final AccessibilityManagerWrapper mAccessibilityMgr;
private Runnable mCancelTimeoutRunnable;
+ private final ShadeInteractor mShadeInteractor;
@Inject
public BrightnessDialog(
BrightnessSliderController.Factory brightnessSliderfactory,
BrightnessController.Factory brightnessControllerFactory,
@Main DelayableExecutor mainExecutor,
- AccessibilityManagerWrapper accessibilityMgr
+ AccessibilityManagerWrapper accessibilityMgr,
+ ShadeInteractor shadeInteractor
) {
mToggleSliderFactory = brightnessSliderfactory;
mBrightnessControllerFactory = brightnessControllerFactory;
mMainExecutor = mainExecutor;
mAccessibilityMgr = accessibilityMgr;
+ mShadeInteractor = shadeInteractor;
}
@@ -79,6 +83,10 @@
setWindowAttributes();
setContentView(R.layout.brightness_mirror_container);
setBrightnessDialogViewAttributes();
+
+ if (mShadeInteractor.isQsExpanded().getValue()) {
+ finish();
+ }
}
private void setWindowAttributes() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 2a071de..0065db3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -66,10 +66,10 @@
private fun upDestinationSceneKey(
isUnlocked: Boolean,
- canSwipeToDismiss: Boolean,
+ canSwipeToDismiss: Boolean?,
): SceneKey {
return when {
- canSwipeToDismiss -> SceneKey.Lockscreen
+ canSwipeToDismiss == true -> SceneKey.Lockscreen
isUnlocked -> SceneKey.Gone
else -> SceneKey.Lockscreen
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 1b096b5..c1a630f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -612,7 +612,7 @@
private static KeyboardShortcutMultiMappingGroup getMultiMappingInputShortcuts(
Context context) {
List<ShortcutMultiMappingInfo> shortcutMultiMappingInfoList = Arrays.asList(
- /* Switch input language (next language): Ctrl + Space or Meta + Space */
+ /* Switch input language (next language): Ctrl + Space */
new ShortcutMultiMappingInfo(
context.getString(R.string.input_switch_input_language_next),
null,
@@ -621,14 +621,9 @@
context.getString(
R.string.input_switch_input_language_next),
KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON),
- null),
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(
- R.string.input_switch_input_language_next),
- KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON),
null))),
/* Switch input language (previous language): */
- /* Ctrl + Shift + Space or Meta + Shift + Space */
+ /* Ctrl + Shift + Space */
new ShortcutMultiMappingInfo(
context.getString(R.string.input_switch_input_language_previous),
null,
@@ -638,12 +633,6 @@
R.string.input_switch_input_language_previous),
KeyEvent.KEYCODE_SPACE,
KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON),
- null),
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(
- R.string.input_switch_input_language_previous),
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON),
null)))
);
return new KeyboardShortcutMultiMappingGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 0e83c78..e486457 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -530,16 +530,12 @@
userHandle = mCurrentUserId;
}
if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs setting too early", new Throwable());
- updateUserShowPrivateSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+ return false;
}
if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs dpm override too early", new Throwable());
- updateDpcSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ return false;
}
return mUsersUsersAllowingPrivateNotifications.get(userHandle)
&& mUsersDpcAllowingPrivateNotifications.get(userHandle);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 64f61d9..8eda96f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -27,7 +27,6 @@
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
@@ -43,11 +42,9 @@
* A view that can be used for both the dimmed and normal background of an notification.
*/
public class NotificationBackgroundView extends View implements Dumpable {
- private static final String TAG = "NotificationBackgroundView";
private final boolean mDontModifyCorners;
private Drawable mBackground;
- private Drawable mBackgroundDrawableToTint;
private int mClipTopAmount;
private int mClipBottomAmount;
private int mTintColor;
@@ -134,7 +131,6 @@
unscheduleDrawable(mBackground);
}
mBackground = background;
- mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground);
mRippleColor = null;
mBackground.mutate();
if (mBackground != null) {
@@ -148,46 +144,25 @@
invalidate();
}
- // setCustomBackground should be called from ActivatableNotificationView.initBackground
- // with R.drawable.notification_material_bg, which is a layer-list with a lower layer
- // for the background color (annotated with an ID so we can find it) and an upper layer
- // to blend in the stateful @color/notification_overlay_color.
- //
- // If the notification is tinted, we want to set a tint list on *just that lower layer* that
- // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful
- // tints in the upper layer that make the hovered and pressed states visible.
- //
- // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it.
- private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) {
- if (background == null) {
- return null;
- }
-
- if (!(background instanceof LayerDrawable)) {
- Log.wtf(TAG, "background is not a LayerDrawable: " + background);
- return background;
- }
-
- final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId(
- R.id.notification_background_color_layer);
-
- if (backgroundColorLayer == null) {
- Log.wtf(TAG, "background is missing background color layer: " + background);
- return background;
- }
-
- return backgroundColorLayer;
- }
-
public void setCustomBackground(int drawableResId) {
final Drawable d = mContext.getDrawable(drawableResId);
setCustomBackground(d);
}
public void setTint(int tintColor) {
- mBackgroundDrawableToTint.setTint(tintColor);
- mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ if (tintColor != 0) {
+ ColorStateList stateList = new ColorStateList(new int[][]{
+ new int[]{com.android.internal.R.attr.state_pressed},
+ new int[]{com.android.internal.R.attr.state_hovered},
+ new int[]{}},
+ new int[]{tintColor, tintColor, tintColor}
+ );
+ mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ mBackground.setTintList(stateList);
+ } else {
+ mBackground.setTintList(null);
+ }
mTintColor = tintColor;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 6d8ec44..c615887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -67,6 +67,7 @@
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
+ private boolean mIsSceneContainerVisible = false;
private boolean mShouldAdjustInsets = false;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
@@ -128,11 +129,14 @@
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
if (sceneContainerFlags.isEnabled()) {
javaAdapter.alwaysCollectFlow(
sceneInteractor.get().isVisible(),
+ this::onSceneContainerVisibilityChanged);
+ } else {
+ javaAdapter.alwaysCollectFlow(
+ shadeInteractor.isAnyExpanded(),
this::onShadeOrQsExpanded);
}
@@ -164,6 +168,17 @@
}
}
+ private void onSceneContainerVisibilityChanged(Boolean isVisible) {
+ if (isVisible != mIsSceneContainerVisible) {
+ mIsSceneContainerVisible = isVisible;
+ if (isVisible) {
+ // make sure our state is sensible
+ mForceCollapsedUntilLayout = false;
+ }
+ updateTouchableRegion();
+ }
+ }
+
/**
* Calculates the touch region needed for heads up notifications, taking into consideration
* any existing display cutouts (notch)
@@ -267,6 +282,7 @@
// since we don't want stray touches to go through the light reveal scrim to whatever is
// underneath.
return mIsStatusBarExpanded
+ || mIsSceneContainerVisible
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index bbba19d..87df180 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -73,7 +73,11 @@
/** Callback to be notified about device posture changes. */
interface Callback {
- /** Called when the posture changes. */
+ /**
+ * Called when the posture changes. If there are multiple active displays ("concurrent"),
+ * this will report the physical posture of the device (also known as the base device
+ * state).
+ */
void onPostureChanged(@DevicePostureInt int posture);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 8f1ac81..422aa4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -39,8 +39,11 @@
/** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
@SysUISingleton
public class DevicePostureControllerImpl implements DevicePostureController {
+ /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
+ private static final int COMMON_STATE_USE_BASE_STATE = 1000;
private final List<Callback> mListeners = new ArrayList<>();
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
+ private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
@@ -73,16 +76,32 @@
mDeviceStateToPostureMap.put(deviceState, posture);
}
- deviceStateManager.registerCallback(executor, state -> {
- Assert.isMainThread();
- mCurrentDevicePosture =
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
+ @Override
+ public void onStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentDevicePosture =
+ mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ sendUpdatePosture();
+ }
- ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
+ @Override
+ public void onBaseStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+
+ if (useBaseState()) {
+ sendUpdatePosture();
+ }
+ }
+
+ private void sendUpdatePosture() {
+ ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
l -> {
- l.onPostureChanged(mCurrentDevicePosture);
+ l.onPostureChanged(getDevicePosture());
return Unit.INSTANCE;
});
+ }
});
}
@@ -100,6 +119,14 @@
@Override
public int getDevicePosture() {
- return mCurrentDevicePosture;
+ if (useBaseState()) {
+ return mCurrentBasePosture;
+ } else {
+ return mCurrentDevicePosture;
+ }
+ }
+
+ private boolean useBaseState() {
+ return mCurrentDevicePosture == COMMON_STATE_USE_BASE_STATE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index e576f36..279e5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -16,6 +16,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable {
@@ -24,6 +25,7 @@
boolean isAvailable();
boolean isEnabled();
+ @WeaklyReferencedCallback
public interface FlashlightListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 0531487..9689811 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -20,10 +20,12 @@
import android.hardware.devicestate.DeviceStateManager
import android.os.SystemProperties
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
@@ -64,6 +66,10 @@
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
+ @Provides
+ @UnfoldBgProgressFlag
+ fun unfoldBgProgressFlag() = Flags.unfoldAnimationBackgroundProgress()
+
/** A globally available FoldStateListener that allows one to query the fold state. */
@Provides
@Singleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index d8799e1..4395282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -133,8 +133,8 @@
}
@Test
- public void setScale() throws RemoteException {
- mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mIWindowMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
waitForIdleSync();
verify(mWindowMagnificationController).setScale(3.0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index f4122d5..ea20d29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -338,6 +338,13 @@
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+
+ // Check credential view persists after new attachment
+ container.onAttachedToWindow()
+
+ assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
index a9f8ea0..81d02b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -85,7 +85,7 @@
`when`(sharedPreferences.edit()).thenReturn(editor)
tile = Tile()
- customTileStatePersister = CustomTileStatePersister(mockContext)
+ customTileStatePersister = CustomTileStatePersisterImpl(mockContext)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 3808c7e..313ccb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -228,16 +228,16 @@
showPairNewDevice = true
)
- val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
- val pairNewLayout =
- bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val seeAllButton = bluetoothTileDialog.requireViewById<View>(R.id.see_all_button)
+ val pairNewButton =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_button)
val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(seeAllLayout).isNotNull()
- assertThat(seeAllLayout.visibility).isEqualTo(GONE)
- assertThat(pairNewLayout).isNotNull()
- assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(seeAllButton).isNotNull()
+ assertThat(seeAllButton.visibility).isEqualTo(GONE)
+ assertThat(pairNewButton).isNotNull()
+ assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
assertThat(adapter.itemCount).isEqualTo(1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index fb5dd21..99993f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -113,9 +112,7 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), any())
- assertThat(bluetoothTileDialogViewModel.dialog?.isShowing).isTrue()
verify(uiEventLogger).log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
}
}
@@ -125,7 +122,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -136,7 +132,6 @@
backgroundExecutor.execute {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -147,7 +142,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(deviceItemInteractor).deviceItemUpdate
}
}
@@ -157,7 +151,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(bluetoothStateInteractor).bluetoothStateUpdate
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 4c173cc..e236f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -220,45 +220,57 @@
@Test
fun testUpdateDeviceItemOnClick_connectedMedia_setActive() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).setActive()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).setActive()
+ verify(logger)
+ .logDeviceClick(
+ cachedDevice1.address,
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+ )
+ }
}
@Test
fun testUpdateDeviceItemOnClick_activeMedia_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_connectedOtherDevice_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_saved_connect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).connect()
- verify(logger).logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).connect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ }
}
private fun createFactory(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 5969bd8..0173c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -74,10 +75,15 @@
.forEach { flagToken ->
setFlagsRule.enableFlags(flagToken)
aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
+ overrideResource(
+ R.bool.config_sceneContainerFrameworkEnabled,
+ testCase.isResourceConfigEnabled
+ )
}
underTest =
SceneContainerFlagsImpl(
+ context = context,
featureFlagsClassic = featureFlags,
isComposeAvailable = testCase.isComposeAvailable,
)
@@ -91,13 +97,12 @@
internal data class TestCase(
val isComposeAvailable: Boolean,
val areAllFlagsSet: Boolean,
+ val isResourceConfigEnabled: Boolean,
val expectedEnabled: Boolean,
) {
override fun toString(): String {
- return """
- (compose=$isComposeAvailable + flags=$areAllFlagsSet) -> expected=$expectedEnabled
- """
- .trimIndent()
+ return "(compose=$isComposeAvailable + flags=$areAllFlagsSet) + XML" +
+ " config=$isResourceConfigEnabled -> expected=$expectedEnabled"
}
}
@@ -105,17 +110,20 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun testCases() = buildList {
- repeat(4) { combination ->
- val isComposeAvailable = combination and 0b10 != 0
- val areAllFlagsSet = combination and 0b01 != 0
+ repeat(8) { combination ->
+ val isComposeAvailable = combination and 0b100 != 0
+ val areAllFlagsSet = combination and 0b010 != 0
+ val isResourceConfigEnabled = combination and 0b001 != 0
- val expectedEnabled = isComposeAvailable && areAllFlagsSet
+ val expectedEnabled =
+ isComposeAvailable && areAllFlagsSet && isResourceConfigEnabled
add(
TestCase(
isComposeAvailable = isComposeAvailable,
areAllFlagsSet = areAllFlagsSet,
expectedEnabled = expectedEnabled,
+ isResourceConfigEnabled = isResourceConfigEnabled,
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 6e487cd..88c728f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -28,12 +28,16 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -55,6 +59,8 @@
@Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
@Mock private lateinit var brightnessController: BrightnessController
@Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
+ @Mock private lateinit var shadeInteractorLazy: Lazy<ShadeInteractor>
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
@@ -68,7 +74,8 @@
brightnessSliderControllerFactory,
brightnessControllerFactory,
mainExecutor,
- accessibilityMgr
+ accessibilityMgr,
+ shadeInteractor
)
},
/* initialTouchMode= */ false,
@@ -82,6 +89,8 @@
.thenReturn(brightnessSliderController)
`when`(brightnessSliderController.rootView).thenReturn(View(context))
`when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
+ whenever(shadeInteractorLazy.get()).thenReturn(shadeInteractor)
+ whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
}
@After
@@ -175,13 +184,15 @@
brightnessSliderControllerFactory: BrightnessSliderController.Factory,
brightnessControllerFactory: BrightnessController.Factory,
mainExecutor: DelayableExecutor,
- accessibilityMgr: AccessibilityManagerWrapper
+ accessibilityMgr: AccessibilityManagerWrapper,
+ shadeInteractor: ShadeInteractor
) :
BrightnessDialog(
brightnessSliderControllerFactory,
brightnessControllerFactory,
mainExecutor,
- accessibilityMgr
+ accessibilityMgr,
+ shadeInteractor
) {
private var finishing = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
new file mode 100644
index 0000000..ce47170
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import android.hardware.devicestate.DeviceStateManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+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.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DevicePostureControllerImplTest : SysuiTestCase() {
+ private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
+ private val deviceStateToPostureMapping =
+ arrayOf(
+ "$DEVICE_POSTURE_UNKNOWN:$DEVICE_POSTURE_UNKNOWN",
+ "$DEVICE_POSTURE_CLOSED:$DEVICE_POSTURE_CLOSED",
+ "$DEVICE_POSTURE_HALF_OPENED:$DEVICE_POSTURE_HALF_OPENED",
+ "$DEVICE_POSTURE_OPENED:$DEVICE_POSTURE_OPENED",
+ "$DEVICE_POSTURE_FLIPPED:$DEVICE_POSTURE_FLIPPED",
+ "$useBaseStateDeviceState:1000"
+ )
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
+ @Captor
+ private lateinit var deviceStateCallback: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var testableResources: TestableResources
+ private lateinit var underTest: DevicePostureControllerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mainExecutor = FakeExecutor(FakeSystemClock())
+ testableResources = context.getOrCreateTestableResources()
+ testableResources.addOverride(
+ com.android.internal.R.array.config_device_state_postures,
+ deviceStateToPostureMapping
+ )
+ underTest =
+ DevicePostureControllerImpl(
+ context,
+ deviceStateManager,
+ mainExecutor,
+ )
+ verifyRegistersForDeviceStateCallback()
+ }
+
+ @Test
+ fun testPostureChanged_updates() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+ }
+
+ @Test
+ fun testPostureChanged_useBaseUpdate() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // base state change doesn't change the posture
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // WHEN the display state maps to using the base state, then posture updates
+ deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+ }
+
+ @Test
+ fun baseStateChanges_doesNotUpdatePosture() {
+ var numPostureChanges = 0
+ underTest.addCallback { numPostureChanges++ }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(numPostureChanges).isEqualTo(1)
+
+ // base state changes doesn't send another posture update since the device state isn't
+ // useBaseStateDeviceState
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(numPostureChanges).isEqualTo(1)
+ }
+
+ private fun verifyRegistersForDeviceStateCallback() {
+ verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 0000000..4e61b89
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 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.unfold.progress
+
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.utils.os.FakeHandler
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+ private val wrappedProgressProvider = TestUnfoldTransitionProvider()
+ private val fakeHandler = FakeHandler(Looper.getMainLooper())
+ private val listener = TestUnfoldProgressListener()
+
+ private val progressProvider =
+ MainThreadUnfoldTransitionProgressProvider(fakeHandler, wrappedProgressProvider)
+
+ @Test
+ fun onTransitionStarted_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertStarted()
+ }
+
+ @Test
+ fun onTransitionProgress_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertLastProgress(0.5f)
+ }
+
+ @Test
+ fun onTransitionFinished_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ wrappedProgressProvider.onTransitionFinished()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.ensureTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionFinishing_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ wrappedProgressProvider.onTransitionFinished()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.ensureTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
+ progressProvider.addCallback(listener)
+ progressProvider.removeCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertNotStarted()
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
new file mode 100644
index 0000000..29702eb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.external
+
+import android.service.quicksettings.Tile
+
+class FakeCustomTileStatePersister : CustomTileStatePersister {
+
+ private val tiles: MutableMap<TileServiceKey, Tile> = mutableMapOf()
+
+ override fun readState(key: TileServiceKey): Tile? = tiles[key]
+
+ override fun persistState(key: TileServiceKey, tile: Tile) {
+ tiles[key] = tile
+ }
+
+ override fun removeState(key: TileServiceKey) {
+ tiles.remove(key)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
new file mode 100644
index 0000000..d2351dc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 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.impl.custom
+
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.tiles
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
+
+/**
+ * [Tile]-specific extension for [Truth]. Use [assertThat] or [tiles] to get an instance of this
+ * subject.
+ */
+class TileSubject private constructor(failureMetadata: FailureMetadata, subject: Tile?) :
+ Subject(failureMetadata, subject) {
+
+ private val actual: Tile? = subject
+
+ /** Asserts if the [Tile] fields are the same. */
+ fun isEqualTo(other: Tile?) {
+ if (actual == null) {
+ check("other").that(other).isNull()
+ return
+ } else {
+ check("other").that(other).isNotNull()
+ other ?: return
+ }
+
+ check("icon").that(actual.icon).isEqualTo(other.icon)
+ check("label").that(actual.label).isEqualTo(other.label)
+ check("subtitle").that(actual.subtitle).isEqualTo(other.subtitle)
+ check("contentDescription")
+ .that(actual.contentDescription)
+ .isEqualTo(other.contentDescription)
+ check("stateDescription").that(actual.stateDescription).isEqualTo(other.stateDescription)
+ check("activityLaunchForClick")
+ .that(actual.activityLaunchForClick)
+ .isEqualTo(other.activityLaunchForClick)
+ check("state").that(actual.state).isEqualTo(other.state)
+ }
+
+ companion object {
+
+ /** Returns a factory to be used with [Truth.assertAbout]. */
+ fun tiles(): Factory<TileSubject, Tile?> {
+ return Factory { failureMetadata: FailureMetadata, subject: Tile? ->
+ TileSubject(failureMetadata, subject)
+ }
+ }
+
+ /** Shortcut for `Truth.assertAbout(tiles()).that(tile)`. */
+ fun assertThat(tile: Tile?): TileSubject = Truth.assertAbout(tiles()).that(tile)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
index 13910fd..ccba072 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -19,15 +19,20 @@
import android.content.ComponentName
import android.os.UserHandle
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
private val defaults: MutableMap<DefaultsKey, CustomTileDefaults> = mutableMapOf()
- private val defaultsFlow = MutableSharedFlow<DefaultsRequest>()
+ private val defaultsFlow =
+ MutableSharedFlow<DefaultsRequest>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
val defaultsRequests: List<DefaultsRequest> = mutableDefaultsRequests
@@ -41,7 +46,7 @@
old == new
}
}
- .map { defaults[DefaultsKey(it.user, it.componentName)]!! }
+ .mapNotNull { defaults[DefaultsKey(it.user, it.componentName)] }
override fun requestNewDefaults(
user: UserHandle,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
new file mode 100644
index 0000000..ccf0391
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.impl.custom.data.repository
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+
+class FakeCustomTileRepository(
+ tileSpec: TileSpec.CustomTileSpec,
+ customTileStatePersister: FakeCustomTileStatePersister,
+ testBackgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val realDelegate: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ tileSpec,
+ customTileStatePersister,
+ testBackgroundContext,
+ )
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) =
+ realDelegate.restoreForTheUserIfNeeded(user, isPersistable)
+
+ override fun getTiles(user: UserHandle): Flow<Tile> = realDelegate.getTiles(user)
+
+ override fun getTile(user: UserHandle): Tile? = realDelegate.getTile(user)
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithTile(user, newTile, isPersistable)
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithDefaults(user, defaults, isPersistable)
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 42d31b3..f7fb014 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -19,8 +19,10 @@
import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.dagger.UnfoldBg
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -36,15 +38,18 @@
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
+import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
import javax.inject.Singleton
+import kotlin.jvm.optionals.getOrDefault
@Module(
includes =
[
+ UnfoldFlagsModule::class,
UnfoldSharedInternalModule::class,
UnfoldRotationProviderInternalModule::class,
HingeAngleProviderInternalModule::class,
@@ -69,6 +74,16 @@
fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
}
+@Module
+abstract class UnfoldFlagsModule {
+ /**
+ * Users of the library can bind this boolean to notify whether the progress should be
+ * calculated only in the background (and the main thread provider is generated by posting the
+ * background events in the main handler).
+ */
+ @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean
+}
+
/**
* Needed as methods inside must be public, but their parameters can be internal (and, a public
* method can't have internal parameters). Making the module internal and included in a public one
@@ -87,17 +102,34 @@
fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
foldStateProvider: FoldStateProvider,
@UnfoldMain mainHandler: Handler,
+ mainThreadUnfoldTransitionProgressProviderFactory:
+ MainThreadUnfoldTransitionProgressProvider.Factory,
+ @UnfoldBg bgProvider: Provider<Optional<UnfoldTransitionProgressProvider>>,
+ @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional<Boolean>,
): Optional<UnfoldTransitionProgressProvider> {
- return createOptionalUnfoldTransitionProgressProvider(
- config = config,
- scaleAwareProviderFactory = scaleAwareProviderFactory,
- tracingListener = tracingListener.create("MainThread"),
- physicsBasedUnfoldTransitionProgressProvider =
- physicsBasedUnfoldTransitionProgressProvider,
- fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
- foldStateProvider = foldStateProvider,
- progressHandler = mainHandler,
- )
+ if (unfoldBgProgressFlag.getOrDefault(false)) {
+ // In this case, we wrap the background progress provider
+ val mainThreadProvider: Optional<UnfoldTransitionProgressProvider> =
+ bgProvider.get().map {
+ mainThreadUnfoldTransitionProgressProviderFactory.create(it)
+ }
+ mainThreadProvider.ifPresent {
+ it.addCallback(tracingListener.create("MainThreadFromBgProgress"))
+ }
+ return mainThreadProvider
+ } else {
+ // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched.
+ return createOptionalUnfoldTransitionProgressProvider(
+ config = config,
+ scaleAwareProviderFactory = scaleAwareProviderFactory,
+ tracingListener = tracingListener.create("MainThread"),
+ physicsBasedUnfoldTransitionProgressProvider =
+ physicsBasedUnfoldTransitionProgressProvider,
+ fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+ foldStateProvider = foldStateProvider,
+ progressHandler = mainHandler,
+ )
+ }
}
@Provides
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
similarity index 63%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
index ee36989..0e371fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.systemui.unfold.dagger
-import android.app.PendingIntent
-import android.content.Intent
+import javax.inject.Qualifier
-open class BaseEntry (
- val providerId: String,
- val entryKey: String,
- val entrySubkey: String,
- val pendingIntent: PendingIntent?,
- val fillInIntent: Intent?,
- val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
-)
\ No newline at end of file
+/**
+ * Annotates the boolean representing whether we are calculating progresses in the background.
+ *
+ * Used to allow clients to provide this value, without depending on the flags directly.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBgProgressFlag
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
new file mode 100644
index 0000000..9bdf3d5
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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.unfold.progress
+
+import android.os.Handler
+import androidx.annotation.FloatRange
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldMain
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * [UnfoldTransitionProgressProvider] that forwards all progress to the main thread handler.
+ *
+ * This is needed when progress are calculated in the background, but some listeners need the
+ * callbacks in the main thread.
+ */
+class MainThreadUnfoldTransitionProgressProvider
+@AssistedInject
+constructor(
+ @UnfoldMain private val mainHandler: Handler,
+ @Assisted private val rootProvider: UnfoldTransitionProgressProvider
+) : UnfoldTransitionProgressProvider {
+
+ private val listenerMap = mutableMapOf<TransitionProgressListener, TransitionProgressListener>()
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ assertMainThread()
+ val proxy = TransitionProgressListerProxy(listener)
+ rootProvider.addCallback(proxy)
+ listenerMap[listener] = proxy
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ assertMainThread()
+ val proxy = listenerMap.remove(listener) ?: return
+ rootProvider.removeCallback(proxy)
+ }
+
+ private fun assertMainThread() {
+ check(mainHandler.looper.isCurrentThread) {
+ "Should be called from the main thread, but this is ${Thread.currentThread()}"
+ }
+ }
+
+ override fun destroy() {
+ rootProvider.destroy()
+ }
+
+ inner class TransitionProgressListerProxy(private val listener: TransitionProgressListener) :
+ TransitionProgressListener {
+ override fun onTransitionStarted() {
+ mainHandler.post { listener.onTransitionStarted() }
+ }
+
+ override fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
+ mainHandler.post { listener.onTransitionProgress(progress) }
+ }
+
+ override fun onTransitionFinishing() {
+ mainHandler.post { listener.onTransitionFinishing() }
+ }
+
+ override fun onTransitionFinished() {
+ mainHandler.post { listener.onTransitionFinished() }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a [MainThreadUnfoldTransitionProgressProvider] that wraps the [rootProvider]. */
+ fun create(
+ rootProvider: UnfoldTransitionProgressProvider
+ ): MainThreadUnfoldTransitionProgressProvider
+ }
+}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index c70c171..79bfa44 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -1,5 +1,8 @@
# Ravenwood "policy" file for framework-minus-apex.
+# Keep all AIDL interfaces
+class :aidl stubclass
+
# Collections
class android.util.ArrayMap stubclass
class android.util.ArraySet stubclass
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 74538ac..6cac6a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -668,7 +668,7 @@
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationConnectionManager(), mAms.getTraceManager(),
mAms.getMagnificationController(),
detectControlGestures,
detectTwoFingerTripleTap,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b5e8c84..2eecb4d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -158,10 +158,10 @@
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.MagnificationScaleProvider;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -3442,7 +3442,7 @@
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
- getWindowMagnificationMgr().requestConnection(connect);
+ getMagnificationConnectionManager().requestConnection(connect);
}
/**
@@ -4122,17 +4122,17 @@
mSecurityPolicy.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR_SERVICE);
- getWindowMagnificationMgr().setConnection(connection);
+ getMagnificationConnectionManager().setConnection(connection);
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return WindowMagnificationManager
+ * @return MagnificationManager
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- return mMagnificationController.getWindowMagnificationMgr();
+ return mMagnificationController.getMagnificationConnectionManager();
}
}
@@ -4423,7 +4423,7 @@
pw.println();
}
pw.append("hasWindowMagnificationConnection=").append(
- String.valueOf(getWindowMagnificationMgr().isConnected()));
+ String.valueOf(getMagnificationConnectionManager().isConnected()));
pw.println();
mMagnificationProcessor.dump(pw, getValidDisplayList());
final int userCount = mUserStates.size();
@@ -5144,7 +5144,7 @@
for (int i = 0; i < displays.size(); i++) {
final int displayId = displays.get(i).getDisplayId();
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
}
}
@@ -5580,6 +5580,8 @@
@Override
public void injectInputEventToInputFilter(InputEvent event) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.INJECT_EVENTS,
+ "injectInputEventToInputFilter");
synchronized (mLock) {
final long endMillis =
SystemClock.uptimeMillis() + WAIT_INPUT_FILTER_INSTALL_TIMEOUT_MS;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d31b1ef..e3797c9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -258,6 +258,11 @@
public void logMagnificationTripleTap(boolean enabled) {
AccessibilityStatsLogUtils.logMagnificationTripleTap(enabled);
}
+
+ @Override
+ public void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ AccessibilityStatsLogUtils.logMagnificationTwoFingerTripleTap(enabled);
+ }
};
}
@@ -419,6 +424,7 @@
/** An interface that allows testing magnification log events. */
interface MagnificationLogger {
void logMagnificationTripleTap(boolean enabled);
+ void logMagnificationTwoFingerTripleTap(boolean enabled);
}
interface State {
@@ -987,12 +993,14 @@
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ // Placing multiple fingers before a single finger, because achieving a
+ // multi finger multi tap also means achieving a single finger triple tap
+ onTripleTap(event);
+
} else if (isMultiTapTriggered(3 /* taps */)) {
onTripleTap(/* up */ event);
- } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
- onTripleTap(event);
-
} else if (
// Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
isFingerDown()
@@ -1026,6 +1034,11 @@
mCompletedTapCount++;
mIsTwoFingerCountReached = false;
}
+
+ if (mDetectTwoFingerTripleTap && mCompletedTapCount > 2) {
+ final boolean enabled = !isActivated();
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ }
return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
}
@@ -1037,6 +1050,29 @@
mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
+
+ void transitionToViewportDraggingStateAndClear(MotionEvent down) {
+
+ if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
+ final boolean shortcutTriggered = mShortcutTriggered;
+
+ // Only log the 3tap and hold event
+ if (!shortcutTriggered) {
+ final boolean enabled = !isActivated();
+ if (mCompletedTapCount == 2) {
+ // Two finger triple tap and hold
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ } else {
+ // Triple tap and hold also belongs to triple tap event
+ mMagnificationLogger.logMagnificationTripleTap(enabled);
+ }
+ }
+ clear();
+
+ mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
+ zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
+ transitionTo(mViewportDraggingState);
+ }
}
/**
@@ -1416,8 +1452,6 @@
// Only log the 3tap and hold event
if (!shortcutTriggered) {
- // TODO:(b/309534286): Add metrics for two-finger triple-tap and fix
- // the log two-finger bug before enabling the flag
// Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
similarity index 94%
rename from services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
rename to services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 3ea805b..5a3c070 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -60,19 +60,19 @@
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
- * A class to manipulate window magnification through {@link MagnificationConnectionWrapper}
+ * A class to manipulate magnification through {@link MagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
* SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
* The applied magnification scale is constrained by
* {@link MagnificationScaleProvider#constrainScale(float)}
*/
-public class WindowMagnificationManager implements
+public class MagnificationConnectionManager implements
PanningScalingHandler.MagnificationDelegate,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DBG = false;
- private static final String TAG = "WindowMagnificationMgr";
+ private static final String TAG = "MagnificationConnectionManager";
/**
* Indicate that the magnification window is at the magnification center.
@@ -208,7 +208,7 @@
private final AccessibilityTraceManager mTrace;
private final MagnificationScaleProvider mScaleProvider;
- public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
+ public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback,
AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
mLock = lock;
@@ -1040,7 +1040,7 @@
private float mScale = MagnificationScaleProvider.MIN_SCALE;
private boolean mEnabled;
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
// Records the bounds of window magnification.
private final Rect mBounds = new Rect();
// The magnified bounds on the screen.
@@ -1058,11 +1058,15 @@
"mTrackingTypingFocusSumTime");
private volatile long mTrackingTypingFocusSumTime = 0;
- WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
+ WindowMagnifier(int displayId,
+ MagnificationConnectionManager magnificationConnectionManager) {
mDisplayId = displayId;
- mWindowMagnificationManager = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#enableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition, int id) {
@@ -1072,8 +1076,8 @@
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
- if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, mMagnificationFrameOffsetRatio.x,
+ if (mMagnificationConnectionManager.enableWindowMagnificationInternal(mDisplayId,
+ normScale, centerX, centerY, mMagnificationFrameOffsetRatio.x,
mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
@@ -1096,12 +1100,15 @@
}
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#disableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
if (!mEnabled) {
return false;
}
- if (mWindowMagnificationManager.disableWindowMagnificationInternal(
+ if (mMagnificationConnectionManager.disableWindowMagnificationInternal(
mDisplayId, animationResultCallback)) {
mEnabled = false;
mIdOfLastServiceToControl = INVALID_SERVICE_ID;
@@ -1112,6 +1119,10 @@
return false;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#setScaleInternal should
+ // be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one as
+ // 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void setScale(float scale) {
if (!mEnabled) {
@@ -1119,7 +1130,8 @@
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (Float.compare(mScale, normScale) != 0
- && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
+ && mMagnificationConnectionManager
+ .setScaleForWindowMagnificationInternal(mDisplayId, scale)) {
mScale = normScale;
}
}
@@ -1159,8 +1171,8 @@
}
void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
- if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId)
- && mWindowMagnificationManager.isImeVisible(mDisplayId)
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
+ && mMagnificationConnectionManager.isImeVisible(mDisplayId)
&& trackingTypingFocusEnabled) {
startTrackingTypingFocusRecord();
}
@@ -1206,7 +1218,7 @@
Slog.d(TAG, "stop and log: session duration = " + duration
+ ", elapsed = " + elapsed);
}
- mWindowMagnificationManager.logTrackingTypingFocus(duration);
+ mMagnificationConnectionManager.logTrackingTypingFocus(duration);
mTrackingTypingFocusStartTime = 0;
mTrackingTypingFocusSumTime = 0;
}
@@ -1216,9 +1228,14 @@
return mEnabled;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#moveWindowMagnifierInternal
+ // should be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one
+ // as 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void move(float offsetX, float offsetY) {
- mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY);
+ mMagnificationConnectionManager.moveWindowMagnifierInternal(
+ mDisplayId, offsetX, offsetY);
}
@GuardedBy("mLock")
@@ -1270,8 +1287,10 @@
animationCallback);
}
- private boolean setScaleInternal(int displayId, float scale) {
- return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
+ @GuardedBy("mLock")
+ private boolean setScaleForWindowMagnificationInternal(int displayId, float scale) {
+ return mConnectionWrapper != null
+ && mConnectionWrapper.setScaleForWindowMagnification(displayId, scale);
}
@GuardedBy("mLock")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index f0c44d6..20538f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -82,16 +82,16 @@
return true;
}
- boolean setScale(int displayId, float scale) {
+ boolean setScaleForWindowMagnification(int displayId, float scale) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale);
}
try {
- mConnection.setScale(displayId, scale);
+ mConnection.setScaleForWindowMagnification(displayId, scale);
} catch (RemoteException e) {
if (DBG) {
- Slog.e(TAG, "Error calling setScale()", e);
+ Slog.e(TAG, "Error calling setScaleForWindowMagnification()", e);
}
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index effd873..52e123a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -77,7 +77,7 @@
* <b>Note</b> Updates magnification switch UI when magnification mode transition
* is done and before invoking {@link TransitionCallBack#onResult}.
*/
-public class MagnificationController implements WindowMagnificationManager.Callback,
+public class MagnificationController implements MagnificationConnectionManager.Callback,
MagnificationGestureHandler.Callback,
FullScreenMagnificationController.MagnificationInfoChangedCallback,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
@@ -96,7 +96,7 @@
private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
private final MagnificationScaleProvider mScaleProvider;
private FullScreenMagnificationController mFullScreenMagnificationController;
- private WindowMagnificationManager mWindowMagnificationMgr;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
@@ -164,11 +164,11 @@
@VisibleForTesting
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, FullScreenMagnificationController fullScreenMagnificationController,
- WindowMagnificationManager windowMagnificationManager,
+ MagnificationConnectionManager magnificationConnectionManager,
MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
this(ams, lock, context, scaleProvider, backgroundExecutor);
mFullScreenMagnificationController = fullScreenMagnificationController;
- mWindowMagnificationMgr = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
@Override
@@ -179,10 +179,10 @@
if (updatePersistence) {
getFullScreenMagnificationController().persistScale(displayId);
}
- } else if (getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId)) {
- getWindowMagnificationMgr().setScale(displayId, scale);
+ } else if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
+ getMagnificationConnectionManager().setScale(displayId, scale);
if (updatePersistence) {
- getWindowMagnificationMgr().persistScale(displayId);
+ getMagnificationConnectionManager().persistScale(displayId);
}
}
}
@@ -222,15 +222,15 @@
}
if (showModeSwitchButton) {
- getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+ getMagnificationConnectionManager().showMagnificationButton(displayId, mode);
} else {
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
if (!enableSettingsPanel) {
// Whether the settings panel needs to be shown is controlled in system UI.
// Here, we only guarantee that the settings panel is closed when it is not needed.
- getWindowMagnificationMgr().removeMagnificationSettingsPanel(displayId);
+ getMagnificationConnectionManager().removeMagnificationSettingsPanel(displayId);
}
}
@@ -284,7 +284,8 @@
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float scale = getTargetModeScaleFromCurrentMagnification(displayId, targetMode);
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -295,7 +296,7 @@
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, animationEndCallback);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ magnificationConnectionManager.disableWindowMagnification(displayId, false,
animationEndCallback);
}
}
@@ -340,7 +341,8 @@
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float targetScale = Float.isNaN(config.getScale())
? getTargetModeScaleFromCurrentMagnification(displayId, targetMode)
: config.getScale();
@@ -353,14 +355,15 @@
if (targetMode == MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
if (targetActivated) {
- windowMagnificationMgr.enableWindowMagnification(displayId,
+ magnificationConnectionManager.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
magnificationAnimationCallback, id);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
} else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) {
- windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
+ magnificationConnectionManager.disableWindowMagnification(
+ displayId, false, null);
if (targetActivated) {
if (!screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.register(displayId);
@@ -409,7 +412,7 @@
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
return getFullScreenMagnificationController().getScale(displayId);
} else {
- return getWindowMagnificationMgr().getScale(displayId);
+ return getMagnificationConnectionManager().getScale(displayId);
}
}
@@ -441,7 +444,8 @@
mAccessibilityCallbacksDelegateArray.put(displayId,
getFullScreenMagnificationController());
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
- mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr());
+ mAccessibilityCallbacksDelegateArray.put(
+ displayId, getMagnificationConnectionManager());
} else {
mAccessibilityCallbacksDelegateArray.delete(displayId);
}
@@ -462,13 +466,13 @@
@Override
public void onRequestMagnificationSpec(int displayId, int serviceId) {
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
updateMagnificationUIControls(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
}
- if (windowMagnificationManager != null) {
- mWindowMagnificationMgr.disableWindowMagnification(displayId, false);
+ if (magnificationConnectionManager != null) {
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -491,7 +495,7 @@
setCurrentMagnificationModeAndSwitchDelegate(displayId,
ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
- scale = mWindowMagnificationMgr.getLastActivatedScale(displayId);
+ scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
}
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
}
@@ -507,13 +511,14 @@
public void onSourceBoundsChanged(int displayId, Rect bounds) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) {
// notify sysui the magnification scale changed on window magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
- mUserId, displayId, getWindowMagnificationMgr().getScale(displayId));
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+ mUserId, displayId, getMagnificationConnectionManager().getScale(displayId));
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
- .setActivated(getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId))
- .setScale(getWindowMagnificationMgr().getScale(displayId))
+ .setActivated(
+ getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId))
+ .setScale(getMagnificationConnectionManager().getScale(displayId))
.setCenterX(bounds.exactCenterX())
.setCenterY(bounds.exactCenterY()).build();
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
@@ -525,7 +530,7 @@
@NonNull MagnificationConfig config) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) {
// notify sysui the magnification scale changed on fullscreen magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
mUserId, displayId, config.getScale());
mAms.notifyMagnificationChanged(displayId, region, config);
@@ -548,8 +553,8 @@
synchronized (mLock) {
final boolean fullScreenActivated = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isActivated(displayId);
- final boolean windowEnabled = mWindowMagnificationMgr != null
- && mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ final boolean windowEnabled = mMagnificationConnectionManager != null
+ && mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
final Integer transitionMode = mTransitionModes.get(displayId);
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenActivated)
|| (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
@@ -608,10 +613,10 @@
}
private void disableWindowMagnificationIfNeeded(int displayId) {
- final WindowMagnificationManager windowMagnificationManager =
- getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
if (isActivated(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
- windowMagnificationManager.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -620,7 +625,7 @@
synchronized (mLock) {
mIsImeVisibleArray.put(displayId, shown);
}
- getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown);
+ getMagnificationConnectionManager().onImeWindowVisibilityChanged(displayId, shown);
logMagnificationModeWithImeOnIfNeeded(displayId);
}
@@ -661,7 +666,7 @@
/**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
- * WindowMagnificationManager}.
+ * MagnificationConnectionManager}.
*
* @param userId the currently active user ID
*/
@@ -671,10 +676,10 @@
}
mUserId = userId;
final FullScreenMagnificationController fullMagnificationController;
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
fullMagnificationController = mFullScreenMagnificationController;
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
mAccessibilityCallbacksDelegateArray.clear();
mCurrentMagnificationModeArray.clear();
mLastMagnificationActivatedModeArray.clear();
@@ -685,8 +690,8 @@
if (fullMagnificationController != null) {
fullMagnificationController.resetAllIfNeeded(false);
}
- if (windowMagnificationManager != null) {
- windowMagnificationManager.disableAllWindowMagnifiers();
+ if (magnificationConnectionManager != null) {
+ magnificationConnectionManager.disableAllWindowMagnifiers();
}
}
@@ -700,8 +705,8 @@
if (mFullScreenMagnificationController != null) {
mFullScreenMagnificationController.onDisplayRemoved(displayId);
}
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ if (mMagnificationConnectionManager != null) {
+ mMagnificationConnectionManager.onDisplayRemoved(displayId);
}
mAccessibilityCallbacksDelegateArray.delete(displayId);
mCurrentMagnificationModeArray.delete(displayId);
@@ -728,7 +733,7 @@
* @param enabled Enable the following typing focus feature
*/
public void setMagnificationFollowTypingEnabled(boolean enabled) {
- getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+ getMagnificationConnectionManager().setMagnificationFollowTypingEnabled(enabled);
getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
}
@@ -805,29 +810,29 @@
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return {@link WindowMagnificationManager}.
+ * @return {@link MagnificationConnectionManager}.
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ if (mMagnificationConnectionManager == null) {
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
mLock, this, mAms.getTraceManager(),
mScaleProvider);
}
- return mWindowMagnificationMgr;
+ return mMagnificationConnectionManager;
}
}
private @Nullable PointF getCurrentMagnificationCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- if (mWindowMagnificationMgr == null
- || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
+ if (mMagnificationConnectionManager == null
+ || !mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
- mWindowMagnificationMgr.getCenterY(displayId));
+ mTempPoint.set(mMagnificationConnectionManager.getCenterX(displayId),
+ mMagnificationConnectionManager.getCenterY(displayId));
} else {
if (mFullScreenMagnificationController == null
|| !mFullScreenMagnificationController.isActivated(displayId)) {
@@ -858,10 +863,10 @@
}
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
+ if (mMagnificationConnectionManager == null) {
return false;
}
- isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ isActivated = mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
}
return isActivated;
@@ -980,7 +985,7 @@
mCurrentCenter.x, mCurrentCenter.y, mAnimate,
MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
+ getMagnificationConnectionManager().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null,
MAGNIFICATION_GESTURE_HANDLER_ID);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 5cf2a63..ed8f1ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -84,13 +84,13 @@
.setCenterX(fullScreenMagnificationController.getCenterX(displayId))
.setCenterY(fullScreenMagnificationController.getCenterY(displayId));
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- final WindowMagnificationManager windowMagnificationManager =
- mController.getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ mController.getMagnificationConnectionManager();
builder.setMode(mode)
.setActivated(mController.isActivated(displayId, MAGNIFICATION_MODE_WINDOW))
- .setScale(windowMagnificationManager.getScale(displayId))
- .setCenterX(windowMagnificationManager.getCenterX(displayId))
- .setCenterY(windowMagnificationManager.getCenterY(displayId));
+ .setScale(magnificationConnectionManager.getScale(displayId))
+ .setCenterX(magnificationConnectionManager.getCenterX(displayId))
+ .setCenterY(magnificationConnectionManager.getCenterY(displayId));
} else {
// For undefined mode, set enabled to false
builder.setActivated(false);
@@ -135,12 +135,12 @@
}
} else if (configMode == MAGNIFICATION_MODE_WINDOW) {
if (configActivated) {
- return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
- config.getScale(), config.getCenterX(), config.getCenterY(),
+ return mController.getMagnificationConnectionManager().enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY(),
animate ? STUB_ANIMATION_CALLBACK : null,
id);
} else {
- return mController.getWindowMagnificationMgr()
+ return mController.getMagnificationConnectionManager()
.disableWindowMagnification(displayId, false);
}
}
@@ -256,7 +256,7 @@
if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
} else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ mController.getMagnificationConnectionManager().getMagnificationSourceBounds(displayId,
outRegion);
}
}
@@ -297,8 +297,8 @@
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
- false, animate ? STUB_ANIMATION_CALLBACK : null);
+ return mController.getMagnificationConnectionManager().disableWindowMagnification(
+ displayId, false, animate ? STUB_ANIMATION_CALLBACK : null);
}
return false;
}
@@ -325,19 +325,20 @@
*/
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
- mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
+ mController.getMagnificationConnectionManager().resetAllIfNeeded(connectionId);
}
/**
* {@link FullScreenMagnificationController#isActivated(int)}
- * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
+ * {@link MagnificationConnectionManager#isWindowMagnifierEnabled(int)}
*/
public boolean isMagnifying(int displayId) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().isActivated(displayId);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+ return mController.getMagnificationConnectionManager().isWindowMagnifierEnabled(
+ displayId);
}
return false;
}
@@ -416,22 +417,23 @@
pw.append(" SupportWindowMagnification="
+ mController.supportWindowMagnification()).println();
pw.append(" WindowMagnificationConnectionState="
- + mController.getWindowMagnificationMgr().getConnectionState()).println();
+ + mController.getMagnificationConnectionManager().getConnectionState()).println();
}
private int getIdOfLastServiceToMagnify(int mode, int displayId) {
return (mode == MAGNIFICATION_MODE_FULLSCREEN)
? mController.getFullScreenMagnificationController()
.getIdOfLastServiceToMagnify(displayId)
- : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+ : mController.getMagnificationConnectionManager().getIdOfLastServiceToMagnify(
displayId);
}
private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
int mode) {
if (mode == MAGNIFICATION_MODE_WINDOW) {
- pw.append(" TrackingTypingFocusEnabled=" + mController
- .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
+ pw.append(" TrackingTypingFocusEnabled="
+ + mController.getMagnificationConnectionManager()
+ .isTrackingTypingFocusEnabled(displayId))
.println();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 9455628..6b48d2b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -45,6 +45,7 @@
private static final String TAG = "PanningScalingHandler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // TODO(b/312372035): Revisit the scope of usage of the interface
interface MagnificationDelegate {
boolean processScroll(int displayId, float distanceX, float distanceY);
void setScale(int displayId, float scale);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 36e75118..73c267a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -79,7 +79,7 @@
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
- private final WindowMagnificationManager mWindowMagnificationMgr;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
@VisibleForTesting
final DelegatingState mDelegatingState;
@VisibleForTesting
@@ -101,7 +101,7 @@
private long mTripleTapAndHoldStartedTime = 0;
public WindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap,
@@ -115,7 +115,7 @@
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
}
mContext = context;
- mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationConnectionManager = magnificationConnectionManager;
mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
(event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
policyFlags));
@@ -128,18 +128,18 @@
@Override
public boolean processScroll(int displayId, float distanceX,
float distanceY) {
- return mWindowMagnificationMgr.processScroll(displayId, distanceX,
- distanceY);
+ return mMagnificationConnectionManager.processScroll(
+ displayId, distanceX, distanceY);
}
@Override
public void setScale(int displayId, float scale) {
- mWindowMagnificationMgr.setScale(displayId, scale);
+ mMagnificationConnectionManager.setScale(displayId, scale);
}
@Override
public float getScale(int displayId) {
- return mWindowMagnificationMgr.getScale(displayId);
+ return mMagnificationConnectionManager.getScale(displayId);
}
}));
@@ -167,7 +167,7 @@
Slog.i(mLogTag, "onDestroy(); delayed = "
+ mDetectingState.toString());
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
resetToDetectState();
}
@@ -176,7 +176,7 @@
final Point screenSize = mTempPoint;
getScreenSize(mTempPoint);
toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
private void getScreenSize(Point outSize) {
@@ -190,28 +190,29 @@
}
private void enableWindowMagnifier(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
if (DEBUG_ALL) {
Slog.i(mLogTag, "enableWindowMagnifier :"
+ centerX + ", " + centerY + ", " + windowPosition);
}
final float scale = MathUtils.constrain(
- mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
- mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
- windowPosition);
+ mMagnificationConnectionManager.getPersistedScale(mDisplayId),
+ MIN_SCALE, MAX_SCALE);
+ mMagnificationConnectionManager.enableWindowMagnification(
+ mDisplayId, scale, centerX, centerY, windowPosition);
}
private void disableWindowMagnifier() {
if (DEBUG_ALL) {
Slog.i(mLogTag, "disableWindowMagnifier()");
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, false);
}
private void toggleMagnification(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
- if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)) {
disableWindowMagnifier();
} else {
enableWindowMagnifier(centerX, centerY, windowPosition);
@@ -223,7 +224,7 @@
Slog.i(mLogTag, "onTripleTap()");
}
toggleMagnification(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
@VisibleForTesting
@@ -232,9 +233,9 @@
Slog.i(mLogTag, "onTripleTapAndHold()");
}
mViewportDraggingState.mEnabledBeforeDrag =
- mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
+ mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId);
enableWindowMagnifier(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
transitionTo(mViewportDraggingState);
}
@@ -242,7 +243,7 @@
@VisibleForTesting
void releaseTripleTapAndHold() {
if (!mViewportDraggingState.mEnabledBeforeDrag) {
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
}
transitionTo(mDetectingState);
if (mTripleTapAndHoldStartedTime != 0) {
@@ -331,7 +332,7 @@
@Override
public void onExit() {
mPanningScalingHandler.setEnabled(false);
- mWindowMagnificationMgr.persistScale(mDisplayId);
+ mMagnificationConnectionManager.persistScale(mDisplayId);
clear();
}
@@ -404,7 +405,7 @@
if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
float offsetX = event.getX() - mLastX;
float offsetY = event.getY() - mLastY;
- mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
+ mMagnificationConnectionManager.moveWindowMagnification(mDisplayId, offsetX,
offsetY);
}
mLastX = event.getX();
@@ -522,7 +523,7 @@
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
- return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
+ return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
&& !mDetectSingleFingerTripleTap
&& !(mDetectTwoFingerTripleTap
&& Flags.enableMagnificationMultipleFingerMultipleTapGesture());
@@ -540,7 +541,8 @@
"onGestureDetected : delayedEventQueue = " + delayedEventQueue);
}
if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
- && mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
+ && mMagnificationConnectionManager
+ .pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
onTripleTap(motionEvent);
@@ -584,7 +586,7 @@
+ ", mMagnifiedInteractionState=" + mObservePanningScalingState
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
- + ", mWindowMagnificationMgr=" + mWindowMagnificationMgr
+ + ", mMagnificationConnectionManager=" + mMagnificationConnectionManager
+ ", mDisplayId=" + mDisplayId
+ '}';
}
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 3583a78..a159a5e 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -55,5 +55,15 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsVirtualDevicesCameraTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 4987fbc..45d7314 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -939,7 +939,7 @@
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.registerCamera(cameraConfig);
}
@Override // Binder call
@@ -951,7 +951,19 @@
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.unregisterCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.unregisterCamera(cameraConfig);
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public int getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ super.getVirtualCameraId_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
+ if (mVirtualCameraController == null) {
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
+ }
+ return mVirtualCameraController.getCameraId(cameraConfig);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index d194bb2..9b78ed4 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -460,11 +460,11 @@
synchronized (mVirtualDeviceManagerLock) {
if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
- final long callindId = Binder.clearCallingIdentity();
+ final long callingId = Binder.clearCallingIdentity();
try {
registerCdmAssociationListener();
} finally {
- Binder.restoreCallingIdentity(callindId);
+ Binder.restoreCallingIdentity(callingId);
}
}
mVirtualDevices.put(deviceId, virtualDevice);
@@ -498,13 +498,16 @@
synchronized (mVirtualDeviceManagerLock) {
virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
if (virtualDeviceImpl == null) {
- throw new SecurityException("Invalid VirtualDevice");
+ throw new SecurityException(
+ "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
+ + ")");
}
}
if (virtualDeviceImpl.getOwnerUid() != callingUid) {
throw new SecurityException(
"uid " + callingUid
- + " is not the owner of the supplied VirtualDevice");
+ + " is not the owner of the supplied VirtualDevice (deviceId = "
+ + virtualDevice.getDeviceId() + ")");
}
return virtualDeviceImpl.createVirtualDisplay(
@@ -851,6 +854,11 @@
}
@Override
+ public int getDeviceIdForDisplayId(int displayId) {
+ return mImpl.getDeviceIdForDisplayId(displayId);
+ }
+
+ @Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 06be3f3..d089b05 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -27,14 +27,14 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Set;
+import java.util.Map;
/**
* Manages the registration and removal of virtual camera from the server side.
@@ -47,10 +47,13 @@
private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
private static final String TAG = "VirtualCameraController";
+ private final Object mServiceLock = new Object();
+
+ @GuardedBy("mServiceLock")
@Nullable private IVirtualCameraService mVirtualCameraService;
@GuardedBy("mCameras")
- private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
+ private final Map<IBinder, CameraDescriptor> mCameras = new ArrayMap<>();
public VirtualCameraController() {
connectVirtualCameraService();
@@ -67,19 +70,16 @@
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
- // Try to connect to service if not connected already.
- if (mVirtualCameraService == null) {
- connectVirtualCameraService();
- }
- // Throw exception if we are unable to connect to service.
- if (mVirtualCameraService == null) {
- throw new IllegalStateException("Virtual camera service is not connected.");
- }
+ connectVirtualCameraServiceIfNeeded();
try {
if (registerCameraWithService(cameraConfig)) {
+ CameraDescriptor cameraDescriptor =
+ new CameraDescriptor(cameraConfig);
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ binder.linkToDeath(cameraDescriptor, 0 /* flags */);
synchronized (mCameras) {
- mCameras.add(cameraConfig);
+ mCameras.put(binder, cameraDescriptor);
}
} else {
// TODO(b/310857519): Revisit this to find a better way of indicating failure.
@@ -96,24 +96,44 @@
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
- try {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
+ synchronized (mCameras) {
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ if (!mCameras.containsKey(binder)) {
+ Slog.w(TAG, "Virtual camera was not registered.");
} else {
- mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ mVirtualCameraService.unregisterCamera(binder);
+ }
+ mCameras.remove(binder);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
- synchronized (mCameras) {
- mCameras.remove(cameraConfig);
+ }
+ }
+
+ /** Return the id of the virtual camera with the given config. */
+ public int getCameraId(@NonNull VirtualCameraConfig cameraConfig) {
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder());
}
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@Override
public void binderDied() {
Slog.d(TAG, "Virtual camera service died.");
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
synchronized (mCameras) {
mCameras.clear();
}
@@ -122,32 +142,48 @@
/** Release resources associated with this controller. */
public void close() {
synchronized (mCameras) {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
- } else {
- for (VirtualCameraConfig config : mCameras) {
- try {
- mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
- } catch (RemoteException e) {
- Slog.w(TAG, "close(): Camera failed to be removed on camera "
- + "service.", e);
+ if (!mCameras.isEmpty()) {
+ connectVirtualCameraServiceIfNeeded();
+
+ synchronized (mServiceLock) {
+ for (IBinder binder : mCameras.keySet()) {
+ try {
+ mVirtualCameraService.unregisterCamera(binder);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "close(): Camera failed to be removed on camera "
+ + "service.", e);
+ }
}
}
+ mCameras.clear();
}
- mCameras.clear();
}
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
}
/** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
public void dump(PrintWriter fout, String indent) {
fout.println(indent + "VirtualCameraController:");
indent += indent;
- fout.printf("%sService:%s\n", indent, mVirtualCameraService);
synchronized (mCameras) {
fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
- for (VirtualCameraConfig config : mCameras) {
- fout.printf("%s token: %s\n", indent, config);
+ for (CameraDescriptor descriptor : mCameras.values()) {
+ fout.printf("%s token: %s\n", indent, descriptor.mConfig);
+ }
+ }
+ }
+
+ private void connectVirtualCameraServiceIfNeeded() {
+ synchronized (mServiceLock) {
+ // Try to connect to service if not connected already.
+ if (mVirtualCameraService == null) {
+ connectVirtualCameraService();
+ }
+ // Throw exception if we are unable to connect to service.
+ if (mVirtualCameraService == null) {
+ throw new IllegalStateException("Virtual camera service is not connected.");
}
}
}
@@ -173,7 +209,24 @@
private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
- return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
- serviceConfiguration);
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+ serviceConfiguration);
+ }
+ }
+
+ private final class CameraDescriptor implements IBinder.DeathRecipient {
+
+ private final VirtualCameraConfig mConfig;
+
+ CameraDescriptor(VirtualCameraConfig config) {
+ mConfig = config;
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Virtual camera binder died");
+ unregisterCamera(mConfig);
+ }
}
}
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index fc56511..23c008e 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -27,6 +27,7 @@
import android.content.LocusId;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -134,6 +135,18 @@
@Nullable LocusId locusId, @NonNull IBinder appToken);
/**
+ * Report a user interaction event to UsageStatsManager
+ *
+ * @param pkgName The package for which this user interaction event occurred.
+ * @param userId The user id to which component belongs to.
+ * @param extras The extra details about this user interaction event.
+ * {@link UsageEvents.Event#USER_INTERACTION}
+ * {@link UsageStatsManager#reportUserInteraction(String, int, PersistableBundle)}
+ */
+ public abstract void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras);
+
+ /**
* Prepares the UsageStatsService for shutdown.
*/
public abstract void prepareShutdown();
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 77b6d583..eb3ec24 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1466,8 +1466,11 @@
if (android.security.Flags.binaryTransparencySepolicyHash()) {
byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes(
"/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer());
- String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
- Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ String sepolicyHashEncoded = null;
+ if (sepolicyHash != null) {
+ sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
+ Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ }
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED,
sepolicyHashEncoded, mVbmetaDigest);
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 7fe0682..c258370 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
# Connectivity / Networking
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
new file mode 100644
index 0000000..98039be
--- /dev/null
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ISecurityStateManager;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewUpdateService;
+
+import com.android.internal.R;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SecurityStateManagerService extends ISecurityStateManager.Stub {
+
+ private static final String TAG = "SecurityStateManagerService";
+
+ static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build"
+ + ".security_patch";
+ static final Pattern KERNEL_RELEASE_PATTERN = Pattern.compile("(\\d+\\.\\d+\\.\\d+)("
+ + ".*)");
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public SecurityStateManagerService(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public Bundle getGlobalSecurityState() {
+ Bundle globalSecurityState = new Bundle();
+ globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
+ globalSecurityState.putString(KEY_VENDOR_SPL,
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ String moduleMetadataProviderPackageName =
+ mContext.getString(R.string.config_defaultModuleMetadataProvider);
+ if (!moduleMetadataProviderPackageName.isEmpty()) {
+ globalSecurityState.putString(moduleMetadataProviderPackageName,
+ getSpl(moduleMetadataProviderPackageName));
+ }
+ globalSecurityState.putString(KEY_KERNEL_VERSION, getKernelVersion());
+ addWebViewPackages(globalSecurityState);
+ addSecurityStatePackages(globalSecurityState);
+ return globalSecurityState;
+ }
+
+ private String getSpl(String packageName) {
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ return mPackageManager.getPackageInfo(packageName, 0 /* flags */).versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, TextUtils.formatSimple("Failed to get SPL for package %s.",
+ packageName), e);
+ }
+ }
+ return "";
+ }
+
+ private String getKernelVersion() {
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return "";
+ }
+
+ private void addWebViewPackages(Bundle bundle) {
+ for (WebViewProviderInfo info : WebViewUpdateService.getAllWebViewPackages()) {
+ String packageName = info.packageName;
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+
+ private void addSecurityStatePackages(Bundle bundle) {
+ String[] packageNames;
+ packageNames = mContext.getResources().getStringArray(R.array.config_securityStatePackages);
+ for (String packageName : packageNames) {
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f6835fe..39b8643 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -215,7 +215,7 @@
public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
/** Extended timeout for the system server watchdog. */
- private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000;
+ private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 20 * 1000;
/** Extended timeout for the system server watchdog for vold#partition operation. */
private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
@@ -1235,11 +1235,16 @@
}
}
+ private void extendWatchdogTimeout(String reason) {
+ Watchdog w = Watchdog.getInstance();
+ w.pauseWatchingMonitorsFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ w.pauseWatchingCurrentThreadFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ }
+
private void onUserStopped(int userId) {
Slog.d(TAG, "onUserStopped " + userId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
try {
mVold.onUserStopped(userId);
mStoraged.onUserStopped(userId);
@@ -1322,8 +1327,7 @@
unlockedUsers.add(userId);
}
}
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
for (Integer userId : unlockedUsers) {
try {
mVold.onUserStopped(userId);
@@ -2343,8 +2347,7 @@
try {
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
+ extendWatchdogTimeout("#mount might be slow");
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
public boolean onVolumeChecking(FileDescriptor fd, String path,
@@ -2474,8 +2477,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2493,8 +2495,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2512,8 +2513,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -3622,8 +3622,7 @@
@Override
public ParcelFileDescriptor open() throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
+ extendWatchdogTimeout("#open might be slow");
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
@@ -3636,8 +3635,7 @@
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
+ extendWatchdogTimeout("#openFile might be slow");
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
@@ -3648,8 +3646,7 @@
@Override
public void close() throws Exception {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
+ extendWatchdogTimeout("#close might be slow");
if (mMounted) {
BackgroundThread.getHandler().post(() -> {
try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c718d39..9eb35fd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2096,14 +2096,48 @@
/**
* Send a notification to registrants about the data activity state.
*
+ * @param subId the subscriptionId for the data connection
+ * @param state indicates the latest data activity type
+ * e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
+ *
+ */
+
+ public void notifyDataActivityForSubscriber(int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivity()")) {
+ return;
+ }
+ int phoneId = getPhoneIdFromSubId(subId);
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mDataActivity[phoneId] = state;
+ for (Record r : mRecords) {
+ // Notify by correct subId.
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onDataActivity(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ /**
+ * Send a notification to registrants about the data activity state.
+ *
* @param phoneId the phoneId carrying the data connection
* @param subId the subscriptionId for the data connection
* @param state indicates the latest data activity type
* e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
*
*/
- public void notifyDataActivityForSubscriber(int phoneId, int subId, int state) {
- if (!checkNotifyPermission("notifyDataActivity()" )) {
+ public void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivityWithSlot()")) {
return;
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 003046a..60f087f 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -69,6 +69,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -98,11 +99,17 @@
// applications may not work with a debug build. CTS will fail.
private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000;
+ // This ratio is used to compute the pre-watchdog timeout (2 means that the pre-watchdog timeout
+ // will be half the full timeout).
+ //
+ // The pre-watchdog event is similar to a full watchdog except it does not crash system server.
+ private static final int PRE_WATCHDOG_TIMEOUT_RATIO = 2;
+
// These are temporally ordered: larger values as lateness increases
- private static final int COMPLETED = 0;
- private static final int WAITING = 1;
- private static final int WAITED_HALF = 2;
- private static final int OVERDUE = 3;
+ static final int COMPLETED = 0;
+ static final int WAITING = 1;
+ static final int WAITED_UNTIL_PRE_WATCHDOG = 2;
+ static final int OVERDUE = 3;
// Track watchdog timeout history and break the crash loop if there is.
private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt";
@@ -237,10 +244,8 @@
}
}
- /**
- * Used for checking status of handle threads and scheduling monitor callbacks.
- */
- public final class HandlerChecker implements Runnable {
+ /** Used for checking status of handle threads and scheduling monitor callbacks. */
+ public static class HandlerChecker implements Runnable {
private final Handler mHandler;
private final String mName;
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
@@ -251,11 +256,19 @@
private long mStartTimeMillis;
private int mPauseCount;
private long mPauseEndTimeMillis;
+ private Clock mClock;
+ private Object mLock;
- HandlerChecker(Handler handler, String name) {
+ HandlerChecker(Handler handler, String name, Object lock, Clock clock) {
mHandler = handler;
mName = name;
+ mLock = lock;
mCompleted = true;
+ mClock = clock;
+ }
+
+ HandlerChecker(Handler handler, String name, Object lock) {
+ this(handler, name, lock, SystemClock.uptimeClock());
}
void addMonitorLocked(Monitor monitor) {
@@ -278,11 +291,9 @@
mMonitorQueue.clear();
}
- long nowMillis = SystemClock.uptimeMillis();
- boolean isPaused = mPauseCount > 0
- || (mPauseEndTimeMillis > 0 && mPauseEndTimeMillis < nowMillis);
- if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
- || isPaused) {
+ long nowMillis = mClock.millis();
+ boolean isPaused = mPauseCount > 0 || mPauseEndTimeMillis > nowMillis;
+ if ((mMonitors.size() == 0 && isHandlerPolling()) || isPaused) {
// Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
@@ -305,15 +316,19 @@
mHandler.postAtFrontOfQueue(this);
}
+ boolean isHandlerPolling() {
+ return mHandler.getLooper().getQueue().isPolling();
+ }
+
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
- long latency = SystemClock.uptimeMillis() - mStartTimeMillis;
- if (latency < mWaitMaxMillis / 2) {
+ long latency = mClock.millis() - mStartTimeMillis;
+ if (latency < mWaitMaxMillis / PRE_WATCHDOG_TIMEOUT_RATIO) {
return WAITING;
} else if (latency < mWaitMaxMillis) {
- return WAITED_HALF;
+ return WAITED_UNTIL_PRE_WATCHDOG;
}
}
return OVERDUE;
@@ -334,7 +349,7 @@
} else {
prefix = "Blocked in monitor " + mCurrentMonitor.getClass().getName();
}
- long latencySeconds = (SystemClock.uptimeMillis() - mStartTimeMillis) / 1000;
+ long latencySeconds = (mClock.millis() - mStartTimeMillis) / 1000;
return prefix + " on " + mName + " (" + getThread().getName() + ")"
+ " for " + latencySeconds + "s";
}
@@ -366,10 +381,11 @@
* the given time.
*/
public void pauseForLocked(int pauseMillis, String reason) {
- mPauseEndTimeMillis = SystemClock.uptimeMillis() + pauseMillis;
+ mPauseEndTimeMillis = mClock.millis() + pauseMillis;
// Mark as completed, because there's a chance we called this after the watchog
- // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
- // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+ // want to ensure the next call to #getCompletionStateLocked for this checker returns
+ // 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing of HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause end time: " + mPauseEndTimeMillis);
@@ -379,8 +395,9 @@
public void pauseLocked(String reason) {
mPauseCount++;
// Mark as completed, because there's a chance we called this after the watchog
- // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
- // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+ // want to ensure the next call to #getCompletionStateLocked for this checker returns
+ // 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause count: " + mPauseCount);
@@ -396,6 +413,11 @@
Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
}
}
+
+ @Override
+ public String toString() {
+ return "CheckerHandler for " + mName;
+ }
}
final class RebootRequestReceiver extends BroadcastReceiver {
@@ -445,31 +467,40 @@
ServiceThread t = new ServiceThread("watchdog.monitor",
android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
t.start();
- mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread");
+ mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock);
mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(FgThread.getHandler(), "foreground thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock)));
// Add checker for main thread. We only do a quick check since there
// can be UI running on the thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ new Handler(Looper.getMainLooper()), "main thread", mLock)));
// Add checker for shared UI thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(UiThread.getHandler(), "ui thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(new HandlerChecker(UiThread.getHandler(), "ui thread", mLock)));
// And also check IO thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(IoThread.getHandler(), "i/o thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(new HandlerChecker(IoThread.getHandler(), "i/o thread", mLock)));
// And the display thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(DisplayThread.getHandler(), "display thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(DisplayThread.getHandler(), "display thread", mLock)));
// And the animation thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(AnimationThread.getHandler(), "animation thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ AnimationThread.getHandler(), "animation thread", mLock)));
// And the surface animation thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(SurfaceAnimationThread.getHandler(),
- "surface animation thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ SurfaceAnimationThread.getHandler(),
+ "surface animation thread",
+ mLock)));
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
@@ -609,7 +640,7 @@
public void addThread(Handler thread) {
synchronized (mLock) {
final String name = thread.getLooper().getThread().getName();
- mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name)));
+ mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name, mLock)));
}
}
@@ -617,7 +648,7 @@
synchronized (mLock) {
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(
- withCustomTimeout(new HandlerChecker(thread, name), timeoutMillis));
+ withCustomTimeout(new HandlerChecker(thread, name, mLock), timeoutMillis));
}
}
@@ -797,11 +828,11 @@
String subject = "";
boolean allowRestart = true;
int debuggerWasConnected = 0;
- boolean doWaitedHalfDump = false;
+ boolean doWaitedPreDump = false;
// The value of mWatchdogTimeoutMillis might change while we are executing the loop.
// We store the current value to use a consistent value for all handlers.
final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
- final long checkIntervalMillis = watchdogTimeoutMillis / 2;
+ final long checkIntervalMillis = watchdogTimeoutMillis / PRE_WATCHDOG_TIMEOUT_RATIO;
final ArrayList<Integer> pids;
synchronized (mLock) {
long timeout = checkIntervalMillis;
@@ -848,15 +879,16 @@
} else if (waitState == WAITING) {
// still waiting but within their configured intervals; back off and recheck
continue;
- } else if (waitState == WAITED_HALF) {
+ } else if (waitState == WAITED_UNTIL_PRE_WATCHDOG) {
if (!waitedHalf) {
- Slog.i(TAG, "WAITED_HALF");
+ Slog.i(TAG, "WAITED_UNTIL_PRE_WATCHDOG");
waitedHalf = true;
- // We've waited half, but we'd need to do the stack trace dump w/o the lock.
- blockedCheckers = getCheckersWithStateLocked(WAITED_HALF);
+ // We've waited until the pre-watchdog, but we'd need to do the stack trace
+ // dump w/o the lock.
+ blockedCheckers = getCheckersWithStateLocked(WAITED_UNTIL_PRE_WATCHDOG);
subject = describeCheckersLocked(blockedCheckers);
pids = new ArrayList<>(mInterestingJavaPids);
- doWaitedHalfDump = true;
+ doWaitedPreDump = true;
} else {
continue;
}
@@ -874,12 +906,12 @@
// First collect stack traces from all threads of the system process.
//
// Then, if we reached the full timeout, kill this process so that the system will
- // restart. If we reached half of the timeout, just log some information and continue.
- logWatchog(doWaitedHalfDump, subject, pids);
+ // restart. If we reached pre-watchdog timeout, just log some information and continue.
+ logWatchog(doWaitedPreDump, subject, pids);
- if (doWaitedHalfDump) {
- // We have waited for only half of the timeout, we continue to wait for the duration
- // of the full timeout before killing the process.
+ if (doWaitedPreDump) {
+ // We have waited for only pre-watchdog timeout, we continue to wait for the
+ // duration of the full timeout before killing the process.
continue;
}
@@ -928,8 +960,8 @@
}
}
- private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) {
- // Get critical event log before logging the half watchdog so that it doesn't
+ private void logWatchog(boolean preWatchdog, String subject, ArrayList<Integer> pids) {
+ // Get critical event log before logging the pre-watchdog so that it doesn't
// occur in the log.
String criticalEvents =
CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
@@ -941,7 +973,7 @@
}
final String dropboxTag;
- if (halfWatchdog) {
+ if (preWatchdog) {
dropboxTag = "pre_watchdog";
CriticalEventLog.getInstance().logHalfWatchdog(subject);
FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED);
@@ -971,7 +1003,7 @@
report.append(processCpuTracker.printCurrentState(anrTime, 10));
report.append(tracesFileException.getBuffer());
- if (!halfWatchdog) {
+ if (!preWatchdog) {
// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the
// kernel log
doSysRq('w');
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54c8ed3..b87d02d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,7 +79,6 @@
import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
-import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
@@ -5051,7 +5050,7 @@
REASON_LOCKED_BOOT_COMPLETED);
}
// Send BOOT_COMPLETED if the user is unlocked
- if (StorageManager.isUserKeyUnlocked(app.userId)) {
+ if (StorageManager.isCeStorageUnlocked(app.userId)) {
sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_BOOT_COMPLETED),
REASON_BOOT_COMPLETED);
}
@@ -14150,7 +14149,8 @@
|| action.startsWith("android.intent.action.PACKAGE_")
|| action.startsWith("android.intent.action.UID_")
|| action.startsWith("android.intent.action.EXTERNAL_")
- || action.startsWith("android.bluetooth.")) {
+ || action.startsWith("android.bluetooth.")
+ || action.equals(Intent.ACTION_SHUTDOWN)) {
if (DEBUG_BROADCAST) {
Slog.wtf(TAG,
"System internals registering for " + filter.toLongString()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e656030..1928780 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -3013,7 +3013,7 @@
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies, null);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
@@ -3055,7 +3055,7 @@
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies, null);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckin(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 192fd6f..69e3aaf 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -152,11 +152,13 @@
"mainline_sdk",
"media_audio",
"media_drm",
+ "media_reliability",
"media_tv",
"media_solutions",
"nfc",
"pdf_viewer",
"pixel_audio_android",
+ "pixel_bluetooth",
"pixel_system_sw_touch",
"pixel_watch",
"platform_security",
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d4b72c1..5d47e35 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4600,7 +4600,7 @@
}
private void setStreamVolume(int streamType, int index, int flags,
- @NonNull AudioDeviceAttributes ada,
+ @Nullable AudioDeviceAttributes ada,
String callingPackage, String caller, String attributionTag, int uid,
boolean hasModifyAudioSettings,
boolean canChangeMuteAndUpdateController) {
@@ -4618,7 +4618,9 @@
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
- final int device = ada.getInternalType();
+ final int device = (ada == null)
+ ? getDeviceForStream(streamType)
+ : ada.getInternalType();
int oldIndex;
// skip a2dp absolute volume control request when the device
@@ -10665,8 +10667,8 @@
/** @see LoudnessCodecConfigurator#addMediaCodec(MediaCodec) */
@Override
- public void addLoudnessCodecInfo(int piid, LoudnessCodecInfo codecInfo) {
- mLoudnessCodecHelper.addLoudnessCodecInfo(piid, codecInfo);
+ public void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo codecInfo) {
+ mLoudnessCodecHelper.addLoudnessCodecInfo(piid, mediaCodecHash, codecInfo);
}
/** @see LoudnessCodecConfigurator#removeMediaCodec(MediaCodec) */
@@ -11370,6 +11372,8 @@
static final int LOG_NB_EVENTS_SPATIAL = 30;
static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
+ static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
+
static final EventLogger
sLifecycleLogger = new EventLogger(LOG_NB_EVENTS_LIFECYCLE,
"audio services lifecycle");
@@ -11569,6 +11573,10 @@
mSpatializerHelper.dump(pw);
sSpatialLogger.dump(pw);
+ pw.println("\n");
+ pw.println("\nLoudness alignment:");
+ mLoudnessCodecHelper.dump(pw);
+
mAudioSystem.dump(pw);
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index f69b9f6..de89011 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -622,6 +622,55 @@
}
}
+ static final class LoudnessEvent extends EventLogger.Event {
+ static final int START_PIID = 0;
+
+ static final int STOP_PIID = 1;
+
+ static final int CLIENT_DIED = 2;
+
+ final int mEventType;
+ final int mIntValue1;
+ final int mIntValue2;
+
+ private LoudnessEvent(int event, int i1, int i2) {
+ mEventType = event;
+ mIntValue1 = i1;
+ mIntValue2 = i2;
+ }
+
+ static LoudnessEvent getStartPiid(int piid, int pid) {
+ return new LoudnessEvent(START_PIID, piid, pid);
+ }
+
+ static LoudnessEvent getStopPiid(int piid, int pid) {
+ return new LoudnessEvent(STOP_PIID, piid, pid);
+ }
+
+ static LoudnessEvent getClientDied(int pid) {
+ return new LoudnessEvent(CLIENT_DIED, 0 /* ignored */, pid);
+ }
+
+
+ @Override
+ public String eventToString() {
+ switch (mEventType) {
+ case START_PIID:
+ return String.format(
+ "Start loudness updates for piid %d for client pid %d",
+ mIntValue1, mIntValue2);
+ case STOP_PIID:
+ return String.format(
+ "Stop loudness updates for piid %d for client pid %d",
+ mIntValue1, mIntValue2);
+ case CLIENT_DIED:
+ return String.format("Loudness client with pid %d died", mIntValue2);
+
+ }
+ return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
+ }
+ }
+
/**
* Class to log stream type mute/unmute events
*/
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 3c67e9d..bbe819f 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -21,6 +21,11 @@
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
import static android.media.AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -41,7 +46,11 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.audio.AudioServiceEvents.LoudnessEvent;
+import com.android.server.utils.EventLogger;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -50,6 +59,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Class to handle the updates in loudness parameters and responsible to generate parameters that
@@ -70,10 +80,14 @@
private static final String SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE =
"audio.loudness.builtin-speaker-spl-range-size";
- private static final int SPL_RANGE_UNKNOWN = 0;
- private static final int SPL_RANGE_SMALL = 1;
- private static final int SPL_RANGE_MEDIUM = 2;
- private static final int SPL_RANGE_LARGE = 3;
+ @VisibleForTesting
+ static final int SPL_RANGE_UNKNOWN = 0;
+ @VisibleForTesting
+ static final int SPL_RANGE_SMALL = 1;
+ @VisibleForTesting
+ static final int SPL_RANGE_MEDIUM = 2;
+ @VisibleForTesting
+ static final int SPL_RANGE_LARGE = 3;
/** The possible transducer SPL ranges as defined in CTA2075 */
@IntDef({
@@ -99,12 +113,19 @@
pid = (Integer) cookie;
}
if (pid != null) {
- mLoudnessCodecHelper.removePid(pid);
+ if (DEBUG) {
+ Log.d(TAG, "Client with pid " + pid + " died, removing from receiving updates");
+ }
+ sLogger.enqueue(LoudnessEvent.getClientDied(pid));
+ mLoudnessCodecHelper.onClientPidDied(pid);
}
super.onCallbackDied(callback, cookie);
}
}
+ private static final EventLogger sLogger = new EventLogger(
+ AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates");
+
private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers =
new LoudnessRemoteCallbackList(this);
@@ -125,7 +146,8 @@
private final AudioService mAudioService;
/** Contains the properties necessary to compute the codec loudness related parameters. */
- private static final class LoudnessCodecInputProperties {
+ @VisibleForTesting
+ static final class LoudnessCodecInputProperties {
private final int mMetadataType;
private final boolean mIsDownmixing;
@@ -200,10 +222,53 @@
}
PersistableBundle createLoudnessParameters() {
- // TODO: create bundle with new parameters
- return new PersistableBundle();
- }
+ PersistableBundle loudnessParams = new PersistableBundle();
+ switch (mDeviceSplRange) {
+ case SPL_RANGE_LARGE:
+ // corresponds to -31dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 124);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_MEDIUM:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_SMALL:
+ // corresponds to -16dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 64);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 1);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // limited playback range compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 3);
+ }
+ break;
+ default:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ }
+
+ return loudnessParams;
+ }
}
@GuardedBy("mLock")
@@ -227,22 +292,25 @@
if (DEBUG) {
Log.d(TAG, "startLoudnessCodecUpdates: piid " + piid + " codecInfos " + codecInfoList);
}
- Set<LoudnessCodecInfo> infoSet;
+
synchronized (mLock) {
if (mStartedPiids.contains(piid)) {
Log.w(TAG, "Already started loudness updates for piid " + piid);
return;
}
- infoSet = new HashSet<>(codecInfoList);
+ Set<LoudnessCodecInfo> infoSet = new HashSet<>(codecInfoList);
mStartedPiids.put(piid, infoSet);
- mPiidToPidCache.put(piid, Binder.getCallingPid());
+ int pid = Binder.getCallingPid();
+ mPiidToPidCache.put(piid, pid);
+
+ sLogger.enqueue(LoudnessEvent.getStartPiid(piid, pid));
}
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
mAudioService.getActivePlaybackConfigurations().stream().filter(
conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
- apc -> updateCodecParametersForConfiguration(apc, infoSet));
+ this::updateCodecParametersForConfiguration);
}
}
@@ -250,20 +318,24 @@
if (DEBUG) {
Log.d(TAG, "stopLoudnessCodecUpdates: piid " + piid);
}
+
synchronized (mLock) {
if (!mStartedPiids.contains(piid)) {
Log.w(TAG, "Loudness updates are already stopped for piid " + piid);
return;
}
mStartedPiids.remove(piid);
+
+ sLogger.enqueue(LoudnessEvent.getStopPiid(piid, mPiidToPidCache.get(piid, -1)));
mPiidToDeviceIdCache.delete(piid);
mPiidToPidCache.delete(piid);
}
}
- void addLoudnessCodecInfo(int piid, LoudnessCodecInfo info) {
+ void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo info) {
if (DEBUG) {
- Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " info " + info);
+ Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " mcHash " + mediaCodecHash + " info "
+ + info);
}
Set<LoudnessCodecInfo> infoSet;
@@ -280,7 +352,20 @@
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
mAudioService.getActivePlaybackConfigurations().stream().filter(
conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
- apc -> updateCodecParametersForConfiguration(apc, Set.of(info)));
+ apc -> {
+ final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
+ if (deviceInfo != null) {
+ PersistableBundle updateBundle = new PersistableBundle();
+ synchronized (mLock) {
+ updateBundle.putPersistableBundle(
+ Integer.toString(mediaCodecHash),
+ getCodecBundle_l(deviceInfo, info));
+ }
+ if (!updateBundle.isDefinitelyEmpty()) {
+ dispatchNewLoudnessParameters(piid, updateBundle);
+ }
+ }
+ });
}
}
@@ -298,24 +383,6 @@
}
}
- void removePid(int pid) {
- if (DEBUG) {
- Log.d(TAG, "Removing pid " + pid + " from receiving updates");
- }
- synchronized (mLock) {
- for (int i = 0; i < mPiidToPidCache.size(); ++i) {
- int piid = mPiidToPidCache.keyAt(i);
- if (mPiidToPidCache.get(piid) == pid) {
- if (DEBUG) {
- Log.d(TAG, "Removing piid " + piid);
- }
- mStartedPiids.delete(piid);
- mPiidToDeviceIdCache.delete(piid);
- }
- }
- }
- }
-
PersistableBundle getLoudnessParams(int piid, LoudnessCodecInfo codecInfo) {
if (DEBUG) {
Log.d(TAG, "getLoudnessParams: piid " + piid + " codecInfo " + codecInfo);
@@ -381,48 +448,77 @@
}
}
- updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc, null));
+ updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc));
}
- /** Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
+ /** Updates and dispatches the new loudness parameters for all its registered codecs. */
+ void dump(PrintWriter pw) {
+ // Registered clients
+ pw.println("\nRegistered clients:\n");
+ synchronized (mLock) {
+ for (int i = 0; i < mStartedPiids.size(); ++i) {
+ int piid = mStartedPiids.keyAt(i);
+ int pid = mPiidToPidCache.get(piid, -1);
+ final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
+ pw.println(String.format("Player piid %d pid %d active codec types %s\n", piid,
+ pid, codecInfos.stream().map(Object::toString).collect(
+ Collectors.joining(", "))));
+ }
+ pw.println();
+ }
+
+ sLogger.dump(pw);
+ pw.println();
+ }
+
+ private void onClientPidDied(int pid) {
+ synchronized (mLock) {
+ for (int i = 0; i < mPiidToPidCache.size(); ++i) {
+ int piid = mPiidToPidCache.keyAt(i);
+ if (mPiidToPidCache.get(piid) == pid) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing piid " + piid);
+ }
+ mStartedPiids.delete(piid);
+ mPiidToDeviceIdCache.delete(piid);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
*
* @param apc the player configuration for which the loudness parameters are updated.
- * @param codecInfos the codec info for which the parameters are updated. If {@code null},
- * send updates for all the started codecs assigned to {@code apc}
*/
- private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc,
- Set<LoudnessCodecInfo> codecInfos) {
+ private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc) {
if (DEBUG) {
- Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc + " codecInfos: "
- + codecInfos);
+ Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc);
}
+
final PersistableBundle allBundles = new PersistableBundle();
final int piid = apc.getPlayerInterfaceId();
+
synchronized (mLock) {
- if (codecInfos == null) {
- codecInfos = mStartedPiids.get(piid);
- }
+ final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
if (codecInfos != null && deviceInfo != null) {
for (LoudnessCodecInfo info : codecInfos) {
- allBundles.putPersistableBundle(Integer.toString(info.mediaCodecHashCode),
+ allBundles.putPersistableBundle(Integer.toString(info.hashCode()),
getCodecBundle_l(deviceInfo, info));
}
}
}
if (!allBundles.isDefinitelyEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Dispatching for piid: " + piid + " bundle: " + allBundles);
- }
dispatchNewLoudnessParameters(piid, allBundles);
}
}
private void dispatchNewLoudnessParameters(int piid, PersistableBundle bundle) {
if (DEBUG) {
- Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid);
+ Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid + " bundle: " + bundle);
}
final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; ++i) {
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index be78ea2..cecde55 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -149,6 +149,14 @@
public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
/**
+ * Returns the ID of the device which owns the display with the given ID.
+ *
+ * <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then
+ * {@link android.content.Context#DEVICE_ID_DEFAULT} is returned.</p>
+ */
+ public abstract int getDeviceIdForDisplayId(int displayId);
+
+ /**
* Gets the persistent ID for the VirtualDevice with the given device ID.
*
* @param deviceId which device we're asking about
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index debf828..d848f4b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -121,6 +121,7 @@
// Display independent, mode dependent values
float[] brightnessLevelsNits;
+ float[] brightnessLevels = null;
float[] luxLevels;
if (isForIdleMode) {
brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
@@ -130,11 +131,21 @@
} else {
brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
+
+ brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels();
+ if (brightnessLevels == null || brightnessLevels.length == 0) {
+ // Load the old configuration in the range [0, 255]. The values need to be
+ // normalized to the range [0, 1].
+ int[] brightnessLevelsInt = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ brightnessLevels = new float[brightnessLevelsInt.length];
+ for (int i = 0; i < brightnessLevels.length; i++) {
+ brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]);
+ }
+ }
}
// Display independent, mode independent values
- int[] brightnessLevelsBacklight = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
@@ -155,8 +166,8 @@
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
autoBrightnessAdjustmentMaxGamma, isForIdleMode, displayWhiteBalanceController);
- } else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
- return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
+ } else if (isValidMapping(luxLevels, brightnessLevels)) {
+ return new SimpleMappingStrategy(luxLevels, brightnessLevels,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
} else {
return null;
@@ -620,7 +631,7 @@
private float mUserBrightness;
private long mShortTermModelTimeout;
- private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma,
+ private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
long timeout) {
Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
"Lux and brightness arrays must not be empty!");
@@ -635,7 +646,7 @@
mBrightness = new float[N];
for (int i = 0; i < N; i++) {
mLux[i] = lux[i];
- mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]);
+ mBrightness[i] = brightness[i];
}
mMaxGamma = maxGamma;
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 70d4ad2..c26118e 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -20,6 +20,8 @@
import android.os.Handler;
import android.view.Display;
+import com.android.server.display.feature.DisplayManagerFlags;
+
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,6 +41,7 @@
private final Handler mHandler;
private final Listener mListener;
private final String mName;
+ private final DisplayManagerFlags mFeatureFlags;
public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
@@ -50,13 +53,14 @@
private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode.
// Called with SyncRoot lock held.
- public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, String name) {
+ DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
+ Listener listener, String name, DisplayManagerFlags featureFlags) {
mSyncRoot = syncRoot;
mContext = context;
mHandler = handler;
mListener = listener;
mName = name;
+ mFeatureFlags = featureFlags;
}
/**
@@ -88,6 +92,10 @@
return mName;
}
+ public final DisplayManagerFlags getFeatureFlags() {
+ return mFeatureFlags;
+ }
+
/**
* Registers the display adapter with the display manager.
*
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2fdf90d..3b05b47 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -400,6 +400,7 @@
}
private DisplayDeviceConfig loadDisplayDeviceConfig() {
- return DisplayDeviceConfig.create(mContext, false);
+ return DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false,
+ mDisplayAdapter.getFeatureFlags());
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index b99de5c..d97127c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,7 @@
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
+import com.android.server.display.config.LuxToBrightnessMapping;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
import com.android.server.display.config.Point;
@@ -77,6 +78,7 @@
import com.android.server.display.config.ThresholdPoint;
import com.android.server.display.config.UsiVersion;
import com.android.server.display.config.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -310,16 +312,18 @@
* <darkeningLightDebounceIdleMillis>
* 1000
* </darkeningLightDebounceIdleMillis>
- * <displayBrightnessMapping>
- * <displayBrightnessPoint>
- * <lux>50</lux>
- * <nits>45.32</nits>
- * </displayBrightnessPoint>
- * <displayBrightnessPoint>
- * <lux>80</lux>
- * <nits>75.43</nits>
- * </displayBrightnessPoint>
- * </displayBrightnessMapping>
+ * <luxToBrightnessMapping>
+ * <map>
+ * <point>
+ * <first>0</first>
+ * <second>0.2</second>
+ * </point>
+ * <point>
+ * <first>80</first>
+ * <second>0.3</second>
+ * </point>
+ * </map>
+ * </luxToBrightnessMapping>
* </autoBrightness>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -528,6 +532,7 @@
* <majorVersion>2</majorVersion>
* <minorVersion>0</minorVersion>
* </usiVersion>
+ * <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
* </displayConfiguration>
* }
* </pre>
@@ -629,7 +634,6 @@
// for the corresponding values above
private float[] mBrightness;
-
/**
* Array of desired screen brightness in nits corresponding to the lux values
* in the mBrightnessLevelsLux array. The display brightness is defined as the
@@ -639,20 +643,25 @@
private float[] mBrightnessLevelsNits;
/**
- * Array of light sensor lux values to define our levels for auto backlight
- * brightness support.
+ * Array of desired screen brightness corresponding to the lux values
+ * in the mBrightnessLevelsLux array. The brightness values must be non-negative and
+ * non-decreasing. They must be between {@link PowerManager.BRIGHTNESS_MIN} and
+ * {@link PowerManager.BRIGHTNESS_MAX}. This must be overridden in platform specific overlays
+ */
+ private float[] mBrightnessLevels;
+
+ /**
+ * Array of light sensor lux values to define our levels for auto-brightness support.
*
- * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits,
- * with first value always being 0 lux
+ * The first lux value is always 0.
*
- * The control points must be strictly increasing. Each control point
- * corresponds to an entry in the brightness backlight values arrays.
- * For example, if lux == level[1] (second element of the levels array)
- * then the brightness will be determined by value[0] (first element
- * of the brightness values array).
+ * The control points must be strictly increasing. Each control point corresponds to an entry
+ * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
+ * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
+ * element of the brightness values array).
*
- * Spline interpolation is used to determine the auto-brightness
- * backlight values for lux levels between these control points.
+ * Spline interpolation is used to determine the auto-brightness values for lux levels between
+ * these control points.
*/
private float[] mBrightnessLevelsLux;
@@ -843,9 +852,17 @@
@Nullable
private HdrBrightnessData mHdrBrightnessData;
+ /**
+ * Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ private float mBrightnessCapForWearBedtimeMode;
+
+ private final DisplayManagerFlags mFlags;
+
@VisibleForTesting
- DisplayDeviceConfig(Context context) {
+ DisplayDeviceConfig(Context context, DisplayManagerFlags flags) {
mContext = context;
+ mFlags = flags;
}
/**
@@ -861,9 +878,9 @@
* @return A configuration instance for the specified display.
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
- boolean isFirstDisplay) {
+ boolean isFirstDisplay, DisplayManagerFlags flags) {
final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
- isFirstDisplay);
+ isFirstDisplay, flags);
config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
return config;
@@ -878,28 +895,29 @@
* or the default values.
* @return A configuration instance.
*/
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml,
+ DisplayManagerFlags flags) {
final DisplayDeviceConfig config;
if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
+ config = getConfigFromGlobalXml(context, flags);
} else {
- config = getConfigFromPmValues(context);
+ config = getConfigFromPmValues(context, flags);
}
return config;
}
private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
@@ -907,7 +925,7 @@
// If no config can be loaded from any ddc xml at all,
// prepare a whole config using the global config.xml.
// Guaranteed not null
- return create(context, isFirstDisplay);
+ return create(context, isFirstDisplay, flags);
}
private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
@@ -960,18 +978,19 @@
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
- File baseDirectory, long physicalDisplayId) {
+ File baseDirectory, long physicalDisplayId, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
// Create config using filename from physical ID (including "stable" bit).
config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
// Create config using filename from physical ID (excluding "stable" bit).
final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
- config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
+ config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag,
+ flags);
if (config != null) {
return config;
}
@@ -980,7 +999,7 @@
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
int port = physicalAddress.getPort();
- config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
+ config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port, flags);
return config;
}
@@ -1599,6 +1618,13 @@
}
/**
+ * @return Auto brightness brightening levels
+ */
+ public float[] getAutoBrightnessBrighteningLevels() {
+ return mBrightnessLevels;
+ }
+
+ /**
* @return Default peak refresh rate of the associated display
*/
public int getDefaultPeakRefreshRate() {
@@ -1741,6 +1767,13 @@
return mHostUsiVersion;
}
+ /**
+ * @return Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ public float getBrightnessCapForWearBedtimeMode() {
+ return mBrightnessCapForWearBedtimeMode;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1844,6 +1877,7 @@
+ mAutoBrightnessDarkeningLightDebounceIdle
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels)
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
+ ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+ "\n"
@@ -1867,36 +1901,39 @@
+ ", mHighAmbientBrightnessThresholds= "
+ Arrays.toString(mHighAmbientBrightnessThresholds)
+ "\n"
- + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
+ + "mScreenOffBrightnessSensorValueToLux= " + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ "\n"
+ "mUsiVersion= " + mHostUsiVersion + "\n"
- + "mHdrBrightnessData" + mHdrBrightnessData
+ + "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+ + "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
+ "}";
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
- String suffixFormat, long idNumber) {
+ String suffixFormat, long idNumber, DisplayManagerFlags flags) {
final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
- final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ final DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
if (config.initFromFile(filePath)) {
return config;
}
return null;
}
- private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromGlobalXml(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromGlobalXml();
return config;
}
- private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromPmValues(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromDefaultValues();
return config;
}
@@ -1938,6 +1975,7 @@
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
loadUsiVersion(config);
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
+ loadBrightnessCapForWearBedtimeMode(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -1961,6 +1999,7 @@
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
loadRefreshRateSetting(null);
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -2599,8 +2638,23 @@
* loading the value from the display config, and if not present, falls back to config.xml.
*/
private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
- if (autoBrightnessConfig == null
- || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
+ if (mFlags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
+ && autoBrightnessConfig.getLuxToBrightnessMapping() != null) {
+ LuxToBrightnessMapping mapping = autoBrightnessConfig.getLuxToBrightnessMapping();
+ final int size = mapping.getMap().getPoint().size();
+ mBrightnessLevels = new float[size];
+ mBrightnessLevelsLux = new float[size];
+ for (int i = 0; i < size; i++) {
+ float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
+ mBrightnessLevels[i] = mBacklightToBrightnessSpline.interpolate(backlight);
+ mBrightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
+ .floatValue();
+ }
+ if (size > 0 && mBrightnessLevelsLux[0] != 0) {
+ throw new IllegalArgumentException(
+ "The first lux value in the display brightness mapping must be 0");
+ }
+ } else {
mBrightnessLevelsNits = getFloatArray(mContext.getResources()
.obtainTypedArray(com.android.internal.R.array
.config_autoBrightnessDisplayValuesNits), PowerManager
@@ -2608,18 +2662,6 @@
mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
.getIntArray(com.android.internal.R.array
.config_autoBrightnessLevels));
- } else {
- final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().size();
- mBrightnessLevelsNits = new float[size];
- // The first control point is implicit and always at 0 lux.
- mBrightnessLevelsLux = new float[size + 1];
- for (int i = 0; i < size; i++) {
- mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getNits().floatValue();
- mBrightnessLevelsLux[i + 1] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getLux().floatValue();
- }
}
}
@@ -3350,6 +3392,23 @@
: null;
}
+ private void loadBrightnessCapForWearBedtimeMode(DisplayConfiguration config) {
+ if (config != null) {
+ BigDecimal configBrightnessCap = config.getScreenBrightnessCapForWearBedtimeMode();
+ if (configBrightnessCap != null) {
+ mBrightnessCapForWearBedtimeMode = configBrightnessCap.floatValue();
+ } else {
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
+ }
+ }
+ }
+
+ private void loadBrightnessCapForWearBedtimeModeFromConfigXml() {
+ mBrightnessCapForWearBedtimeMode = BrightnessSynchronizer.brightnessIntToFloat(
+ mContext.getResources().getInteger(com.android.internal.R.integer
+ .config_screenBrightnessCapForWearBedtimeMode));
+ }
+
/**
* Container for high brightness mode configuration data.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a7c17d..2ab15e6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1853,7 +1853,7 @@
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo);
+ mHandler, mDisplayDeviceRepo, mFlags);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
@@ -1871,7 +1871,7 @@
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler, mFlags));
}
private void registerWifiDisplayAdapterLocked() {
@@ -1880,7 +1880,7 @@
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
- mPersistentDataStore);
+ mPersistentDataStore, mFlags);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
}
@@ -3288,8 +3288,10 @@
@VisibleForTesting
static class Injector {
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ flags);
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index ff9a1ab..22898a6 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -84,8 +84,6 @@
private final boolean mIsBootDisplayModeSupported;
- private final DisplayManagerFlags mFlags;
-
private final DisplayNotificationManager mDisplayNotificationManager;
private Context mOverlayContext;
@@ -103,12 +101,11 @@
Listener listener, DisplayManagerFlags flags,
DisplayNotificationManager displayNotificationManager,
Injector injector) {
- super(syncRoot, context, handler, listener, TAG);
+ super(syncRoot, context, handler, listener, TAG, flags);
mDisplayNotificationManager = displayNotificationManager;
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
- mFlags = flags;
}
@Override
@@ -510,7 +507,7 @@
// Load display device config
final Context context = getOverlayContext();
mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
- mIsFirstDisplay);
+ mIsFirstDisplay, getFeatureFlags());
// Load brightness HWC quirk
mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -831,7 +828,8 @@
+ ", state=" + Display.stateToString(state) + ")");
}
- boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+ boolean isDisplayOffloadEnabled =
+ getFeatureFlags().isDisplayOffloadEnabled();
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
@@ -1377,8 +1375,8 @@
}
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay, flags);
}
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2ce7690..22ff2d0 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -36,6 +36,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import java.io.PrintWriter;
@@ -134,8 +135,9 @@
// Called with SyncRoot lock held.
public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, Handler uiHandler) {
- super(syncRoot, context, handler, listener, TAG);
+ Context context, Handler handler, Listener listener, Handler uiHandler,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mUiHandler = uiHandler;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index edbd424..ec5ad7d 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -61,6 +61,7 @@
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -88,7 +89,7 @@
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener) {
+ Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) {
this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
@Override
public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
@@ -99,14 +100,15 @@
public void destroyDisplay(IBinder displayToken) {
DisplayControl.destroyDisplay(displayToken);
}
- });
+ }, featureFlags);
}
@VisibleForTesting
VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
- super(syncRoot, context, handler, listener, TAG);
+ SurfaceControlDisplayFactory surfaceControlDisplayFactory,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mHandler = handler;
mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 7660cf8..aa98cd8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -41,6 +41,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import java.io.PrintWriter;
@@ -99,8 +100,8 @@
// Called with SyncRoot lock held.
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- PersistentDataStore persistentDataStore) {
- super(syncRoot, context, handler, listener, TAG);
+ PersistentDataStore persistentDataStore, DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
throw new RuntimeException("WiFi display was requested, "
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index dfcda40..8a884b6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness.clamper;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
@@ -31,6 +32,18 @@
protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
protected boolean mIsActive = false;
+ @NonNull
+ protected final Handler mHandler;
+
+ @NonNull
+ protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+ BrightnessClamper(Handler handler,
+ BrightnessClamperController.ClamperChangeListener changeListener) {
+ mHandler = handler;
+ mChangeListener = changeListener;
+ }
+
float getBrightnessCap() {
return mBrightnessCap;
}
@@ -60,6 +73,7 @@
protected enum Type {
THERMAL,
- POWER
+ POWER,
+ BEDTIME_MODE
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index b574919..765608e 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -90,7 +90,8 @@
}
};
- mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags);
+ mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+ context);
mModifiers = injector.getModifiers(context);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -234,7 +235,7 @@
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, Context context) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
clampers.add(
new BrightnessThermalClamper(handler, clamperChangeListener, data));
@@ -242,6 +243,10 @@
clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
data));
}
+ if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
+ clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
+ clamperChangeListener, data));
+ }
return clampers;
}
@@ -257,7 +262,8 @@
* Config Data for clampers
*/
public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
- BrightnessPowerClamper.PowerData {
+ BrightnessPowerClamper.PowerData,
+ BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
private final String mUniqueDisplayId;
@NonNull
@@ -315,5 +321,10 @@
public PowerThrottlingConfigData getPowerThrottlingConfigData() {
return mDisplayDeviceConfig.getPowerThrottlingConfigData();
}
+
+ @Override
+ public float getBrightnessWearBedtimeModeCap() {
+ return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 339b589..790322d 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -49,10 +49,6 @@
private final Injector mInjector;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangeListener;
@Nullable
private PmicMonitor mPmicMonitor;
// data from DeviceConfig, for all displays, for all dataSets
@@ -99,10 +95,9 @@
@VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
PowerData powerData) {
+ super(handler, listener);
mInjector = injector;
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangeListener = listener;
mHandler.post(() -> {
setDisplayData(powerData);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 8ae962b..944a8a6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -54,10 +54,6 @@
private final IThermalService mThermalService;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangelistener;
// data from DeviceConfig, for all displays, for all dataSets
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
@NonNull
@@ -108,10 +104,9 @@
@VisibleForTesting
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
+ super(handler, listener);
mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangelistener = listener;
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
@@ -220,7 +215,7 @@
if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
mBrightnessCap = brightnessCap;
mIsActive = isActive;
- mChangelistener.onChanged();
+ mChangeListener.onChanged();
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
new file mode 100644
index 0000000..7e853bf
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.brightness.clamper;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BrightnessWearBedtimeModeClamper extends
+ BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> {
+
+ public static final int BEDTIME_MODE_OFF = 0;
+ public static final int BEDTIME_MODE_ON = 1;
+
+ private final Context mContext;
+
+ private final ContentObserver mSettingsObserver;
+
+ BrightnessWearBedtimeModeClamper(Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ this(new Injector(), handler, context, listener, data);
+ }
+
+ @VisibleForTesting
+ BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ super(handler, listener);
+ mContext = context;
+ mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final int bedtimeModeSetting = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.Wearable.BEDTIME_MODE,
+ BEDTIME_MODE_OFF);
+ mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
+ mChangeListener.onChanged();
+ }
+ };
+ injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
+ }
+
+ @NonNull
+ @Override
+ Type getType() {
+ return Type.BEDTIME_MODE;
+ }
+
+ @Override
+ void onDeviceConfigChanged() {}
+
+ @Override
+ void onDisplayChanged(WearBedtimeModeData displayData) {
+ mHandler.post(() -> {
+ mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap();
+ mChangeListener.onChanged();
+ });
+ }
+
+ @Override
+ void stop() {
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+
+ interface WearBedtimeModeData {
+ float getBrightnessWearBedtimeModeCap();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
+ /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5cfbf26..c71f0cf2 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -90,6 +90,14 @@
Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
Flags::enableExternalVsyncProximityVote);
+ private final FlagState mBrightnessWearBedtimeModeClamperFlagState = new FlagState(
+ Flags.FLAG_BRIGHTNESS_WEAR_BEDTIME_MODE_CLAMPER,
+ Flags::brightnessWearBedtimeModeClamper);
+
+ private final FlagState mAutoBrightnessModesFlagState = new FlagState(
+ Flags.FLAG_AUTO_BRIGHTNESS_MODES,
+ Flags::autoBrightnessModes);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -178,6 +186,17 @@
return mVsyncProximityVote.isEnabled();
}
+ public boolean isBrightnessWearBedtimeModeClamperEnabled() {
+ return mBrightnessWearBedtimeModeClamperFlagState.isEnabled();
+ }
+
+ /**
+ * @return Whether generic auto-brightness modes are enabled
+ */
+ public boolean areAutoBrightnessModesEnabled() {
+ return mAutoBrightnessModesFlagState.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -197,6 +216,8 @@
pw.println(" " + mSmallAreaDetectionFlagState);
pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
pw.println(" " + mVsyncProximityVote);
+ pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
+ pw.println(" " + mAutoBrightnessModesFlagState);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index d95bdae..9dfa1ee 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -120,3 +120,19 @@
bug: "284866750"
is_fixed_read_only: true
}
+
+flag {
+ name: "brightness_wear_bedtime_mode_clamper"
+ namespace: "display_manager"
+ description: "Feature flag for the Wear Bedtime mode brightness clamper"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "auto_brightness_modes"
+ namespace: "display_manager"
+ description: "Feature flag for generic auto-brightness modes"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 8eb03ec..8fa838d 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -21,8 +21,8 @@
import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
import static android.view.Display.Mode.INVALID_MODE_ID;
-import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRate;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -925,16 +925,11 @@
}
@VisibleForTesting
- DisplayObserver getDisplayObserver() {
- return mDisplayObserver;
- }
-
- @VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
synchronized (mLock) {
- mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate,
- defaultRefreshRate, Display.DEFAULT_DISPLAY);
+ mSettingsObserver.updateRefreshRateSettingLocked(
+ minRefreshRate, peakRefreshRate, defaultRefreshRate);
return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
}
}
@@ -1268,23 +1263,9 @@
mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
}
- /**
- * Update refresh rate settings for all displays
- */
private void updateRefreshRateSettingLocked() {
- Display[] displays = mInjector.getDisplays();
- for (int i = 0; i < displays.length; i++) {
- updateRefreshRateSettingLocked(displays[i].getDisplayId());
- }
- }
-
- /**
- * Update refresh rate settings for a specific display
- * @param displayId The display ID
- */
- private void updateRefreshRateSettingLocked(int displayId) {
final ContentResolver cr = mContext.getContentResolver();
- float highestRefreshRate = findHighestRefreshRate(mContext, displayId);
+ float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
float minRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
@@ -1312,12 +1293,11 @@
}
}
- updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
- displayId);
+ updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
- private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
- float defaultRefreshRate, int displayId) {
+ private void updateRefreshRateSettingLocked(
+ float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
// TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
// used to predict if we're going to be doing frequent refresh rate switching, and if
// so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1325,9 +1305,9 @@
Vote peakVote = peakRefreshRate == 0f
? null
: Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
peakVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
Vote defaultVote =
defaultRefreshRate == 0f
@@ -1484,8 +1464,7 @@
}
}
- @VisibleForTesting
- public final class DisplayObserver implements DisplayManager.DisplayListener {
+ private final class DisplayObserver implements DisplayManager.DisplayListener {
// Note that we can never call into DisplayManager or any of the non-POD classes it
// returns, while holding mLock since it may call into DMS, which might be simultaneously
// calling into us already holding its own lock.
@@ -1577,7 +1556,6 @@
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
- mSettingsObserver.updateRefreshRateSettingLocked(displayId);
}
@Nullable
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 6f9b7d6..27b01a5 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -1048,10 +1049,22 @@
stopBatching();
if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
- // change period and/or lowPowerMode
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, mProviderRequest.isLowPower())) {
- Log.e(TAG, "set_position_mode failed in updateRequirements");
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (!positionMode.equals(mLastPositionMode)) {
+ stopNavigating();
+ startNavigating();
+ }
+ } else {
+ // change period and/or lowPowerMode
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, mProviderRequest.isLowPower())) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
+ }
}
} else if (!mStarted) {
// start GPS
@@ -1234,11 +1247,32 @@
}
int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- interval, mProviderRequest.isLowPower())) {
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
+
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ boolean success = mGnssNative.setPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (success) {
+ mLastPositionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ } else {
+ mLastPositionMode = null;
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ } else {
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ interval, mProviderRequest.isLowPower())) {
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
}
if (!mGnssNative.start()) {
setStarted(false);
diff --git a/services/core/java/com/android/server/media/AudioAttributesUtils.java b/services/core/java/com/android/server/media/AudioAttributesUtils.java
deleted file mode 100644
index 8cb334d..0000000
--- a/services/core/java/com/android/server/media/AudioAttributesUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2023 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.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.AudioAttributes;
-import android.media.AudioDeviceAttributes;
-import android.media.AudioDeviceInfo;
-import android.media.MediaRoute2Info;
-
-import com.android.media.flags.Flags;
-
-/* package */ final class AudioAttributesUtils {
-
- /* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .build();
-
- private AudioAttributesUtils() {
- // no-op to prevent instantiation.
- }
-
- @MediaRoute2Info.Type
- /* package */ static int mapToMediaRouteType(
- @NonNull AudioDeviceAttributes audioDeviceAttributes) {
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- return MediaRoute2Info.TYPE_HDMI_ARC;
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- return MediaRoute2Info.TYPE_HDMI_EARC;
- }
- }
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
- case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
- return MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
- case AudioDeviceInfo.TYPE_WIRED_HEADSET:
- return MediaRoute2Info.TYPE_WIRED_HEADSET;
- case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
- return MediaRoute2Info.TYPE_WIRED_HEADPHONES;
- case AudioDeviceInfo.TYPE_DOCK:
- case AudioDeviceInfo.TYPE_DOCK_ANALOG:
- return MediaRoute2Info.TYPE_DOCK;
- case AudioDeviceInfo.TYPE_HDMI:
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- return MediaRoute2Info.TYPE_HDMI;
- case AudioDeviceInfo.TYPE_USB_DEVICE:
- return MediaRoute2Info.TYPE_USB_DEVICE;
- case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
- return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- case AudioDeviceInfo.TYPE_BLE_HEADSET:
- return MediaRoute2Info.TYPE_BLE_HEADSET;
- case AudioDeviceInfo.TYPE_HEARING_AID:
- return MediaRoute2Info.TYPE_HEARING_AID;
- default:
- return MediaRoute2Info.TYPE_UNKNOWN;
- }
- }
-
- /* package */ static boolean isDeviceOutputAttributes(
- @Nullable AudioDeviceAttributes audioDeviceAttributes) {
- if (audioDeviceAttributes == null) {
- return false;
- }
-
- if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
- return false;
- }
-
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
- case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
- case AudioDeviceInfo.TYPE_WIRED_HEADSET:
- case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
- case AudioDeviceInfo.TYPE_DOCK:
- case AudioDeviceInfo.TYPE_DOCK_ANALOG:
- case AudioDeviceInfo.TYPE_HDMI:
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- case AudioDeviceInfo.TYPE_USB_DEVICE:
- return true;
- default:
- return false;
- }
- }
-
- /* package */ static boolean isBluetoothOutputAttributes(
- @Nullable AudioDeviceAttributes audioDeviceAttributes) {
- if (audioDeviceAttributes == null) {
- return false;
- }
-
- if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
- return false;
- }
-
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
- case AudioDeviceInfo.TYPE_BLE_HEADSET:
- case AudioDeviceInfo.TYPE_BLE_SPEAKER:
- case AudioDeviceInfo.TYPE_HEARING_AID:
- return true;
- default:
- return false;
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
index 8bc69c2..a00999d 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -17,7 +17,6 @@
package com.android.server.media;
import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
-import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,38 +30,37 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.media.AudioSystem;
import android.media.MediaRoute2Info;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
- * Controls bluetooth routes and provides selected route override.
+ * Maintains a list of connected {@link BluetoothDevice bluetooth devices} and allows their
+ * activation.
*
- * <p>The controller offers similar functionality to {@link LegacyBluetoothRouteController} but does
- * not support routes selection logic. Instead, relies on external clients to make a decision
- * about currently selected route.
- *
- * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
- * Policies.
+ * <p>This class also serves as ground truth for assigning {@link MediaRoute2Info#getId() route ids}
+ * for bluetooth routes via {@link #getRouteIdForBluetoothAddress}.
*/
-/* package */ class AudioPoliciesBluetoothRouteController
- implements BluetoothRouteController {
- private static final String TAG = "APBtRouteController";
+// TODO: b/305199571 - Rename this class to remove the RouteController suffix, which causes
+// confusion with the BluetoothRouteController interface.
+/* package */ class AudioPoliciesBluetoothRouteController {
+ private static final String TAG = SystemMediaRoute2Provider.TAG;
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
@@ -75,11 +73,8 @@
private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
new DeviceStateChangedReceiver();
- @NonNull
- private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
-
- @NonNull
- private final SparseIntArray mVolumeMap = new SparseIntArray();
+ @NonNull private Map<String, BluetoothDevice> mAddressToBondedDevice = new HashMap<>();
+ @NonNull private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
@NonNull
private final Context mContext;
@@ -89,11 +84,6 @@
private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
@NonNull
private final BluetoothProfileMonitor mBluetoothProfileMonitor;
- @NonNull
- private final AudioManager mAudioManager;
-
- @Nullable
- private BluetoothRouteInfo mSelectedBluetoothRoute;
AudioPoliciesBluetoothRouteController(@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@@ -107,21 +97,12 @@
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(bluetoothAdapter);
- Objects.requireNonNull(bluetoothProfileMonitor);
- Objects.requireNonNull(listener);
-
- mContext = context;
- mBluetoothAdapter = bluetoothAdapter;
- mBluetoothProfileMonitor = bluetoothProfileMonitor;
- mAudioManager = mContext.getSystemService(AudioManager.class);
- mListener = listener;
-
- updateBluetoothRoutes();
+ mContext = Objects.requireNonNull(context);
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
+ mBluetoothProfileMonitor = Objects.requireNonNull(bluetoothProfileMonitor);
+ mListener = Objects.requireNonNull(listener);
}
- @Override
public void start(UserHandle user) {
mBluetoothProfileMonitor.start();
@@ -133,122 +114,63 @@
IntentFilter deviceStateChangedIntentFilter = new IntentFilter();
- deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
deviceStateChangedIntentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
deviceStateChangedIntentFilter.addAction(
BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
deviceStateChangedIntentFilter.addAction(
BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- deviceStateChangedIntentFilter.addAction(
- BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
mContext.registerReceiverAsUser(mDeviceStateChangedReceiver, user,
deviceStateChangedIntentFilter, null, null);
+ updateBluetoothRoutes();
}
- @Override
public void stop() {
mContext.unregisterReceiver(mAdapterStateChangedReceiver);
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
- @Override
- public boolean selectRoute(@Nullable String deviceAddress) {
- synchronized (this) {
- // Fetch all available devices in order to avoid race conditions with Bluetooth stack.
- updateBluetoothRoutes();
-
- if (deviceAddress == null) {
- mSelectedBluetoothRoute = null;
- return true;
- }
-
- BluetoothRouteInfo bluetoothRouteInfo = mBluetoothRoutes.get(deviceAddress);
-
- if (bluetoothRouteInfo == null) {
- Slog.w(TAG, "Cannot find bluetooth route for " + deviceAddress);
- return false;
- }
-
- mSelectedBluetoothRoute = bluetoothRouteInfo;
- setRouteConnectionState(mSelectedBluetoothRoute, STATE_CONNECTED);
-
- updateConnectivityStateForDevicesInTheSameGroup();
-
- return true;
- }
+ @Nullable
+ public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) {
+ BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address);
+ // TODO: b/305199571 - Optimize the following statement to avoid creating the full
+ // MediaRoute2Info instance. We just need the id.
+ return bluetoothDevice != null
+ ? createBluetoothRoute(bluetoothDevice).mRoute.getId()
+ : null;
}
- /**
- * Updates connectivity state for devices in the same devices group.
- *
- * <p>{@link BluetoothProfile#LE_AUDIO} and {@link BluetoothProfile#HEARING_AID} support
- * grouping devices. Devices that belong to the same group should have the same routeId but
- * different physical address.
- *
- * <p>In case one of the devices from the group is selected then other devices should also
- * reflect this by changing their connectivity status to
- * {@link MediaRoute2Info#CONNECTION_STATE_CONNECTED}.
- */
- private void updateConnectivityStateForDevicesInTheSameGroup() {
- synchronized (this) {
- for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRoute.mRoute.getId(), mSelectedBluetoothRoute.mRoute.getId())
- && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
- mSelectedBluetoothRoute.mBtDevice.getAddress())) {
- setRouteConnectionState(btRoute, STATE_CONNECTED);
- }
- }
- }
- }
-
- @Override
- public void transferTo(@Nullable String routeId) {
- if (routeId == null) {
- mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
- return;
- }
-
- BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId);
+ public synchronized void activateBluetoothDeviceWithAddress(String address) {
+ BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(address);
if (btRouteInfo == null) {
- Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId);
+ Slog.w(TAG, "activateBluetoothDeviceWithAddress: Ignoring unknown address " + address);
return;
}
-
mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
}
- @Nullable
- private BluetoothRouteInfo findBluetoothRouteWithRouteId(@Nullable String routeId) {
- if (routeId == null) {
- return null;
- }
- synchronized (this) {
- for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
- return btRouteInfo;
- }
- }
- }
- return null;
- }
-
private void updateBluetoothRoutes() {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
- if (bondedDevices == null) {
- return;
- }
-
synchronized (this) {
mBluetoothRoutes.clear();
-
- // We need to query all available to BT stack devices in order to avoid inconsistency
- // between external services, like, AndroidManager, and BT stack.
+ if (bondedDevices == null) {
+ // Bonded devices is null upon running into a BluetoothAdapter error.
+ Log.w(TAG, "BluetoothAdapter.getBondedDevices returned null.");
+ return;
+ }
+ // We don't clear bonded devices if we receive a null getBondedDevices result, because
+ // that probably means that the bluetooth stack ran into an issue. Not that all devices
+ // have been unpaired.
+ mAddressToBondedDevice =
+ bondedDevices.stream()
+ .collect(
+ Collectors.toMap(
+ BluetoothDevice::getAddress, Function.identity()));
for (BluetoothDevice device : bondedDevices) {
- if (isDeviceConnected(device)) {
+ if (device.isConnected()) {
BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
if (newBtRoute.mConnectedProfiles.size() > 0) {
mBluetoothRoutes.put(device.getAddress(), newBtRoute);
@@ -258,106 +180,51 @@
}
}
- @VisibleForTesting
- /* package */ boolean isDeviceConnected(@NonNull BluetoothDevice device) {
- return device.isConnected();
- }
-
- @Nullable
- @Override
- public MediaRoute2Info getSelectedRoute() {
- synchronized (this) {
- if (mSelectedBluetoothRoute == null) {
- return null;
- }
-
- return mSelectedBluetoothRoute.mRoute;
- }
- }
-
@NonNull
- @Override
- public List<MediaRoute2Info> getTransferableRoutes() {
- List<MediaRoute2Info> routes = getAllBluetoothRoutes();
- synchronized (this) {
- if (mSelectedBluetoothRoute != null) {
- routes.remove(mSelectedBluetoothRoute.mRoute);
- }
- }
- return routes;
- }
-
- @NonNull
- @Override
- public List<MediaRoute2Info> getAllBluetoothRoutes() {
+ public List<MediaRoute2Info> getAvailableBluetoothRoutes() {
List<MediaRoute2Info> routes = new ArrayList<>();
- List<String> routeIds = new ArrayList<>();
-
- MediaRoute2Info selectedRoute = getSelectedRoute();
- if (selectedRoute != null) {
- routes.add(selectedRoute);
- routeIds.add(selectedRoute.getId());
- }
+ Set<String> routeIds = new HashSet<>();
synchronized (this) {
for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- // A pair of hearing aid devices or having the same hardware address
- if (routeIds.contains(btRoute.mRoute.getId())) {
- continue;
+ // See createBluetoothRoute for info on why we do this.
+ if (routeIds.add(btRoute.mRoute.getId())) {
+ routes.add(btRoute.mRoute);
}
- routes.add(btRoute.mRoute);
- routeIds.add(btRoute.mRoute.getId());
}
}
return routes;
}
- @Override
- public boolean updateVolumeForDevices(int devices, int volume) {
- int routeType;
- if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
- routeType = MediaRoute2Info.TYPE_HEARING_AID;
- } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
- | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
- | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
- routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) {
- routeType = MediaRoute2Info.TYPE_BLE_HEADSET;
- } else {
- return false;
- }
-
- synchronized (this) {
- mVolumeMap.put(routeType, volume);
- if (mSelectedBluetoothRoute == null
- || mSelectedBluetoothRoute.mRoute.getType() != routeType) {
- return false;
- }
-
- mSelectedBluetoothRoute.mRoute =
- new MediaRoute2Info.Builder(mSelectedBluetoothRoute.mRoute)
- .setVolume(volume)
- .build();
- }
-
- notifyBluetoothRoutesUpdated();
- return true;
- }
-
private void notifyBluetoothRoutesUpdated() {
mListener.onBluetoothRoutesUpdated();
}
+ /**
+ * Creates a new {@link BluetoothRouteInfo}, including its member {@link
+ * BluetoothRouteInfo#mRoute}.
+ *
+ * <p>The most important logic in this method is around the {@link MediaRoute2Info#getId() route
+ * id} assignment. In some cases we want to group multiple {@link BluetoothDevice bluetooth
+ * devices} as a single media route. For example, the left and right hearing aids get exposed as
+ * two different BluetoothDevice instances, but we want to show them as a single route. In this
+ * case, we assign the same route id to all "group" bluetooth devices (like left and right
+ * hearing aids), so that a single route is exposed for both of them.
+ *
+ * <p>Deduplication by id happens downstream because we need to be able to refer to all
+ * bluetooth devices individually, since the audio stack refers to a bluetooth device group by
+ * any of its member devices.
+ */
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
BluetoothRouteInfo
newBtRoute = new BluetoothRouteInfo();
newBtRoute.mBtDevice = device;
-
- String routeId = device.getAddress();
String deviceName = device.getName();
if (TextUtils.isEmpty(deviceName)) {
deviceName = mContext.getResources().getText(R.string.unknownName).toString();
}
+
+ String routeId = device.getAddress();
int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
newBtRoute.mConnectedProfiles = new SparseBooleanArray();
if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.A2DP, device)) {
@@ -365,7 +232,6 @@
}
if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.HEARING_AID, device)) {
newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
- // Intentionally assign the same ID for a pair of devices to publish only one of them.
routeId = HEARING_AID_ROUTE_ID_PREFIX
+ mBluetoothProfileMonitor.getGroupId(BluetoothProfile.HEARING_AID, device);
type = MediaRoute2Info.TYPE_HEARING_AID;
@@ -377,66 +243,27 @@
type = MediaRoute2Info.TYPE_BLE_HEADSET;
}
- // Current volume will be set when connected.
- newBtRoute.mRoute = new MediaRoute2Info.Builder(routeId, deviceName)
- .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
- .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
- .setDescription(mContext.getResources().getText(
- R.string.bluetooth_a2dp_audio_route_name).toString())
- .setType(type)
- .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setAddress(device.getAddress())
- .build();
+ // Note that volume is only relevant for active bluetooth routes, and those are managed via
+ // AudioManager.
+ newBtRoute.mRoute =
+ new MediaRoute2Info.Builder(routeId, deviceName)
+ .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
+ .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
+ .setDescription(
+ mContext.getResources()
+ .getText(R.string.bluetooth_a2dp_audio_route_name)
+ .toString())
+ .setType(type)
+ .setAddress(device.getAddress())
+ .build();
return newBtRoute;
}
- private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
- @MediaRoute2Info.ConnectionState int state) {
- if (btRoute == null) {
- Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
- return;
- }
- if (btRoute.mRoute.getConnectionState() == state) {
- return;
- }
-
- MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.mRoute)
- .setConnectionState(state);
- builder.setType(btRoute.getRouteType());
-
-
-
- if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
- int currentVolume;
- synchronized (this) {
- currentVolume = mVolumeMap.get(btRoute.getRouteType(), 0);
- }
- builder.setVolume(currentVolume);
- }
-
- btRoute.mRoute = builder.build();
- }
-
private static class BluetoothRouteInfo {
private BluetoothDevice mBtDevice;
private MediaRoute2Info mRoute;
private SparseBooleanArray mConnectedProfiles;
-
- @MediaRoute2Info.Type
- int getRouteType() {
- // Let hearing aid profile have a priority.
- if (mConnectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
- return MediaRoute2Info.TYPE_HEARING_AID;
- }
-
- if (mConnectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) {
- return MediaRoute2Info.TYPE_BLE_HEADSET;
- }
-
- return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- }
}
private class AdapterStateChangedReceiver extends BroadcastReceiver {
@@ -468,9 +295,6 @@
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
- case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
- case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
- case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 6bdfae2..27df00f 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -17,228 +17,590 @@
package com.android.server.media;
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
-import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
-import static android.media.MediaRoute2Info.TYPE_DOCK;
-import static android.media.MediaRoute2Info.TYPE_HDMI;
-import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
-import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
-import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.IAudioService;
import android.media.MediaRoute2Info;
-import android.os.RemoteException;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.media.BluetoothRouteController.NoOpBluetoothRouteController;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+/**
+ * Maintains a list of all available routes and supports transfers to any of them.
+ *
+ * <p>This implementation is intended for use in conjunction with {@link
+ * NoOpBluetoothRouteController}, as it manages bluetooth devices directly.
+ *
+ * <p>This implementation obtains and manages all routes via {@link AudioManager}, with the
+ * exception of {@link AudioManager#handleBluetoothActiveDeviceChanged inactive bluetooth} routes
+ * which are managed by {@link AudioPoliciesBluetoothRouteController}, which depends on the
+ * bluetooth stack (for example {@link BluetoothAdapter}.
+ */
+// TODO: b/305199571 - Rename this class to avoid the AudioPolicies prefix, which has been flagged
+// by the audio team as a confusing name.
/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
-
- private static final String TAG = "APDeviceRoutesController";
+ private static final String TAG = SystemMediaRoute2Provider.TAG;
@NonNull
- private final Context mContext;
- @NonNull
- private final AudioManager mAudioManager;
- @NonNull
- private final IAudioService mAudioService;
+ private static final AudioAttributes MEDIA_USAGE_AUDIO_ATTRIBUTES =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
@NonNull
- private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
- @NonNull
- private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+ private static final SparseArray<SystemRouteInfo> AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO =
+ new SparseArray<>();
- private int mDeviceVolume;
+ @NonNull private final Context mContext;
+ @NonNull private final AudioManager mAudioManager;
+ @NonNull private final Handler mHandler;
+ @NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ @NonNull private final AudioPoliciesBluetoothRouteController mBluetoothRouteController;
@NonNull
- private MediaRoute2Info mDeviceRoute;
- @Nullable
- private MediaRoute2Info mSelectedRoute;
+ private final Map<String, MediaRoute2InfoHolder> mRouteIdToAvailableDeviceRoutes =
+ new HashMap<>();
- @VisibleForTesting
- /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context,
+ @NonNull private final AudioProductStrategy mStrategyForMedia;
+
+ @NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl();
+
+ @NonNull
+ private final AudioManager.OnDevicesForAttributesChangedListener
+ mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener;
+
+ @NonNull private MediaRoute2Info mSelectedRoute;
+
+ // TODO: b/305199571 - Support nullable btAdapter and strategyForMedia which, when null, means
+ // no support for transferring to inactive bluetooth routes and transferring to any routes
+ // respectively.
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ /* package */ AudioPoliciesDeviceRouteController(
+ @NonNull Context context,
@NonNull AudioManager audioManager,
- @NonNull IAudioService audioService,
+ @NonNull Looper looper,
+ @NonNull AudioProductStrategy strategyForMedia,
+ @NonNull BluetoothAdapter btAdapter,
@NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(audioManager);
- Objects.requireNonNull(audioService);
- Objects.requireNonNull(onDeviceRouteChangedListener);
-
- mContext = context;
- mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
-
- mAudioManager = audioManager;
- mAudioService = audioService;
-
- AudioRoutesInfo newAudioRoutes = null;
- try {
- newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
- } catch (RemoteException e) {
- Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
- }
-
- mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ mContext = Objects.requireNonNull(context);
+ mAudioManager = Objects.requireNonNull(audioManager);
+ mHandler = new Handler(Objects.requireNonNull(looper));
+ mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
+ mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
+ mBluetoothRouteController =
+ new AudioPoliciesBluetoothRouteController(
+ mContext, btAdapter, this::rebuildAvailableRoutes);
+ rebuildAvailableRoutes();
}
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
@Override
- public synchronized boolean selectRoute(@Nullable Integer type) {
- if (type == null) {
- mSelectedRoute = null;
- return true;
- }
+ public void start(UserHandle mUser) {
+ mBluetoothRouteController.start(mUser);
+ mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler);
+ mAudioManager.addOnDevicesForAttributesChangedListener(
+ AudioRoutingUtils.ATTRIBUTES_MEDIA,
+ new HandlerExecutor(mHandler),
+ mOnDevicesForAttributesChangedListener);
+ }
- if (!isDeviceRouteType(type)) {
- return false;
- }
-
- mSelectedRoute = createRouteFromAudioInfo(type);
- return true;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public void stop() {
+ mAudioManager.removeOnDevicesForAttributesChangedListener(
+ mOnDevicesForAttributesChangedListener);
+ mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
+ mBluetoothRouteController.stop();
+ mHandler.removeCallbacksAndMessages(/* token= */ null);
}
@Override
@NonNull
public synchronized MediaRoute2Info getSelectedRoute() {
- if (mSelectedRoute != null) {
- return mSelectedRoute;
- }
- return mDeviceRoute;
+ return mSelectedRoute;
}
@Override
+ @NonNull
+ public synchronized List<MediaRoute2Info> getAvailableRoutes() {
+ return mRouteIdToAvailableDeviceRoutes.values().stream()
+ .map(it -> it.mMediaRoute2Info)
+ .toList();
+ }
+
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Override
+ public synchronized void transferTo(@Nullable String routeId) {
+ if (routeId == null) {
+ // This should never happen: This branch should only execute when the matching bluetooth
+ // route controller is not the no-op one.
+ // TODO: b/305199571 - Make routeId non-null and remove this branch once we remove the
+ // legacy route controller implementations.
+ Slog.e(TAG, "Unexpected call to AudioPoliciesDeviceRouteController#transferTo(null)");
+ return;
+ }
+ MediaRoute2InfoHolder mediaRoute2InfoHolder = mRouteIdToAvailableDeviceRoutes.get(routeId);
+ if (mediaRoute2InfoHolder == null) {
+ Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId);
+ return;
+ }
+ if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) {
+ // By default, the last connected device is the active route so we don't need to apply a
+ // routing audio policy.
+ mBluetoothRouteController.activateBluetoothDeviceWithAddress(
+ mediaRoute2InfoHolder.mMediaRoute2Info.getAddress());
+ mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
+ } else {
+ AudioDeviceAttributes attr =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ mediaRoute2InfoHolder.mAudioDeviceInfoType,
+ /* address= */ ""); // This is not a BT device, hence no address needed.
+ mAudioManager.setPreferredDeviceForStrategy(mStrategyForMedia, attr);
+ }
+ }
+
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
public synchronized boolean updateVolume(int volume) {
- if (mDeviceVolume == volume) {
- return false;
- }
-
- mDeviceVolume = volume;
-
- if (mSelectedRoute != null) {
- mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute)
- .setVolume(volume)
- .build();
- }
-
- mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
- .setVolume(volume)
- .build();
-
+ // TODO: b/305199571 - Optimize so that we only update the volume of the selected route. We
+ // don't need to rebuild all available routes.
+ rebuildAvailableRoutes();
return true;
}
- @NonNull
- private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
- int type = TYPE_BUILTIN_SPEAKER;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ private void onDevicesForAttributesChangedListener(
+ AudioAttributes attributes, List<AudioDeviceAttributes> unusedAudioDeviceAttributes) {
+ if (attributes.getUsage() == AudioAttributes.USAGE_MEDIA) {
+ // We only care about the media usage. Ignore everything else.
+ rebuildAvailableRoutes();
+ }
+ }
- if (newRoutes != null) {
- if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
- type = TYPE_WIRED_HEADPHONES;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
- type = TYPE_WIRED_HEADSET;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
- type = TYPE_DOCK;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
- type = TYPE_HDMI;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
- type = TYPE_USB_DEVICE;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ private synchronized void rebuildAvailableRoutes() {
+ List<AudioDeviceAttributes> attributesOfSelectedOutputDevices =
+ mAudioManager.getDevicesForAttributes(MEDIA_USAGE_AUDIO_ATTRIBUTES);
+ int selectedDeviceAttributesType;
+ if (attributesOfSelectedOutputDevices.isEmpty()) {
+ Slog.e(
+ TAG,
+ "Unexpected empty list of output devices for media. Using built-in speakers.");
+ selectedDeviceAttributesType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ } else {
+ if (attributesOfSelectedOutputDevices.size() > 1) {
+ Slog.w(
+ TAG,
+ "AudioManager.getDevicesForAttributes returned more than one element. Using"
+ + " the first one.");
+ }
+ selectedDeviceAttributesType = attributesOfSelectedOutputDevices.get(0).getType();
+ }
+
+ AudioDeviceInfo[] audioDeviceInfos =
+ mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ mRouteIdToAvailableDeviceRoutes.clear();
+ MediaRoute2InfoHolder newSelectedRouteHolder = null;
+ for (AudioDeviceInfo audioDeviceInfo : audioDeviceInfos) {
+ MediaRoute2Info mediaRoute2Info =
+ createMediaRoute2InfoFromAudioDeviceInfo(audioDeviceInfo);
+ // Null means audioDeviceInfo is not a supported media output, like a phone's builtin
+ // earpiece. We ignore those.
+ if (mediaRoute2Info != null) {
+ int audioDeviceInfoType = audioDeviceInfo.getType();
+ MediaRoute2InfoHolder newHolder =
+ MediaRoute2InfoHolder.createForAudioManagerRoute(
+ mediaRoute2Info, audioDeviceInfoType);
+ mRouteIdToAvailableDeviceRoutes.put(mediaRoute2Info.getId(), newHolder);
+ if (selectedDeviceAttributesType == audioDeviceInfoType) {
+ newSelectedRouteHolder = newHolder;
+ }
}
}
- return createRouteFromAudioInfo(type);
- }
-
- @NonNull
- private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) {
- int name = R.string.default_audio_route_name;
- switch (type) {
- case TYPE_WIRED_HEADPHONES:
- case TYPE_WIRED_HEADSET:
- name = R.string.default_audio_route_name_headphones;
- break;
- case TYPE_DOCK:
- name = R.string.default_audio_route_name_dock_speakers;
- break;
- case TYPE_HDMI:
- case TYPE_HDMI_ARC:
- case TYPE_HDMI_EARC:
- name = R.string.default_audio_route_name_external_device;
- break;
- case TYPE_USB_DEVICE:
- name = R.string.default_audio_route_name_usb;
- break;
+ if (mRouteIdToAvailableDeviceRoutes.isEmpty()) {
+ // Due to an unknown reason (possibly an audio server crash), we ended up with an empty
+ // list of routes. Our entire codebase assumes at least one system route always exists,
+ // so we create a placeholder route represented as a built-in speaker for
+ // user-presentation purposes.
+ Slog.e(TAG, "Ended up with an empty list of routes. Creating a placeholder route.");
+ MediaRoute2InfoHolder placeholderRouteHolder = createPlaceholderBuiltinSpeakerRoute();
+ String placeholderRouteId = placeholderRouteHolder.mMediaRoute2Info.getId();
+ mRouteIdToAvailableDeviceRoutes.put(placeholderRouteId, placeholderRouteHolder);
}
- synchronized (this) {
- return new MediaRoute2Info.Builder(
- MediaRoute2Info.ROUTE_ID_DEVICE,
- mContext.getResources().getText(name).toString())
- .setVolumeHandling(
- mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ if (newSelectedRouteHolder == null) {
+ Slog.e(
+ TAG,
+ "Could not map this selected device attribute type to an available route: "
+ + selectedDeviceAttributesType);
+ // We know mRouteIdToAvailableDeviceRoutes is not empty.
+ newSelectedRouteHolder = mRouteIdToAvailableDeviceRoutes.values().iterator().next();
+ }
+ MediaRoute2InfoHolder selectedRouteHolderWithUpdatedVolumeInfo =
+ newSelectedRouteHolder.copyWithVolumeInfoFromAudioManager(mAudioManager);
+ mRouteIdToAvailableDeviceRoutes.put(
+ newSelectedRouteHolder.mMediaRoute2Info.getId(),
+ selectedRouteHolderWithUpdatedVolumeInfo);
+ mSelectedRoute = selectedRouteHolderWithUpdatedVolumeInfo.mMediaRoute2Info;
+
+ // We only add those BT routes that we have not already obtained from audio manager (which
+ // are active).
+ mBluetoothRouteController.getAvailableBluetoothRoutes().stream()
+ .filter(it -> !mRouteIdToAvailableDeviceRoutes.containsKey(it.getId()))
+ .map(MediaRoute2InfoHolder::createForInactiveBluetoothRoute)
+ .forEach(
+ it -> mRouteIdToAvailableDeviceRoutes.put(it.mMediaRoute2Info.getId(), it));
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged();
+ }
+
+ private MediaRoute2InfoHolder createPlaceholderBuiltinSpeakerRoute() {
+ int type = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ return MediaRoute2InfoHolder.createForAudioManagerRoute(
+ createMediaRoute2Info(
+ /* routeId= */ null, type, /* productName= */ null, /* address= */ null),
+ type);
+ }
+
+ @Nullable
+ private MediaRoute2Info createMediaRoute2InfoFromAudioDeviceInfo(
+ AudioDeviceInfo audioDeviceInfo) {
+ String address = audioDeviceInfo.getAddress();
+ // Passing a null route id means we want to get the default id for the route. Generally, we
+ // only expect to pass null for non-Bluetooth routes.
+ String routeId =
+ TextUtils.isEmpty(address)
+ ? null
+ : mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
+ return createMediaRoute2Info(
+ routeId, audioDeviceInfo.getType(), audioDeviceInfo.getProductName(), address);
+ }
+
+ /**
+ * Creates a new {@link MediaRoute2Info} using the provided information.
+ *
+ * @param routeId A route id, or null to use an id pre-defined for the given {@code type}.
+ * @param audioDeviceInfoType The type as obtained from {@link AudioDeviceInfo#getType}.
+ * @param productName The product name as obtained from {@link
+ * AudioDeviceInfo#getProductName()}, or null to use a predefined name for the given {@code
+ * type}.
+ * @param address The type as obtained from {@link AudioDeviceInfo#getAddress()} or {@link
+ * BluetoothDevice#getAddress()}.
+ * @return The new {@link MediaRoute2Info}.
+ */
+ @Nullable
+ private MediaRoute2Info createMediaRoute2Info(
+ @Nullable String routeId,
+ int audioDeviceInfoType,
+ @Nullable CharSequence productName,
+ @Nullable String address) {
+ SystemRouteInfo systemRouteInfo =
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.get(audioDeviceInfoType);
+ if (systemRouteInfo == null) {
+ // Device type that's intentionally unsupported for media output, like the built-in
+ // earpiece.
+ return null;
+ }
+ CharSequence humanReadableName = productName;
+ if (TextUtils.isEmpty(humanReadableName)) {
+ humanReadableName = mContext.getResources().getText(systemRouteInfo.mNameResource);
+ }
+ if (routeId == null) {
+ // The caller hasn't provided an id, so we use a pre-defined one. This happens when we
+ // are creating a non-BT route, or we are creating a BT route but a race condition
+ // caused AudioManager to expose the BT route before BluetoothAdapter, preventing us
+ // from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress.
+ routeId = systemRouteInfo.mDefaultRouteId;
+ }
+ return new MediaRoute2Info.Builder(routeId, humanReadableName)
+ .setType(systemRouteInfo.mMediaRoute2InfoType)
+ .setAddress(address)
+ .setSystemRoute(true)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+ .build();
+ }
+
+ /**
+ * Holds a {@link MediaRoute2Info} and associated information that we don't want to put in the
+ * {@link MediaRoute2Info} class because it's solely necessary for the implementation of this
+ * class.
+ */
+ private static class MediaRoute2InfoHolder {
+
+ public final MediaRoute2Info mMediaRoute2Info;
+ public final int mAudioDeviceInfoType;
+ public final boolean mCorrespondsToInactiveBluetoothRoute;
+
+ public static MediaRoute2InfoHolder createForAudioManagerRoute(
+ MediaRoute2Info mediaRoute2Info, int audioDeviceInfoType) {
+ return new MediaRoute2InfoHolder(
+ mediaRoute2Info,
+ audioDeviceInfoType,
+ /* correspondsToInactiveBluetoothRoute= */ false);
+ }
+
+ public static MediaRoute2InfoHolder createForInactiveBluetoothRoute(
+ MediaRoute2Info mediaRoute2Info) {
+ // There's no corresponding audio device info, hence the audio device info type is
+ // unknown.
+ return new MediaRoute2InfoHolder(
+ mediaRoute2Info,
+ /* audioDeviceInfoType= */ AudioDeviceInfo.TYPE_UNKNOWN,
+ /* correspondsToInactiveBluetoothRoute= */ true);
+ }
+
+ private MediaRoute2InfoHolder(
+ MediaRoute2Info mediaRoute2Info,
+ int audioDeviceInfoType,
+ boolean correspondsToInactiveBluetoothRoute) {
+ mMediaRoute2Info = mediaRoute2Info;
+ mAudioDeviceInfoType = audioDeviceInfoType;
+ mCorrespondsToInactiveBluetoothRoute = correspondsToInactiveBluetoothRoute;
+ }
+
+ public MediaRoute2InfoHolder copyWithVolumeInfoFromAudioManager(
+ AudioManager mAudioManager) {
+ MediaRoute2Info routeInfoWithVolumeInfo =
+ new MediaRoute2Info.Builder(mMediaRoute2Info)
+ .setVolumeHandling(
+ mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
+ .setVolumeMax(
+ mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .build();
+ return new MediaRoute2InfoHolder(
+ routeInfoWithVolumeInfo,
+ mAudioDeviceInfoType,
+ mCorrespondsToInactiveBluetoothRoute);
}
}
/**
- * Checks if the given type is a device route.
- *
- * <p>Device route means a route which is either built-in or wired to the current device.
- *
- * @param type specifies the type of the device.
- * @return {@code true} if the device is wired or built-in and {@code false} otherwise.
+ * Holds route information about an {@link AudioDeviceInfo#getType() audio device info type}.
*/
- private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) {
- switch (type) {
- case TYPE_BUILTIN_SPEAKER:
- case TYPE_WIRED_HEADPHONES:
- case TYPE_WIRED_HEADSET:
- case TYPE_DOCK:
- case TYPE_HDMI:
- case TYPE_HDMI_ARC:
- case TYPE_HDMI_EARC:
- case TYPE_USB_DEVICE:
- return true;
- default:
- return false;
+ private static class SystemRouteInfo {
+ /** The type to use for {@link MediaRoute2Info#getType()}. */
+ public final int mMediaRoute2InfoType;
+
+ /**
+ * Holds the route id to use if no other id is provided.
+ *
+ * <p>We only expect this id to be used for non-bluetooth routes. For bluetooth routes, in a
+ * normal scenario, the id is generated from the device information (like address, or
+ * hiSyncId), and this value is ignored. A non-normal scenario may occur when there's race
+ * condition between {@link BluetoothAdapter} and {@link AudioManager}, who are not
+ * synchronized.
+ */
+ public final String mDefaultRouteId;
+
+ /**
+ * The name to use for {@link MediaRoute2Info#getName()}.
+ *
+ * <p>Usually replaced by the UI layer with a localized string.
+ */
+ public final int mNameResource;
+
+ private SystemRouteInfo(int mediaRoute2InfoType, String defaultRouteId, int nameResource) {
+ mMediaRoute2InfoType = mediaRoute2InfoType;
+ mDefaultRouteId = defaultRouteId;
+ mNameResource = nameResource;
}
}
- private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
-
+ private class AudioDeviceCallbackImpl extends AudioDeviceCallback {
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Override
- public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
- boolean isDeviceRouteChanged;
- MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
-
- synchronized (AudioPoliciesDeviceRouteController.this) {
- mDeviceRoute = deviceRoute;
- isDeviceRouteChanged = mSelectedRoute == null;
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ for (AudioDeviceInfo deviceInfo : addedDevices) {
+ if (AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.contains(deviceInfo.getType())) {
+ // When a new valid media output is connected, we clear any routing policies so
+ // that the default routing logic from the audio framework kicks in. As a result
+ // of this, when the user connects a bluetooth device or a wired headset, the
+ // new device becomes the active route, which is the traditional behavior.
+ mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
+ rebuildAvailableRoutes();
+ break;
+ }
}
+ }
- if (isDeviceRouteChanged) {
- mOnDeviceRouteChangedListener.onDeviceRouteChanged();
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ for (AudioDeviceInfo deviceInfo : removedDevices) {
+ if (AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.contains(deviceInfo.getType())) {
+ rebuildAvailableRoutes();
+ break;
+ }
}
}
}
+ static {
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
+ /* defaultRouteId= */ "ROUTE_ID_BUILTIN_SPEAKER",
+ /* nameResource= */ R.string.default_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_WIRED_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_WIRED_HEADSET",
+ /* nameResource= */ R.string.default_audio_route_name_headphones));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_WIRED_HEADPHONES,
+ /* defaultRouteId= */ "ROUTE_ID_WIRED_HEADPHONES",
+ /* nameResource= */ R.string.default_audio_route_name_headphones));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLUETOOTH_A2DP,
+ /* defaultRouteId= */ "ROUTE_ID_BLUETOOTH_A2DP",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_DOCK,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_DOCK,
+ /* defaultRouteId= */ "ROUTE_ID_DOCK",
+ /* nameResource= */ R.string.default_audio_route_name_dock_speakers));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_USB_DEVICE,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_USB_DEVICE,
+ /* defaultRouteId= */ "ROUTE_ID_USB_DEVICE",
+ /* nameResource= */ R.string.default_audio_route_name_usb));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_USB_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_USB_HEADSET",
+ /* nameResource= */ R.string.default_audio_route_name_usb));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI_ARC,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI_ARC,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI_ARC",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI_EARC,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI_EARC,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI_EARC",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ // TODO: b/305199571 - Add a proper type constants and human readable names for AUX_LINE,
+ // LINE_ANALOG, LINE_DIGITAL, BLE_BROADCAST, BLE_SPEAKER, BLE_HEADSET, and HEARING_AID.
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HEARING_AID,
+ /* defaultRouteId= */ "ROUTE_ID_HEARING_AID",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_BLE_HEADSET",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET, // TODO: b/305199571 - Make a new type.
+ /* defaultRouteId= */ "ROUTE_ID_BLE_SPEAKER",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_BLE_BROADCAST",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_LINE_DIGITAL,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_LINE_DIGITAL",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_LINE_ANALOG,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_LINE_ANALOG",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_AUX_LINE,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_AUX_LINE",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_DOCK_ANALOG,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_DOCK,
+ /* defaultRouteId= */ "ROUTE_ID_DOCK_ANALOG",
+ /* nameResource= */ R.string.default_audio_route_name_dock_speakers));
+ }
}
diff --git a/services/core/java/com/android/server/media/AudioRoutingUtils.java b/services/core/java/com/android/server/media/AudioRoutingUtils.java
new file mode 100644
index 0000000..13f11eb
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioRoutingUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.media;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioProductStrategy;
+
+/** Holds utils related to routing in the audio framework. */
+/* package */ final class AudioRoutingUtils {
+
+ /* package */ static final AudioAttributes ATTRIBUTES_MEDIA =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Nullable
+ /* package */ static AudioProductStrategy getMediaAudioProductStrategy() {
+ for (AudioProductStrategy strategy : AudioManager.getAudioProductStrategies()) {
+ if (strategy.supportsAudioAttributes(AudioRoutingUtils.ATTRIBUTES_MEDIA)) {
+ return strategy;
+ }
+ }
+ return null;
+ }
+
+ private AudioRoutingUtils() {
+ // no-op to prevent instantiation.
+ }
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index 2b01001..74fdf6e 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -44,19 +44,11 @@
@NonNull
static BluetoothRouteController createInstance(@NonNull Context context,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
- Objects.requireNonNull(context);
Objects.requireNonNull(listener);
+ BluetoothAdapter btAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
- BluetoothManager bluetoothManager = (BluetoothManager)
- context.getSystemService(Context.BLUETOOTH_SERVICE);
- BluetoothAdapter btAdapter = bluetoothManager.getAdapter();
-
- if (btAdapter == null) {
+ if (btAdapter == null || Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
return new NoOpBluetoothRouteController();
- }
-
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesBluetoothRouteController(context, btAdapter, listener);
} else {
return new LegacyBluetoothRouteController(context, btAdapter, listener);
}
@@ -74,17 +66,6 @@
*/
void stop();
-
- /**
- * Selects the route with the given {@code deviceAddress}.
- *
- * @param deviceAddress The physical address of the device to select. May be null to unselect
- * the currently selected device.
- * @return Whether the selection succeeds. If the selection fails, the state of the instance
- * remains unaltered.
- */
- boolean selectRoute(@Nullable String deviceAddress);
-
/**
* Transfers Bluetooth output to the given route.
*
@@ -158,12 +139,6 @@
}
@Override
- public boolean selectRoute(String deviceAddress) {
- // no op
- return false;
- }
-
- @Override
public void transferTo(String routeId) {
// no op
}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 0fdaaa7..9f175a9 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -16,17 +16,25 @@
package com.android.server.media;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.media.AudioManager;
-import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Looper;
import android.os.ServiceManager;
+import android.os.UserHandle;
import com.android.media.flags.Flags;
+import java.util.List;
+
/**
* Controls device routes.
*
@@ -37,46 +45,67 @@
*/
/* package */ interface DeviceRouteController {
- /**
- * Returns a new instance of {@link DeviceRouteController}.
- */
- /* package */ static DeviceRouteController createInstance(@NonNull Context context,
+ /** Returns a new instance of {@link DeviceRouteController}. */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ /* package */ static DeviceRouteController createInstance(
+ @NonNull Context context,
+ @NonNull Looper looper,
@NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
AudioManager audioManager = context.getSystemService(AudioManager.class);
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
+ AudioProductStrategy strategyForMedia = AudioRoutingUtils.getMediaAudioProductStrategy();
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesDeviceRouteController(context,
+ BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
+ BluetoothAdapter btAdapter =
+ bluetoothManager != null ? bluetoothManager.getAdapter() : null;
+
+ // TODO: b/305199571 - Make the audio policies implementation work without the need for a
+ // bluetooth adapter or a strategy for media. If no strategy for media is available we can
+ // disallow media router transfers, and without a bluetooth adapter we can remove support
+ // for transfers to inactive bluetooth routes.
+ if (strategyForMedia != null
+ && btAdapter != null
+ && Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ return new AudioPoliciesDeviceRouteController(
+ context,
audioManager,
- audioService,
+ looper,
+ strategyForMedia,
+ btAdapter,
onDeviceRouteChangedListener);
} else {
- return new LegacyDeviceRouteController(context,
- audioManager,
- audioService,
- onDeviceRouteChangedListener);
+ IAudioService audioService =
+ IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
+ return new LegacyDeviceRouteController(
+ context, audioManager, audioService, onDeviceRouteChangedListener);
}
}
- /**
- * Select the route with the given built-in or wired {@link MediaRoute2Info.Type}.
- *
- * <p>If the type is {@code null} then unselects the route and falls back to the default device
- * route observed from
- * {@link com.android.server.audio.AudioService#startWatchingRoutes(IAudioRoutesObserver)}.
- *
- * @param type device type. May be {@code null} to unselect currently selected route.
- * @return whether the selection succeeds. If the selection fails the state of the controller
- * remains intact.
- */
- boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
-
/** Returns the currently selected device (built-in or wired) route. */
@NonNull
MediaRoute2Info getSelectedRoute();
/**
+ * Returns all available routes.
+ *
+ * <p>Note that this method returns available routes including the selected route because (a)
+ * this interface doesn't guarantee that the internal state of the controller won't change
+ * between calls to {@link #getSelectedRoute()} and this method and (b) {@link
+ * #getSelectedRoute()} may be treated as a transferable route (not a selected route) if the
+ * selected route is from {@link BluetoothRouteController}.
+ */
+ List<MediaRoute2Info> getAvailableRoutes();
+
+ /**
+ * Transfers device output to the given route.
+ *
+ * <p>If the route is {@code null} then active route will be deactivated.
+ *
+ * @param routeId to switch to or {@code null} to unset the active device.
+ */
+ void transferTo(@Nullable String routeId);
+
+ /**
* Updates device route volume.
*
* @param volume specifies a volume for the device route or 0 for unknown.
@@ -85,6 +114,18 @@
boolean updateVolume(int volume);
/**
+ * Starts listening for changes in the system to keep an up to date view of available and
+ * selected devices.
+ */
+ void start(UserHandle mUser);
+
+ /**
+ * Stops keeping the internal state up to date with the system, releasing any resources acquired
+ * in {@link #start}
+ */
+ void stop();
+
+ /**
* Interface for receiving events when device route has changed.
*/
interface OnDeviceRouteChangedListener {
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index ba3cecf..041fceaf 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -132,12 +132,6 @@
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
- @Override
- public boolean selectRoute(String deviceAddress) {
- // No-op as the class decides if a route is selected based on Bluetooth events.
- return false;
- }
-
/**
* Transfers to a given bluetooth route.
* The dedicated BT device with the route would be activated.
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 65874e2..c0f2834 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -35,11 +35,13 @@
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -73,7 +75,6 @@
private int mDeviceVolume;
private MediaRoute2Info mDeviceRoute;
- @VisibleForTesting
/* package */ LegacyDeviceRouteController(@NonNull Context context,
@NonNull AudioManager audioManager,
@NonNull IAudioService audioService,
@@ -100,9 +101,13 @@
}
@Override
- public boolean selectRoute(@Nullable Integer type) {
- // No-op as the controller does not support selection from the outside of the class.
- return false;
+ public void start(UserHandle mUser) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do.
}
@Override
@@ -112,6 +117,17 @@
}
@Override
+ public synchronized List<MediaRoute2Info> getAvailableRoutes() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public synchronized void transferTo(@Nullable String routeId) {
+ // Unsupported. This implementation doesn't support transferable routes (always exposes a
+ // single non-bluetooth route).
+ }
+
+ @Override
public synchronized boolean updateVolume(int volume) {
if (mDeviceVolume == volume) {
return false;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index c8dba80..86d7833 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,15 +16,12 @@
package com.android.server.media;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioAttributes;
-import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
@@ -51,7 +48,8 @@
*/
// TODO: check thread safety. We may need to use lock to protect variables.
class SystemMediaRoute2Provider extends MediaRoute2Provider {
- private static final String TAG = "MR2SystemProvider";
+ // Package-visible to use this tag for all system routing logic (done across multiple classes).
+ /* package */ static final String TAG = "MR2SystemProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final ComponentName COMPONENT_NAME = new ComponentName(
@@ -77,26 +75,6 @@
private final AudioManagerBroadcastReceiver mAudioReceiver =
new AudioManagerBroadcastReceiver();
- private final AudioManager.OnDevicesForAttributesChangedListener
- mOnDevicesForAttributesChangedListener =
- new AudioManager.OnDevicesForAttributesChangedListener() {
- @Override
- public void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
- @NonNull List<AudioDeviceAttributes> devices) {
- if (attributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
- return;
- }
-
- mHandler.post(() -> {
- updateSelectedAudioDevice(devices);
- notifyProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- }
- };
-
private final Object mRequestLock = new Object();
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
@@ -106,7 +84,8 @@
mIsSystemRouteProvider = true;
mContext = context;
mUser = user;
- mHandler = new Handler(Looper.getMainLooper());
+ Looper looper = Looper.getMainLooper();
+ mHandler = new Handler(looper);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -123,25 +102,15 @@
mDeviceRouteController =
DeviceRouteController.createInstance(
context,
- () -> {
- mHandler.post(
- () -> {
- publishProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- });
-
- mAudioManager.addOnDevicesForAttributesChangedListener(
- AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(),
- mOnDevicesForAttributesChangedListener);
-
- // These methods below should be called after all fields are initialized, as they
- // access the fields inside.
- List<AudioDeviceAttributes> devices =
- mAudioManager.getDevicesForAttributes(AudioAttributesUtils.ATTRIBUTES_MEDIA);
- updateSelectedAudioDevice(devices);
+ looper,
+ () ->
+ mHandler.post(
+ () -> {
+ publishProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
+ }));
updateProviderState();
updateSessionInfosIfNeeded();
}
@@ -151,20 +120,21 @@
intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
mContext.registerReceiverAsUser(mAudioReceiver, mUser,
intentFilter, null, null);
-
- mHandler.post(() -> {
- mBluetoothRouteController.start(mUser);
- notifyProviderState();
- });
- updateVolume();
+ mHandler.post(
+ () -> {
+ mDeviceRouteController.start(mUser);
+ mBluetoothRouteController.start(mUser);
+ });
}
public void stop() {
mContext.unregisterReceiver(mAudioReceiver);
- mHandler.post(() -> {
- mBluetoothRouteController.stop();
- notifyProviderState();
- });
+ mHandler.post(
+ () -> {
+ mBluetoothRouteController.stop();
+ mDeviceRouteController.stop();
+ notifyProviderState();
+ });
}
@Override
@@ -225,13 +195,26 @@
public void transferToRoute(long requestId, String sessionId, String routeId) {
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
// The currently selected route is the default route.
+ Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
return;
}
-
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
- if (TextUtils.equals(routeId, selectedDeviceRoute.getId())) {
+ boolean isAvailableDeviceRoute =
+ mDeviceRouteController.getAvailableRoutes().stream()
+ .anyMatch(it -> it.getId().equals(routeId));
+ boolean isSelectedDeviceRoute = TextUtils.equals(routeId, selectedDeviceRoute.getId());
+
+ if (isSelectedDeviceRoute || isAvailableDeviceRoute) {
+ // The requested route is managed by the device route controller. Note that the selected
+ // device route doesn't necessarily match mSelectedRouteId (which is the selected route
+ // of the routing session). If the selected device route is transferred to, we need to
+ // make the bluetooth routes inactive so that the device route becomes the selected
+ // route of the routing session.
+ mDeviceRouteController.transferTo(routeId);
mBluetoothRouteController.transferTo(null);
} else {
+ // The requested route is managed by the bluetooth route controller.
+ mDeviceRouteController.transferTo(null);
mBluetoothRouteController.transferTo(routeId);
}
}
@@ -280,41 +263,38 @@
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
- RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- SYSTEM_SESSION_ID, packageName).setSystemSession(true);
+ RoutingSessionInfo.Builder builder =
+ new RoutingSessionInfo.Builder(SYSTEM_SESSION_ID, packageName)
+ .setSystemSession(true);
builder.addSelectedRoute(selectedDeviceRoute.getId());
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addTransferableRoute(route.getId());
}
+
+ if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
+ if (!TextUtils.equals(selectedDeviceRoute.getId(), route.getId())) {
+ builder.addTransferableRoute(route.getId());
+ }
+ }
+ }
return builder.setProviderId(mUniqueId).build();
}
}
- private void updateSelectedAudioDevice(@NonNull List<AudioDeviceAttributes> devices) {
- if (devices.isEmpty()) {
- Slog.w(TAG, "The list of preferred devices was empty.");
- return;
- }
-
- AudioDeviceAttributes audioDeviceAttributes = devices.get(0);
-
- if (AudioAttributesUtils.isDeviceOutputAttributes(audioDeviceAttributes)) {
- mDeviceRouteController.selectRoute(
- AudioAttributesUtils.mapToMediaRouteType(audioDeviceAttributes));
- mBluetoothRouteController.selectRoute(null);
- } else if (AudioAttributesUtils.isBluetoothOutputAttributes(audioDeviceAttributes)) {
- mDeviceRouteController.selectRoute(null);
- mBluetoothRouteController.selectRoute(audioDeviceAttributes.getAddress());
- } else {
- Slog.w(TAG, "Unknown audio attributes: " + audioDeviceAttributes);
- }
- }
-
private void updateProviderState() {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
// We must have a device route in the provider info.
- builder.addRoute(mDeviceRouteController.getSelectedRoute());
+ if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ List<MediaRoute2Info> deviceRoutes = mDeviceRouteController.getAvailableRoutes();
+ for (MediaRoute2Info route : deviceRoutes) {
+ builder.addRoute(route);
+ }
+ setProviderState(builder.build());
+ } else {
+ builder.addRoute(mDeviceRouteController.getSelectedRoute());
+ }
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addRoute(route);
@@ -352,7 +332,12 @@
.setProviderId(mUniqueId)
.build();
builder.addSelectedRoute(mSelectedRouteId);
-
+ for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
+ String routeId = route.getId();
+ if (!mSelectedRouteId.equals(routeId)) {
+ builder.addTransferableRoute(routeId);
+ }
+ }
for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
builder.addTransferableRoute(route.getId());
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3b3d79e..07e0ddf 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -357,6 +357,12 @@
final DeletePackageAction action;
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Attempted to remove non-existent package " + packageName);
+ }
+ return false;
+ }
final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
if (PackageManagerServiceUtils.isSystemApp(ps)
&& mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 0a81b2b..5494bd9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -539,6 +539,12 @@
}
@Nullable
+ public PackageSetting getScanRequestDisabledPackageSetting() {
+ assertScanResultExists();
+ return mScanResult.mRequest.mDisabledPkgSetting;
+ }
+
+ @Nullable
public String getRealPackageName() {
assertScanResultExists();
return mScanResult.mRequest.mRealPkgName;
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index d0fdfa9..9384c13 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -856,9 +856,9 @@
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? installRequest.getDisabledPackageSetting() == null
+ ? installRequest.getScanRequestDisabledPackageSetting() == null
? installRequest.getScanRequestOldPackageSetting()
- : installRequest.getDisabledPackageSetting()
+ : installRequest.getScanRequestDisabledPackageSetting()
: null;
if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
|| updatedSystemPs.getPkg().getLibraryNames() == null)) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 73c4224..e817df8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3638,15 +3638,6 @@
return true;
}
break;
- case KeyEvent.KEYCODE_SPACE:
- // Handle keyboard layout switching. (META + SPACE)
- if (firstDown && event.isMetaPressed()) {
- int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- sendSwitchKeyboardLayout(event, direction);
- logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
- return true;
- }
- break;
case KeyEvent.KEYCODE_META_LEFT:
case KeyEvent.KEYCODE_META_RIGHT:
if (down) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index e3f3638..5d90851 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -32,7 +32,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
@@ -41,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ConcurrentUtils;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -809,12 +809,13 @@
private static Policy fromSettings(String settings, String deviceSpecificSettings,
DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
- final KeyValueListParser parser = new KeyValueListParser(',');
+ final UserSettingDeviceConfigMediator userSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
configSuffix = TextUtils.emptyIfNull(configSuffix);
// Device-specific parameters.
try {
- parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
+ userSettingDeviceConfigMediator.setSettingsString(deviceSpecificSettings);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad device specific battery saver constants: "
+ deviceSpecificSettings);
@@ -822,68 +823,58 @@
// Non-device-specific parameters.
try {
- parser.setString(settings == null ? "" : settings);
+ userSettingDeviceConfigMediator.setSettingsString(settings);
+ userSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad battery saver constants: " + settings);
}
// The Settings value overrides everything, since that will be set by the user.
// The DeviceConfig value takes second place, with the default as the last choice.
- final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
- properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
- defaultPolicy.adjustBrightnessFactor));
- final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
- properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
- defaultPolicy.advertiseIsEnabled));
- final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
- properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
- defaultPolicy.deferFullBackup));
- final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
- properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
- defaultPolicy.deferKeyValueBackup));
- final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
- properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
- defaultPolicy.disableAnimation));
- final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
- properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
- defaultPolicy.disableAod));
- final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
- properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
- defaultPolicy.disableLaunchBoost));
- final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
- properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
- defaultPolicy.disableOptionalSensors));
- final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
- properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
- defaultPolicy.disableVibration));
- final boolean enableBrightnessAdjustment = parser.getBoolean(
- KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
- properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
- defaultPolicy.enableAdjustBrightness));
- final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
- properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
- defaultPolicy.enableDataSaver));
- final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
- properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
- defaultPolicy.enableFirewall));
- final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
- properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
- defaultPolicy.enableNightMode));
- final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
- properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
- defaultPolicy.enableQuickDoze));
- final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
- properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
- defaultPolicy.forceAllAppsStandby));
- final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
- properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
- defaultPolicy.forceBackgroundCheck));
- final int locationMode = parser.getInt(KEY_LOCATION_MODE,
- properties.getInt(KEY_LOCATION_MODE + configSuffix,
- defaultPolicy.locationMode));
- final int soundTriggerMode = parser.getInt(KEY_SOUNDTRIGGER_MODE,
- properties.getInt(KEY_SOUNDTRIGGER_MODE + configSuffix,
- defaultPolicy.soundTriggerMode));
+ final float adjustBrightnessFactor = userSettingDeviceConfigMediator.getFloat(
+ KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
+ defaultPolicy.adjustBrightnessFactor);
+ final boolean advertiseIsEnabled = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ADVERTISE_IS_ENABLED + configSuffix,
+ defaultPolicy.advertiseIsEnabled);
+ final boolean deferFullBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_FULL_BACKUP + configSuffix, defaultPolicy.deferFullBackup);
+ final boolean deferKeyValueBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
+ defaultPolicy.deferKeyValueBackup);
+ final boolean disableAnimation = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_ANIMATION + configSuffix, defaultPolicy.disableAnimation);
+ final boolean disableAod = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_AOD + configSuffix, defaultPolicy.disableAod);
+ final boolean disableLaunchBoost = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_LAUNCH_BOOST + configSuffix,
+ defaultPolicy.disableLaunchBoost);
+ final boolean disableOptionalSensors = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
+ defaultPolicy.disableOptionalSensors);
+ final boolean disableVibrationConfig = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_VIBRATION + configSuffix, defaultPolicy.disableVibration);
+ final boolean enableBrightnessAdjustment = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
+ defaultPolicy.enableAdjustBrightness);
+ final boolean enableDataSaver = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_DATASAVER + configSuffix, defaultPolicy.enableDataSaver);
+ final boolean enableFirewall = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_FIREWALL + configSuffix, defaultPolicy.enableFirewall);
+ final boolean enableNightMode = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_NIGHT_MODE + configSuffix, defaultPolicy.enableNightMode);
+ final boolean enableQuickDoze = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_QUICK_DOZE + configSuffix, defaultPolicy.enableQuickDoze);
+ final boolean forceAllAppsStandby = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
+ defaultPolicy.forceAllAppsStandby);
+ final boolean forceBackgroundCheck = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_BACKGROUND_CHECK + configSuffix,
+ defaultPolicy.forceBackgroundCheck);
+ final int locationMode = userSettingDeviceConfigMediator.getInt(
+ KEY_LOCATION_MODE + configSuffix, defaultPolicy.locationMode);
+ final int soundTriggerMode = userSettingDeviceConfigMediator.getInt(
+ KEY_SOUNDTRIGGER_MODE + configSuffix, defaultPolicy.soundTriggerMode);
return new Policy(
adjustBrightnessFactor,
advertiseIsEnabled,
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dd39fb0..c17d6ab 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -51,6 +51,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Objects;
/** An hint service implementation that runs in System Server process. */
@@ -544,7 +545,11 @@
if (mHalSessionPtr == 0) return;
mNativeWrapper.halCloseHintSession(mHalSessionPtr);
mHalSessionPtr = 0;
- mToken.unlinkToDeath(this, 0);
+ try {
+ mToken.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException ignored) {
+ Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
+ }
}
synchronized (mLock) {
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 0656a6a..59766ec 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -35,6 +35,7 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
+import static android.hardware.SensorPrivacyManager.EXTRA_NOTIFICATION_ID;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
import static android.hardware.SensorPrivacyManager.EXTRA_TOGGLE_TYPE;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
@@ -164,6 +165,7 @@
private final AppOpsManagerInternal mAppOpsManagerInternal;
private final TelephonyManager mTelephonyManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private final NotificationManager mNotificationManager;
private CameraPrivacyLightController mCameraPrivacyLightController;
@@ -188,6 +190,7 @@
mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
}
@@ -288,9 +291,18 @@
@Override
public void onReceive(Context context, Intent intent) {
setToggleSensorPrivacy(
- ((UserHandle) intent.getParcelableExtra(
- Intent.EXTRA_USER, android.os.UserHandle.class)).getIdentifier(), OTHER,
- intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
+ intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class)
+ .getIdentifier(),
+ OTHER,
+ intent.getIntExtra(EXTRA_SENSOR, UNKNOWN),
+ false
+ );
+
+ int notificationId =
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, SystemMessage.NOTE_UNKNOWN);
+ if (notificationId != SystemMessage.NOTE_UNKNOWN) {
+ mNotificationManager.cancel(notificationId);
+ }
}
}, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY),
MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
@@ -635,8 +647,6 @@
notificationId = SystemMessage.NOTE_UNBLOCK_CAM_TOGGLE;
}
- NotificationManager notificationManager =
- mContext.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(
SENSOR_PRIVACY_CHANNEL_ID,
getUiContext().getString(R.string.sensor_privacy_notification_channel_label),
@@ -646,7 +656,7 @@
channel.enableVibration(false);
channel.setBlockable(false);
- notificationManager.createNotificationChannel(channel);
+ mNotificationManager.createNotificationChannel(channel);
Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
@@ -669,10 +679,11 @@
new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY)
.setPackage(mContext.getPackageName())
.putExtra(EXTRA_SENSOR, sensor)
+ .putExtra(EXTRA_NOTIFICATION_ID, notificationId)
.putExtra(Intent.EXTRA_USER, user),
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT);
- notificationManager.notify(notificationId,
+ mNotificationManager.notify(notificationId,
new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
.setContentTitle(contentTitle)
.setContentText(contentText)
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index e6273d3..0467d0c 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -38,6 +38,7 @@
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
+import android.media.tv.ad.ITvAdManager;
import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
import android.media.tv.interactive.ITvInteractiveAppManager;
@@ -345,6 +346,7 @@
Slogf.d(TAG, "onStart");
}
publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
+ publishBinderService(Context.TV_AD_SERVICE, new TvAdBinderService());
}
@Override
@@ -688,6 +690,12 @@
}
return session;
}
+ private final class TvAdBinderService extends ITvAdManager.Stub {
+ @Override
+ public void startAdService(IBinder sessionToken, int userId) {
+ }
+
+ }
private final class BinderService extends ITvInteractiveAppManager.Stub {
diff --git a/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
new file mode 100644
index 0000000..e542349
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+import android.util.KeyValueListParser;
+
+/**
+ * Helper class to mediate the value to use when a constant exists in both a key=value pair Settings
+ * constant (that can be parsed by {@link KeyValueListParser})
+ * and the {@link DeviceConfig} properties.
+ */
+public abstract class UserSettingDeviceConfigMediator {
+ private static final String TAG = UserSettingDeviceConfigMediator.class.getSimpleName();
+
+ @Nullable
+ protected DeviceConfig.Properties mProperties;
+ @NonNull
+ protected final KeyValueListParser mSettingsParser;
+
+ /**
+ * @param keyValueListDelimiter The delimiter passed into the {@link KeyValueListParser}.
+ */
+ protected UserSettingDeviceConfigMediator(char keyValueListDelimiter) {
+ mSettingsParser = new KeyValueListParser(keyValueListDelimiter);
+ }
+
+ /**
+ * Sets the key=value list string to read from. Setting {@code null} will clear any previously
+ * set string.
+ */
+ public void setSettingsString(@Nullable String settings) {
+ mSettingsParser.setString(settings);
+ }
+
+ /**
+ * Sets the DeviceConfig Properties to read from. Setting {@code null} will clear any previously
+ * set properties.
+ */
+ public void setDeviceConfigProperties(@Nullable DeviceConfig.Properties properties) {
+ mProperties = properties;
+ }
+
+ /**
+ * Get the value for key as a boolean.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract boolean getBoolean(@NonNull String key, boolean defaultValue);
+
+ /**
+ * Get the value for key as a float.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract float getFloat(@NonNull String key, float defaultValue);
+
+ /**
+ * Get the value for key as an int.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract int getInt(@NonNull String key, int defaultValue);
+
+ /**
+ * Get the value for key as a long.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract long getLong(@NonNull String key, long defaultValue);
+
+ /**
+ * Get the value for key as a String.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract String getString(@NonNull String key, @Nullable String defaultValue);
+
+ /**
+ * A mediator in which the existence of a single settings key-value pair will override usage
+ * of DeviceConfig properties. That is, if the Settings constant has any values set,
+ * then everything in the DeviceConfig namespace will be ignored.
+ */
+ public static class SettingsOverridesAllMediator extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesAllMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getBoolean(key, defaultValue);
+ }
+ return mSettingsParser.getBoolean(key, defaultValue);
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue);
+ }
+ return mSettingsParser.getFloat(key, defaultValue);
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue);
+ }
+ return mSettingsParser.getInt(key, defaultValue);
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue);
+ }
+ return mSettingsParser.getLong(key, defaultValue);
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getString(key, defaultValue);
+ }
+ return mSettingsParser.getString(key, defaultValue);
+ }
+ }
+
+ /**
+ * A mediator in which only individual keys in the DeviceConfig namespace will be overridden
+ * by the same key in the Settings constant. If the Settings constant does not have a specific
+ * key set, then the DeviceConfig value will be used instead.
+ */
+ public static class SettingsOverridesIndividualMediator
+ extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesIndividualMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ return mSettingsParser.getBoolean(key,
+ mProperties == null ? defaultValue : mProperties.getBoolean(key, defaultValue));
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ return mSettingsParser.getFloat(key,
+ mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue));
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ return mSettingsParser.getInt(key,
+ mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue));
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ return mSettingsParser.getLong(key,
+ mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue));
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ return mSettingsParser.getString(key,
+ mProperties == null ? defaultValue : mProperties.getString(key, defaultValue));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index b12da61..e9c4096 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -258,6 +258,11 @@
}
@Override // Binder call
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ return WebViewUpdateService.this.mImpl.getDefaultWebViewPackage();
+ }
+
+ @Override // Binder call
public WebViewProviderInfo[] getAllWebViewPackages() {
return WebViewUpdateService.this.mImpl.getWebViewPackages();
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 26c3fba..60dc4ff 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -421,6 +421,13 @@
return providers;
}
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ throw new IllegalStateException(
+ "getDefaultWebViewPackage shouldn't be called if update_service_v2 flag is"
+ + " disabled.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 89cb4c8..29782d9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -24,6 +24,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
@@ -374,6 +375,23 @@
return providers;
}
+ /**
+ * Returns the default WebView provider which should be first availableByDefault option in the
+ * system config.
+ */
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.availableByDefault) {
+ return provider;
+ }
+ }
+ // This should be unreachable because the config parser enforces that there is at least one
+ // availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
index a9c3dc4..1772ef9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
@@ -40,6 +40,8 @@
WebViewProviderInfo[] getValidWebViewPackages();
+ WebViewProviderInfo getDefaultWebViewPackage();
+
PackageInfo getCurrentWebViewPackage();
boolean isMultiProcessEnabled();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6302e0..bbaa691 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,6 +1582,7 @@
// An activity has changed order/visibility or the task is occluded by a transient
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
+ && !transitionController.hasOrderChanges()
&& !transitionController.isTransientHide(startedActivityRootTask)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 39e900a..4625b4fe 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -60,9 +60,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import com.android.server.am.PendingIntentRecord;
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.util.HashMap;
@@ -236,6 +236,7 @@
private final @ActivityManager.ProcessState int mCallingUidProcState;
private final boolean mIsCallingUidPersistentSystemProcess;
private final BackgroundStartPrivileges mBalAllowedByPiSender;
+ private final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
private final BackgroundStartPrivileges mBalAllowedByPiCreator;
private final String mRealCallingPackage;
private final int mRealCallingUid;
@@ -267,20 +268,33 @@
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
if (originatingPendingIntent == null) {
- // grant creator BAL privileges unless explicitly opted out
- mBalAllowedByPiCreator =
+ // grant BAL privileges unless explicitly opted out
+ mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
? BackgroundStartPrivileges.NONE
: BackgroundStartPrivileges.ALLOW_BAL;
+ mBalAllowedByPiSender =
+ checkedOptions.getPendingIntentBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
} else {
// for PendingIntents we restrict BAL based on target_sdk
- mBalAllowedByPiCreator = getBackgroundStartPrivilegesAllowedByCreator(
+ mBalAllowedByPiCreatorWithHardening = getBackgroundStartPrivilegesAllowedByCreator(
callingUid, callingPackage, checkedOptions);
+ final BackgroundStartPrivileges mBalAllowedByPiCreatorWithoutHardening =
+ checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ mBalAllowedByPiCreator = balRequireOptInByPendingIntentCreator()
+ ? mBalAllowedByPiCreatorWithHardening
+ : mBalAllowedByPiCreatorWithoutHardening;
+ mBalAllowedByPiSender =
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid, mRealCallingPackage);
}
- mBalAllowedByPiSender =
- PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
- checkedOptions, realCallingUid, mRealCallingPackage);
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -319,10 +333,6 @@
return BackgroundStartPrivileges.NONE;
case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
// no explicit choice by the app - let us decide what to do
- if (!balRequireOptInByPendingIntentCreator()) {
- // if feature is disabled allow
- return BackgroundStartPrivileges.ALLOW_BAL;
- }
if (callingPackage != null) {
// determine based on the calling/creating package
boolean changeEnabled = CompatChanges.isChangeEnabled(
@@ -367,11 +377,6 @@
return mOriginatingPendingIntent != null && hasRealCaller();
}
- private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
- Preconditions.checkState(!isPendingIntent());
- return dump(resultIfPiCreatorAllowsBal, null);
- }
-
private boolean callerIsRealCaller() {
return mCallingUid == mRealCallingUid;
}
@@ -396,6 +401,8 @@
sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
}
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
+ sb.append("; balAllowedByPiCreatorWithHardening: ")
+ .append(mBalAllowedByPiCreatorWithHardening);
sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
sb.append("; hasRealCaller: ").append(hasRealCaller());
sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -579,11 +586,12 @@
resultForCaller.allows() && resultForRealCaller.blocks());
}
+ // Handle cases with explicit opt-in
if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+ Slog.d(TAG, "Activity start explicitly allowed by caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForCaller, state);
@@ -592,11 +600,12 @@
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+ Slog.d(TAG, "Activity start explicitly allowed by real caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForRealCaller, state);
}
+ // Handle PendingIntent cases with default behavior next
boolean callerCanAllow = resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -716,17 +725,18 @@
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
final boolean appSwitchAllowedOrFg =
appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
- final boolean allowCallingUidStartActivity =
- ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
- && callingUidHasAnyVisibleWindow)
- || isCallingUidPersistentSystemProcess;
- if (allowCallingUidStartActivity) {
+ if (appSwitchAllowedOrFg && callingUidHasAnyVisibleWindow) {
return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false,
- "callingUidHasAnyVisibleWindow = "
- + callingUid
- + ", isCallingUidPersistentSystemProcess = "
- + isCallingUidPersistentSystemProcess);
+ /*background*/ false, "callingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) {
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ /*background*/ false, "callingUid has non-app visible window");
+ }
+
+ if (isCallingUidPersistentSystemProcess) {
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ false, "callingUid is persistent system process");
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
@@ -806,13 +816,29 @@
"realCallingUid has BAL permission.");
}
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
- if (state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false,
- "realCallingUid has visible (non-toast) window.");
+ // Normal apps with visible app window will be allowed to start activity if app switching
+ // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+ final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
+ || state.mAppSwitchState == APP_SWITCH_FG_ONLY;
+ if (Flags.balImproveRealCallerVisibilityCheck()) {
+ if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has non-app visible window");
+ }
+ } else {
+ // don't abort if the realCallingUid has a visible window
+ // TODO(b/171459802): We should check appSwitchAllowed also
+ if (state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
+ "realCallingUid has visible (non-toast) window.");
+ }
}
+
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2fabb0e..7ce9de4 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -83,6 +83,7 @@
/**
* Mark all dims as pending completion on the next call to {@link #updateDims}
*
+ * Called before iterating on mHost's children, first step of dimming.
* This is intended for us by the host container, to be called at the beginning of
* {@link WindowContainer#prepareSurfaces}. After calling this, the container should
* chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
@@ -100,8 +101,7 @@
/**
* Call after invoking {@link WindowContainer#prepareSurfaces} on children as
- * described in {@link #resetDimStates}. The dim bounds returned by {@link #resetDimStates}
- * should be set before calling this method.
+ * described in {@link #resetDimStates}.
*
* @param t A transaction in which to update the dims.
* @return true if any Dims were updated.
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
new file mode 100644
index 0000000..e91857f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2023 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;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
+import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+
+/**
+ * Contains the information relative to the changes to apply to the dim layer
+ */
+public class DimmerAnimationHelper {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationHelper" : TAG_WM;
+ private static final int DEFAULT_DIM_ANIM_DURATION_MS = 200;
+
+ /**
+ * Contains the requested changes
+ */
+ static class Change {
+ private float mAlpha = -1f;
+ private int mBlurRadius = -1;
+ private WindowContainer mDimmingContainer = null;
+ private int mRelativeLayer = -1;
+ private static final float EPSILON = 0.0001f;
+
+ Change() {}
+
+ Change(Change other) {
+ mAlpha = other.mAlpha;
+ mBlurRadius = other.mBlurRadius;
+ mDimmingContainer = other.mDimmingContainer;
+ mRelativeLayer = other.mRelativeLayer;
+ }
+
+ // Same alpha and blur
+ boolean hasSameVisualProperties(Change other) {
+ return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius;
+ }
+
+ boolean hasSameDimmingContainer(Change other) {
+ return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer;
+ }
+
+ void inheritPropertiesFromAnimation(AnimationSpec anim) {
+ mAlpha = anim.mCurrentAlpha;
+ mBlurRadius = anim.mCurrentBlur;
+ }
+
+ @Override
+ public String toString() {
+ return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
+ + mDimmingContainer + ", relativePosition=" + mRelativeLayer;
+ }
+ }
+
+ private Change mCurrentProperties = new Change();
+ private Change mRequestedProperties = new Change();
+ private AnimationSpec mAlphaAnimationSpec;
+
+ private final AnimationAdapterFactory mAnimationAdapterFactory;
+ private AnimationAdapter mLocalAnimationAdapter;
+
+ DimmerAnimationHelper(AnimationAdapterFactory animationFactory) {
+ mAnimationAdapterFactory = animationFactory;
+ }
+
+ void setExitParameters() {
+ setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */);
+ setRequestedAppearance(0f /* alpha */, 0 /* blur */);
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+ mRequestedProperties.mDimmingContainer = relativeParent;
+ mRequestedProperties.mRelativeLayer = relativeLayer;
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedAppearance(float alpha, int blurRadius) {
+ mRequestedProperties.mAlpha = alpha;
+ mRequestedProperties.mBlurRadius = blurRadius;
+ }
+
+ /**
+ * Commit the last changes we received. Called after
+ * {@link Change#setExitParameters()},
+ * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedAppearance(float, int)}
+ */
+ void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ if (mRequestedProperties.mDimmingContainer == null) {
+ Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ + "call adjustRelativeLayer?");
+ return;
+ }
+ if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
+ Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ + "does not have a surface");
+ dim.remove(t);
+ return;
+ }
+
+ dim.ensureVisible(t);
+ relativeReparent(dim.mDimSurface,
+ mRequestedProperties.mDimmingContainer.getSurfaceControl(),
+ mRequestedProperties.mRelativeLayer, t);
+
+ if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) {
+ stopCurrentAnimation(dim.mDimSurface);
+
+ if (dim.mSkipAnimation
+ // If the container doesn't change but requests a dim change, then it is
+ // directly providing us the animated values
+ || (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties)
+ && dim.isDimming())) {
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "%s skipping animation and directly setting alpha=%f, blur=%d",
+ dim, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius);
+ setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius, t);
+ dim.mSkipAnimation = false;
+ } else {
+ startAnimation(t, dim);
+ }
+
+ } else if (!dim.isDimming()) {
+ // We are not dimming, so we tried the exit animation but the alpha is already 0,
+ // therefore, let's just remove this surface
+ dim.remove(t);
+ }
+ mCurrentProperties = new Change(mRequestedProperties);
+ }
+
+ private void startAnimation(
+ SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
+ mAlphaAnimationSpec = getRequestedAnimationSpec();
+ mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
+ dim.mHostContainer.mWmService.mSurfaceAnimationRunner);
+
+ float targetAlpha = mRequestedProperties.mAlpha;
+ int targetBlur = mRequestedProperties.mBlurRadius;
+
+ mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
+ ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
+ setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t);
+ if (targetAlpha == 0f && !dim.isDimming()) {
+ dim.remove(t);
+ }
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ });
+ }
+
+ private boolean isAnimating() {
+ return mAlphaAnimationSpec != null;
+ }
+
+ void stopCurrentAnimation(SurfaceControl surface) {
+ if (mLocalAnimationAdapter != null && isAnimating()) {
+ // Save the current animation progress and cancel the animation
+ mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec);
+ mLocalAnimationAdapter.onAnimationCancelled(surface);
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ }
+ }
+
+ private AnimationSpec getRequestedAnimationSpec() {
+ final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
+ final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
+ long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
+ * Math.abs(mRequestedProperties.mAlpha - startAlpha));
+
+ final AnimationSpec spec = new AnimationSpec(
+ new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha),
+ new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius),
+ duration
+ );
+ ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec);
+ return spec;
+ }
+
+ /**
+ * Change the relative parent of this dim layer
+ */
+ void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent,
+ int relativePosition, SurfaceControl.Transaction t) {
+ try {
+ t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
+ } catch (NullPointerException e) {
+ Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
+ }
+ }
+
+ void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) {
+ try {
+ t.setAlpha(sc, alpha);
+ t.setBackgroundBlurRadius(sc, blur);
+ } catch (NullPointerException e) {
+ Log.w(TAG , "Tried to change look of dim " + sc + " after remove", e);
+ }
+ }
+
+ private long getDimDuration(WindowContainer container) {
+ // Use the same duration as the animation on the WindowContainer
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+
+ /**
+ * Collects the animation specifics
+ */
+ static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationSpec" : TAG_WM;
+
+ static class AnimationExtremes<T> {
+ final T mStartValue;
+ final T mFinishValue;
+
+ AnimationExtremes(T fromValue, T toValue) {
+ mStartValue = fromValue;
+ mFinishValue = toValue;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + mStartValue + "->" + mFinishValue + "]";
+ }
+ }
+
+ private final long mDuration;
+ private final AnimationSpec.AnimationExtremes<Float> mAlpha;
+ private final AnimationSpec.AnimationExtremes<Integer> mBlur;
+
+ float mCurrentAlpha = 0;
+ int mCurrentBlur = 0;
+ boolean mStarted = false;
+
+ AnimationSpec(AnimationSpec.AnimationExtremes<Float> alpha,
+ AnimationSpec.AnimationExtremes<Integer> blur, long duration) {
+ mAlpha = alpha;
+ mBlur = blur;
+ mDuration = duration;
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+ if (!mStarted) {
+ // The first frame would end up in the sync transaction, and since this could be
+ // applied after the animation transaction, we avoid putting visible changes here.
+ // The initial state of the animation matches the current state of the dim anyway.
+ mStarted = true;
+ return;
+ }
+ final float fraction = getFraction(currentPlayTime);
+ mCurrentAlpha =
+ fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
+ mCurrentBlur =
+ (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
+ if (sc.isValid()) {
+ t.setAlpha(sc, mCurrentAlpha);
+ t.setBackgroundBlurRadius(sc, mCurrentBlur);
+ } else {
+ Log.w(TAG, "Dimmer#AnimationSpec tried to access " + sc + " after release");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Animation spec: alpha=" + mAlpha + ", blur=" + mBlur;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
+ pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
+ pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
+ pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
+ pw.print(" duration="); pw.println(mDuration);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ALPHA);
+ proto.write(FROM, mAlpha.mStartValue);
+ proto.write(TO, mAlpha.mFinishValue);
+ proto.write(DURATION_MS, mDuration);
+ proto.end(token);
+ }
+ }
+
+ static class AnimationAdapterFactory {
+ public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
+ SurfaceAnimationRunner runner) {
+ return new LocalAnimationAdapter(alphaAnimationSpec, runner);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bb59936..50376fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1648,7 +1648,7 @@
? new Transition.ReadyCondition("displayConfig", this) : null;
if (displayConfig != null) {
mTransitionController.waitFor(displayConfig);
- } else if (mTransitionController.isShellTransitionsEnabled()) {
+ } else if (mTransitionController.isShellTransitionsEnabled() && mLastHasContent) {
Slog.e(TAG, "Display reconfigured outside of a transition: " + this);
}
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index 2549bbf..b5d94a2 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -17,265 +17,203 @@
package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
-import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.AnimationSpecProto.ALPHA;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.graphics.Rect;
import android.util.Log;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import java.io.PrintWriter;
-
class SmoothDimmer extends Dimmer {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
- private static final float EPSILON = 0.0001f;
- // This is in milliseconds.
- private static final int DEFAULT_DIM_ANIM_DURATION = 200;
- DimState mDimState;
- private WindowContainer mLastRequestedDimContainer;
- private final AnimationAdapterFactory mAnimationAdapterFactory;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
+ DimState mDimState;
+ final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
+
+ /**
+ * Controls the dim behaviour
+ */
@VisibleForTesting
class DimState {
- /**
- * The layer where property changes should be invoked on.
- */
- SurfaceControl mDimLayer;
- boolean mDimming;
- boolean mIsVisible;
-
+ /** Related objects */
+ SurfaceControl mDimSurface;
+ final WindowContainer mHostContainer;
+ // The last container to request to dim
+ private WindowContainer mLastRequestedDimContainer;
+ /** Animation */
+ private final DimmerAnimationHelper mAnimationHelper;
+ boolean mSkipAnimation = false;
+ // Determines whether the dim layer should animate before destroying.
+ boolean mAnimateExit = true;
+ /** Surface visibility and bounds */
+ private boolean mIsVisible = false;
// TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
final Rect mDimBounds = new Rect();
- /**
- * Determines whether the dim layer should animate before destroying.
- */
- boolean mAnimateExit = true;
-
- /**
- * Used for Dims not associated with a WindowContainer.
- * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
- * lifecycle.
- */
- boolean mDontReset;
-
- Change mCurrentProperties;
- Change mRequestedProperties;
- private AnimationSpec mAlphaAnimationSpec;
- private AnimationAdapter mLocalAnimationAdapter;
-
- static class Change {
- private float mAlpha = -1f;
- private int mBlurRadius = -1;
- private WindowContainer mDimmingContainer = null;
- private int mRelativeLayer = -1;
- private boolean mSkipAnimation = false;
-
- Change() {}
-
- Change(Change other) {
- mAlpha = other.mAlpha;
- mBlurRadius = other.mBlurRadius;
- mDimmingContainer = other.mDimmingContainer;
- mRelativeLayer = other.mRelativeLayer;
- }
-
- @Override
- public String toString() {
- return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
- + mDimmingContainer + ", relativePosition=" + mRelativeLayer
- + ", skipAnimation=" + mSkipAnimation;
- }
- }
-
- DimState(SurfaceControl dimLayer) {
- mDimLayer = dimLayer;
- mDimming = true;
- mCurrentProperties = new Change();
- mRequestedProperties = new Change();
- }
-
- void setExitParameters(WindowContainer container) {
- setRequestedRelativeParent(container, -1 /* relativeLayer */);
- setRequestedAppearance(0f /* alpha */, 0 /* blur */);
- }
-
- // Sets a requested change without applying it immediately
- void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
- mRequestedProperties.mDimmingContainer = relativeParent;
- mRequestedProperties.mRelativeLayer = relativeLayer;
- }
-
- // Sets a requested change without applying it immediately
- void setRequestedAppearance(float alpha, int blurRadius) {
- mRequestedProperties.mAlpha = alpha;
- mRequestedProperties.mBlurRadius = blurRadius;
- }
-
- /**
- * Commit the last changes we received. Called after
- * {@link Change#setExitParameters(WindowContainer)},
- * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
- * {@link Change#setRequestedAppearance(float, int)}
- */
- void applyChanges(SurfaceControl.Transaction t) {
- if (mRequestedProperties.mDimmingContainer == null) {
- Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
- + "call adjustRelativeLayer?");
- return;
- }
- if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
- Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
- + "does not have a surface");
- return;
- }
- if (!mDimState.mIsVisible) {
- mDimState.mIsVisible = true;
- t.show(mDimState.mDimLayer);
- }
- t.setRelativeLayer(mDimLayer,
- mRequestedProperties.mDimmingContainer.getSurfaceControl(),
- mRequestedProperties.mRelativeLayer);
-
- if (aspectChanged()) {
- if (isAnimating()) {
- mLocalAnimationAdapter.onAnimationCancelled(mDimLayer);
- }
- if (mRequestedProperties.mSkipAnimation
- || (!dimmingContainerChanged() && mDimming)) {
- // If the dimming container has not changed, then it is running its own
- // animation, thus we can directly set the values we get requested, unless it's
- // the exiting animation
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- mDimLayer, mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- mRequestedProperties.mSkipAnimation = false;
- } else {
- startAnimation(t);
- }
- }
- mCurrentProperties = new Change(mRequestedProperties);
- }
-
- private void startAnimation(SurfaceControl.Transaction t) {
- mAlphaAnimationSpec = getRequestedAnimationSpec(mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
- mHost.mWmService.mSurfaceAnimationRunner);
-
- mLocalAnimationAdapter.startAnimation(mDimLayer, t,
- ANIMATION_TYPE_DIMMER, (type, animator) -> {
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- if (mRequestedProperties.mAlpha == 0f && !mDimming) {
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Removing dim surface %s on transaction %s", mDimLayer, t);
- t.remove(mDimLayer);
- }
- mLocalAnimationAdapter = null;
- mAlphaAnimationSpec = null;
- });
- }
-
- private boolean isAnimating() {
- return mAlphaAnimationSpec != null;
- }
-
- private boolean aspectChanged() {
- return Math.abs(mRequestedProperties.mAlpha - mCurrentProperties.mAlpha) > EPSILON
- || mRequestedProperties.mBlurRadius != mCurrentProperties.mBlurRadius;
- }
-
- private boolean dimmingContainerChanged() {
- return mRequestedProperties.mDimmingContainer != mCurrentProperties.mDimmingContainer;
- }
-
- private AnimationSpec getRequestedAnimationSpec(float targetAlpha, int targetBlur) {
- final float startAlpha;
- final int startBlur;
- if (mAlphaAnimationSpec != null) {
- startAlpha = mAlphaAnimationSpec.mCurrentAlpha;
- startBlur = mAlphaAnimationSpec.mCurrentBlur;
- } else {
- startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
- startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
- }
- long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
- * Math.abs(targetAlpha - startAlpha));
-
- ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on dim layer %s, requested by %s, "
- + "alpha: %f -> %f, blur: %d -> %d",
- mDimLayer, mRequestedProperties.mDimmingContainer, startAlpha, targetAlpha,
- startBlur, targetBlur);
- return new AnimationSpec(
- new AnimationExtremes<>(startAlpha, targetAlpha),
- new AnimationExtremes<>(startBlur, targetBlur),
- duration
- );
- }
- }
-
- protected SmoothDimmer(WindowContainer host) {
- this(host, new AnimationAdapterFactory());
- }
-
- @VisibleForTesting
- SmoothDimmer(WindowContainer host, AnimationAdapterFactory animationFactory) {
- super(host);
- mAnimationAdapterFactory = animationFactory;
- }
-
- private DimState obtainDimState(WindowContainer container) {
- if (mDimState == null) {
+ DimState() {
+ mHostContainer = mHost;
+ mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory);
try {
- final SurfaceControl ctl = makeDimLayer();
- mDimState = new DimState(ctl);
+ mDimSurface = makeDimLayer();
} catch (Surface.OutOfResourcesException e) {
Log.w(TAG, "OutOfResourcesException creating dim surface");
}
}
- mLastRequestedDimContainer = container;
- return mDimState;
+ void ensureVisible(SurfaceControl.Transaction t) {
+ if (!mIsVisible) {
+ t.show(mDimSurface);
+ t.setAlpha(mDimSurface, 0f);
+ mIsVisible = true;
+ }
+ }
+
+ void adjustSurfaceLayout(SurfaceControl.Transaction t) {
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
+ t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
+ }
+
+ /**
+ * Set the parameters to prepare the dim to change its appearance
+ */
+ void prepareLookChange(float alpha, int blurRadius) {
+ mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
+ }
+
+ /**
+ * Prepare the dim for the exit animation
+ */
+ void exit(SurfaceControl.Transaction t) {
+ if (!mAnimateExit) {
+ remove(t);
+ } else {
+ mAnimationHelper.setExitParameters();
+ setReady(t);
+ }
+ }
+
+ void remove(SurfaceControl.Transaction t) {
+ mAnimationHelper.stopCurrentAnimation(mDimSurface);
+ if (mDimSurface.isValid()) {
+ t.remove(mDimSurface);
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "Removing dim surface %s on transaction %s", this, t);
+ } else {
+ Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SmoothDimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
+ }
+
+ /**
+ * Set the parameters to prepare the dim to be relative parented to the dimming container
+ */
+ void prepareReparent(WindowContainer relativeParent, int relativeLayer) {
+ mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
+ }
+
+ /**
+ * Call when all the changes have been requested to have them applied
+ * @param t The transaction in which to apply the changes
+ */
+ void setReady(SurfaceControl.Transaction t) {
+ mAnimationHelper.applyChanges(t, this);
+ }
+
+ /**
+ * Whether anyone is currently requesting the dim
+ */
+ boolean isDimming() {
+ return mLastRequestedDimContainer != null;
+ }
+
+ private SurfaceControl makeDimLayer() {
+ return mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer()
+ .setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("DimLayer.makeDimLayer")
+ .build();
+ }
}
- private SurfaceControl makeDimLayer() {
- return mHost.makeChildSurface(null)
- .setParent(mHost.getSurfaceControl())
- .setColorLayer()
- .setName("Dim Layer for - " + mHost.getName())
- .setCallsite("Dimmer.makeDimLayer")
- .build();
+ protected SmoothDimmer(WindowContainer host) {
+ this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
}
- @Override
- SurfaceControl getDimLayer() {
- return mDimState != null ? mDimState.mDimLayer : null;
+ @VisibleForTesting
+ SmoothDimmer(WindowContainer host,
+ DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
+ super(host);
+ mAnimationAdapterFactory = animationFactory;
}
@Override
void resetDimStates() {
+ if (mDimState != null) {
+ mDimState.mLastRequestedDimContainer = null;
+ }
+ }
+
+ @Override
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
+ final DimState d = obtainDimState(container);
+ d.prepareLookChange(alpha, blurRadius);
+ }
+
+ @Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ if (mDimState != null) {
+ mDimState.prepareReparent(container, relativeLayer);
+ }
+ }
+
+ @Override
+ boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
- return;
+ return false;
}
- if (!mDimState.mDontReset) {
- mDimState.mDimming = false;
+ if (!mDimState.isDimming()) {
+ // No one is dimming, fade out and remove the dim
+ mDimState.exit(t);
+ mDimState = null;
+ return false;
+ } else {
+ // Someone is dimming, show the requested changes
+ mDimState.adjustSurfaceLayout(t);
+ final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
+ if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
+ && ws.mActivityRecord.mStartingData != null) {
+ // Skip enter animation while starting window is on top of its activity
+ mDimState.mSkipAnimation = true;
+ }
+ mDimState.setReady(t);
+ return true;
}
}
+ private DimState obtainDimState(WindowContainer container) {
+ if (mDimState == null) {
+ mDimState = new DimState();
+ }
+ mDimState.mLastRequestedDimContainer = container;
+ return mDimState;
+ }
+
+ @Override
+ @VisibleForTesting
+ SurfaceControl getDimLayer() {
+ return mDimState != null ? mDimState.mDimSurface : null;
+ }
+
@Override
Rect getDimBounds() {
return mDimState != null ? mDimState.mDimBounds : null;
@@ -287,127 +225,4 @@
mDimState.mAnimateExit = false;
}
}
-
- @Override
- protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
- final DimState d = obtainDimState(container);
- mDimState.setRequestedAppearance(alpha, blurRadius);
- d.mDimming = true;
- }
-
- @Override
- protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
- if (mDimState != null) {
- mDimState.setRequestedRelativeParent(container, relativeLayer);
- }
- }
-
- boolean updateDims(SurfaceControl.Transaction t) {
- if (mDimState == null) {
- return false;
- }
-
- if (!mDimState.mDimming) {
- // No one is dimming anymore, fade out dim and remove
- if (!mDimState.mAnimateExit) {
- if (mDimState.mDimLayer.isValid()) {
- t.remove(mDimState.mDimLayer);
- }
- } else {
- mDimState.setExitParameters(
- mDimState.mRequestedProperties.mDimmingContainer);
- mDimState.applyChanges(t);
- }
- mDimState = null;
- return false;
- }
- final Rect bounds = mDimState.mDimBounds;
- // TODO: Once we use geometry from hierarchy this falls away.
- t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
- t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
- // Skip enter animation while starting window is on top of its activity
- final WindowState ws = mLastRequestedDimContainer.asWindowState();
- if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
- && ws.mActivityRecord.mStartingData != null) {
- mDimState.mRequestedProperties.mSkipAnimation = true;
- }
- mDimState.applyChanges(t);
- return true;
- }
-
- private long getDimDuration(WindowContainer container) {
- // Use the same duration as the animation on the WindowContainer
- AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
- final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
- return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
- : animationAdapter.getDurationHint();
- }
-
- private static class AnimationExtremes<T> {
- final T mStartValue;
- final T mFinishValue;
-
- AnimationExtremes(T fromValue, T toValue) {
- mStartValue = fromValue;
- mFinishValue = toValue;
- }
- }
-
- private static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
- private final long mDuration;
- private final AnimationExtremes<Float> mAlpha;
- private final AnimationExtremes<Integer> mBlur;
-
- float mCurrentAlpha = 0;
- int mCurrentBlur = 0;
-
- AnimationSpec(AnimationExtremes<Float> alpha,
- AnimationExtremes<Integer> blur, long duration) {
- mAlpha = alpha;
- mBlur = blur;
- mDuration = duration;
- }
-
- @Override
- public long getDuration() {
- return mDuration;
- }
-
- @Override
- public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
- final float fraction = getFraction(currentPlayTime);
- mCurrentAlpha =
- fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
- mCurrentBlur =
- (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
- t.setAlpha(sc, mCurrentAlpha);
- t.setBackgroundBlurRadius(sc, mCurrentBlur);
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
- pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
- pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
- pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
- pw.print(" duration="); pw.println(mDuration);
- }
-
- @Override
- public void dumpDebugInner(ProtoOutputStream proto) {
- final long token = proto.start(ALPHA);
- proto.write(FROM, mAlpha.mStartValue);
- proto.write(TO, mAlpha.mFinishValue);
- proto.write(DURATION_MS, mDuration);
- proto.end(token);
- }
- }
-
- static class AnimationAdapterFactory {
-
- public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
- SurfaceAnimationRunner runner) {
- return new LocalAnimationAdapter(alphaAnimationSpec, runner);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7375512..5f08212 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -422,6 +422,11 @@
// TODO: remove this once the recents animation is moved to the Shell
SurfaceControl mLastRecentsAnimationOverlay;
+ // A surface that is used by TaskFragmentOrganizer to place content on top of own activities and
+ // trusted TaskFragments.
+ @Nullable
+ DecorSurfaceContainer mDecorSurfaceContainer;
+
static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
@@ -1540,6 +1545,11 @@
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
+ if (mDecorSurfaceContainer != null && r == mDecorSurfaceContainer.mOwnerTaskFragment) {
+ // Remove the decor surface if the owner TaskFragment is removed;
+ removeDecorSurface();
+ }
+
if (hasChild()) {
updateEffectiveIntent();
@@ -2638,6 +2648,9 @@
}
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
+ if (mDecorSurfaceContainer != null) {
+ mDecorSurfaceContainer.release();
+ }
super.removeImmediately();
mRemoving = false;
@@ -3644,7 +3657,8 @@
*/
TaskFragmentParentInfo getTaskFragmentParentInfo() {
return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
- shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity());
+ shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(),
+ getDecorSurface());
}
@Override
@@ -3666,6 +3680,62 @@
}
}
+ @Override
+ void assignChildLayers(@NonNull SurfaceControl.Transaction t) {
+ int layer = 0;
+ boolean decorSurfacePlaced = false;
+
+ // We use two passes as a way to promote children which
+ // need Z-boosting to the end of the list.
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ wc.assignChildLayers(t);
+ if (!wc.needsZBoost()) {
+ // Place the decor surface under any untrusted content.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+ && shouldPlaceDecorSurfaceBelowContainer(wc)) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+ wc.assignLayer(t, layer++);
+
+ // Place the decor surface just above the owner TaskFragment.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+ && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+ }
+ }
+
+ // If not placed yet, the decor surface should be on top of all non-boosted children.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ if (wc.needsZBoost()) {
+ wc.assignLayer(t, layer++);
+ }
+ }
+ if (mOverlayHost != null) {
+ mOverlayHost.setLayer(t, layer++);
+ }
+ }
+
+ boolean shouldPlaceDecorSurfaceBelowContainer(@NonNull WindowContainer wc) {
+ boolean isOwnActivity =
+ wc.asActivityRecord() != null
+ && wc.asActivityRecord().isUid(effectiveUid);
+ boolean isTrustedTaskFragment =
+ wc.asTaskFragment() != null
+ && wc.asTaskFragment().isEmbedded()
+ && wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode();
+ return !isOwnActivity && !isTrustedTaskFragment;
+ }
+
boolean isTaskId(int taskId) {
return mTaskId == taskId;
}
@@ -6177,6 +6247,7 @@
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
+ && display != null
&& display.isDefaultDisplay) {
return false;
}
@@ -6672,4 +6743,77 @@
mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
}
}
+
+ /**
+ * Associates the decor surface with the given TF, or create one if there
+ * isn't one in the Task yet. The surface will be removed with the TF,
+ * and become invisible if the TF is invisible. */
+ void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment) {
+ if (mDecorSurfaceContainer != null) {
+ mDecorSurfaceContainer.mOwnerTaskFragment = taskFragment;
+ } else {
+ mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment);
+ assignChildLayers();
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
+ }
+
+ void removeDecorSurface() {
+ if (mDecorSurfaceContainer == null) {
+ return;
+ }
+ mDecorSurfaceContainer.release();
+ mDecorSurfaceContainer = null;
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
+
+ @Nullable SurfaceControl getDecorSurface() {
+ return mDecorSurfaceContainer != null ? mDecorSurfaceContainer.mDecorSurface : null;
+ }
+
+ /**
+ * A decor surface that is requested by a {@code TaskFragmentOrganizer} which will be placed
+ * below children windows except for own Activities and TaskFragment in fully trusted mode.
+ */
+ @VisibleForTesting
+ class DecorSurfaceContainer {
+ @VisibleForTesting
+ @NonNull final SurfaceControl mContainerSurface;
+
+ @VisibleForTesting
+ @NonNull final SurfaceControl mDecorSurface;
+
+ // The TaskFragment that requested the decor surface. If it is destroyed, the decor surface
+ // is also released.
+ @VisibleForTesting
+ @NonNull TaskFragment mOwnerTaskFragment;
+
+ private DecorSurfaceContainer(@NonNull TaskFragment initialOwner) {
+ mOwnerTaskFragment = initialOwner;
+ mContainerSurface = makeSurface().setContainerLayer()
+ .setParent(mSurfaceControl)
+ .setName(mSurfaceControl + " - decor surface container")
+ .setEffectLayer()
+ .setHidden(false)
+ .setCallsite("Task.DecorSurfaceContainer")
+ .build();
+
+ mDecorSurface = makeSurface()
+ .setParent(mContainerSurface)
+ .setName(mSurfaceControl + " - decor surface")
+ .setHidden(false)
+ .setCallsite("Task.DecorSurfaceContainer")
+ .build();
+ }
+
+ private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) {
+ t.setLayer(mContainerSurface, layer);
+ t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible());
+ }
+
+ private void release() {
+ mDecorSurface.release();
+ mContainerSurface.release();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8bc461f..39b4480 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -316,7 +316,8 @@
/** Organizer that organizing this TaskFragment. */
@Nullable
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
- private int mTaskFragmentOrganizerUid = INVALID_UID;
+ @VisibleForTesting
+ int mTaskFragmentOrganizerUid = INVALID_UID;
private @Nullable String mTaskFragmentOrganizerProcessName;
/** Client assigned unique token for this TaskFragment if this is created by an organizer. */
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index e7a1cf1..707f9fc 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -762,6 +762,8 @@
.setTask(task)
.build());
}
+ // Make sure the parent info changed event will be dispatched if there are no other changes.
+ mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index caa57bb..e5604ec 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1737,8 +1737,8 @@
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
- mLogger.logOnSendAsync(mController.mLoggerHandler);
if (mLogger.mInfo != null) {
+ mLogger.logOnSendAsync(mController.mLoggerHandler);
mController.mTransitionTracer.logSentTransition(this, mTargets);
}
}
@@ -1756,6 +1756,27 @@
}
/**
+ * Checks if the transition contains order changes.
+ *
+ * This is a shallow check that doesn't account for collection in parallel, unlike
+ * {@code collectOrderChanges}
+ */
+ boolean hasOrderChanges() {
+ ArrayList<Task> onTopTasks = new ArrayList<>();
+ // Iterate over target displays to get up to date on top tasks.
+ // Cannot use `mOnTopTasksAtReady` as it's not populated before the `applyReady` is called.
+ for (DisplayContent dc : mTargetDisplays) {
+ addOnTopTasks(dc, onTopTasks);
+ }
+ for (Task task : onTopTasks) {
+ if (!mOnTopTasksStart.contains(task)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Collect tasks which moved-to-top as part of this transition. This also updates the
* controller's latest-reported when relevant.
*
@@ -3305,7 +3326,6 @@
*/
void addGroup(WindowContainer wc) {
if (mReadyGroups.containsKey(wc)) {
- Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
return;
}
mReadyGroups.put(wc, false);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bacfda5..e648d64 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -792,6 +792,12 @@
mCollectingTransition.recordTaskOrder(wc);
}
+ /** @see Transition#hasOrderChanges */
+ boolean hasOrderChanges() {
+ if (mCollectingTransition == null) return false;
+ return mCollectingTransition.hasOrderChanges();
+ }
+
/**
* Collects the window containers which need to be synced with the changing display area into
* the current collecting transition.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 208df6c7..2af6569 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -23,7 +23,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1468,6 +1470,16 @@
}
break;
}
+ case OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE: {
+ final Task task = taskFragment.getTask();
+ task.moveOrCreateDecorSurfaceFor(taskFragment);
+ break;
+ }
+ case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: {
+ final Task task = taskFragment.getTask();
+ task.removeDecorSurface();
+ break;
+ }
}
return effects;
}
@@ -1507,6 +1519,19 @@
return false;
}
+ // TODO (b/293654166) remove the decor surface checks once we clear security reviews
+ if ((opType == OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE
+ || opType == OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
+ && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ final Throwable exception = new SecurityException(
+ "Only a system organizer can perform OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE"
+ + " or OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE."
+ );
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ opType, exception);
+ return false;
+ }
+
final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
return secondaryFragmentToken == null
|| validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
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 cca4261..c625b1e 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -159,6 +159,12 @@
<xs:element type="usiVersion" name="usiVersion">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Maximum screen brightness setting when screen brightness capped in
+ Wear Bedtime mode. This must be a non-negative decimal within the range defined by
+ the first and the last brightness value in screenBrightnessMap. -->
+ <xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -586,42 +592,39 @@
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
- <!-- Sets the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
+ <!-- Sets the brightness mapping of the desired screen brightness to the corresponding
+ lux for the current display -->
+ <xs:element name="luxToBrightnessMapping" type="luxToBrightnessMapping"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
</xs:complexType>
- <!-- Represents the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:complexType name="displayBrightnessMapping">
- <xs:sequence>
- <!-- Sets the list of display brightness points, each representing the desired screen
- brightness in nits to the corresponding lux for the current display
+ <!-- Sets the list of display brightness points, each representing the desired screen brightness
+ in a certain lux environment.
- The N entries of this array define N + 1 control points as follows:
- (1-based arrays)
+ The first value of each point is the lux value and the second value is the brightness value.
- Point 1: (0, nits[1]): currentLux <= 0
- Point 2: (lux[1], nits[2]): 0 < currentLux <= lux[1]
- Point 3: (lux[2], nits[3]): lux[2] < currentLux <= lux[3]
- ...
- Point N+1: (lux[N], nits[N+1]): lux[N] < currentLux
+ The first lux value must be 0.
- The control points must be strictly increasing. Each control point
- corresponds to an entry in the brightness backlight values arrays.
- For example, if currentLux == lux[1] (first element of the levels array)
- then the brightness will be determined by nits[2] (second element
- of the brightness values array).
- -->
- <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
- minOccurs="1" maxOccurs="unbounded">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
+ The control points must be strictly increasing.
+
+ Example: if currentLux == the second lux value in the mapping then the brightness will be
+ determined by the second brightness value in the mapping. Spline interpolation is used
+ to determine the auto-brightness values for lux levels between these control points.
+
+ The brightness values must be non-negative decimals within the range between the first and
+ the last brightness values in screenBrightnessMap.
+
+ This is used in place of config_autoBrightnessLevels and config_autoBrightnessLcdBacklightValues
+ defined in the config XML resource.
+ -->
+ <xs:complexType name="luxToBrightnessMapping">
+ <xs:element name="map" type="nonNegativeFloatToFloatMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:complexType>
<!-- Represents a point in the display brightness mapping, representing the lux level from the
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index f767291..8c8c123 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,14 +7,14 @@
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
- method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public boolean getEnabled();
+ method public final com.android.server.display.config.LuxToBrightnessMapping getLuxToBrightnessMapping();
method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
- method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
method public void setEnabled(boolean);
+ method public final void setLuxToBrightnessMapping(com.android.server.display.config.LuxToBrightnessMapping);
}
public class BlockingZoneConfig {
@@ -78,11 +78,6 @@
method public java.util.List<com.android.server.display.config.Density> getDensity();
}
- public class DisplayBrightnessMapping {
- ctor public DisplayBrightnessMapping();
- method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
- }
-
public class DisplayBrightnessPoint {
ctor public DisplayBrightnessPoint();
method public final java.math.BigInteger getLux();
@@ -110,6 +105,7 @@
method public final com.android.server.display.config.SensorDetails getProxSensor();
method public com.android.server.display.config.DisplayQuirks getQuirks();
method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
+ method public final java.math.BigDecimal getScreenBrightnessCapForWearBedtimeMode();
method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxIdleMillis();
@@ -143,6 +139,7 @@
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
method public void setQuirks(com.android.server.display.config.DisplayQuirks);
method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
+ method public final void setScreenBrightnessCapForWearBedtimeMode(java.math.BigDecimal);
method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
method public final void setScreenBrightnessRampDecreaseMaxIdleMillis(java.math.BigInteger);
@@ -220,6 +217,12 @@
method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap();
}
+ public class LuxToBrightnessMapping {
+ ctor public LuxToBrightnessMapping();
+ method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
+ method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ }
+
public class NitsMap {
ctor public NitsMap();
method public String getInterpolation();
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 1988bb6..da44aac 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -23,12 +23,14 @@
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.credentials.ui.ProviderData;
import android.credentials.ui.UserSelectionDialogResult;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -94,6 +96,9 @@
private final Set<ComponentName> mEnabledProviders;
+ private final RequestSessionDeathRecipient mDeathRecipient =
+ new RequestSessionDeathRecipient();
+
protected PendingIntent mPendingIntent;
@NonNull
@@ -141,11 +146,26 @@
mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted,
mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType));
setCancellationListener();
+ if (Flags.clearSessionEnabled()) {
+ setUpClientCallbackListener();
+ }
+ }
+
+ private void setUpClientCallbackListener() {
+ if (mClientCallback != null && mClientCallback instanceof IInterface) {
+ IInterface callback = (IInterface) mClientCallback;
+ try {
+ callback.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+ }
}
private void setCancellationListener() {
mCancellationSignal.setOnCancelListener(
() -> {
+ Slog.d(TAG, "Cancellation invoked from the client - clearing session");
boolean isUiActive = maybeCancelUi();
finishSession(!isUiActive);
}
@@ -168,6 +188,17 @@
return false;
}
+ private boolean isUiWaitingForData() {
+ // Technically, the status can also be IN_PROGRESS when the user has made a selection
+ // so this an over estimation, but safe to do so as it is used for cancellation
+ // propagation to the provider in a very narrow time frame. If provider has
+ // already responded, cancellation is not an issue as the cancellation listener
+ // is independent of the service binding.
+ // TODO(b/313512500): Do not propagate cancelation if provider has responded in
+ // query phase.
+ return mCredentialManagerUi.getStatus() == CredentialManagerUi.UiStatus.IN_PROGRESS;
+ }
+
public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
RemoteCredentialService remoteCredentialService);
@@ -373,4 +404,12 @@
return chosenProviderSession != null && chosenProviderSession.mProviderInfo != null
&& chosenProviderSession.mProviderInfo.isPrimary();
}
+
+ private class RequestSessionDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Client binder died - clearing session");
+ finishSession(isUiWaitingForData());
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0a2e806..1919eb3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2112,6 +2112,15 @@
networkPolicy.bindConnectivityManager();
t.traceEnd();
+ t.traceBegin("StartSecurityStateManagerService");
+ try {
+ ServiceManager.addService(Context.SECURITY_STATE_SERVICE,
+ new SecurityStateManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting SecurityStateManagerService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartVpnManagerService");
try {
vpnManager = VpnManagerService.create(context);
diff --git a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
deleted file mode 100644
index 0ad4184..0000000
--- a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2023 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.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.when;
-
-import android.app.Application;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.MediaRoute2Info;
-import android.os.UserHandle;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowBluetoothAdapter;
-import org.robolectric.shadows.ShadowBluetoothDevice;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(RobolectricTestRunner.class)
-public class AudioPoliciesBluetoothRouteControllerTest {
-
- private static final String DEVICE_ADDRESS_UNKNOWN = ":unknown:ip:address:";
- private static final String DEVICE_ADDRESS_SAMPLE_1 = "30:59:8B:E4:C6:35";
- private static final String DEVICE_ADDRESS_SAMPLE_2 = "0D:0D:A6:FF:8D:B6";
- private static final String DEVICE_ADDRESS_SAMPLE_3 = "2D:9B:0C:C2:6F:78";
- private static final String DEVICE_ADDRESS_SAMPLE_4 = "66:88:F9:2D:A8:1E";
-
- private Context mContext;
-
- private ShadowBluetoothAdapter mShadowBluetoothAdapter;
-
- @Mock
- private BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
-
- @Mock
- private BluetoothProfileMonitor mBluetoothProfileMonitor;
-
- private AudioPoliciesBluetoothRouteController mAudioPoliciesBluetoothRouteController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- Application application = ApplicationProvider.getApplicationContext();
- mContext = application;
-
- BluetoothManager bluetoothManager = (BluetoothManager)
- mContext.getSystemService(Context.BLUETOOTH_SERVICE);
-
- BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
- mShadowBluetoothAdapter = Shadows.shadowOf(bluetoothAdapter);
-
- mAudioPoliciesBluetoothRouteController =
- new AudioPoliciesBluetoothRouteController(mContext, bluetoothAdapter,
- mBluetoothProfileMonitor, mListener) {
- @Override
- boolean isDeviceConnected(BluetoothDevice device) {
- return true;
- }
- };
-
- // Enable A2DP profile.
- when(mBluetoothProfileMonitor.isProfileSupported(eq(BluetoothProfile.A2DP), any()))
- .thenReturn(true);
- mShadowBluetoothAdapter.setProfileConnectionState(BluetoothProfile.A2DP,
- BluetoothProfile.STATE_CONNECTED);
-
- mAudioPoliciesBluetoothRouteController.start(UserHandle.of(0));
- }
-
- @Test
- public void getSelectedRoute_noBluetoothRoutesAvailable_returnsNull() {
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
- }
-
- @Test
- public void selectRoute_noBluetoothRoutesAvailable_returnsFalse() {
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_UNKNOWN)).isFalse();
- }
-
- @Test
- public void selectRoute_noDeviceWithGivenAddress_returnsFalse() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_3);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_2)).isFalse();
- }
-
- @Test
- public void selectRoute_deviceIsInDevicesSet_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_1)).isTrue();
- }
-
- @Test
- public void selectRoute_resetSelectedDevice_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_1);
- assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void selectRoute_noSelectedDevice_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void getSelectedRoute_updateRouteFailed_returnsNull() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
- mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_3);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
- }
-
- @Test
- public void getSelectedRoute_updateRouteSuccessful_returnsUpdateDevice() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_4)).isTrue();
-
- MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
- assertThat(selectedRoute.getAddress()).isEqualTo(DEVICE_ADDRESS_SAMPLE_4);
- }
-
- @Test
- public void getSelectedRoute_resetSelectedRoute_returnsNull() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Device is not null now.
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
- // Rest the device.
- mAudioPoliciesBluetoothRouteController.selectRoute(null);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute())
- .isNull();
- }
-
- @Test
- public void getTransferableRoutes_noSelectedRoute_returnsAllBluetoothDevices() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
-
- Set<String> transferableDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
- assertThat(transferableDevices).containsExactlyElementsIn(addresses);
- }
-
- @Test
- public void getTransferableRoutes_hasSelectedRoute_returnsRoutesWithoutSelectedDevice() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- Set<String> transferableDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
- assertThat(transferableDevices).containsExactly(DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2);
- }
-
- @Test
- public void getAllBluetoothRoutes_hasSelectedRoute_returnsAllRoutes() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- Set<String> bluetoothDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getAllBluetoothRoutes());
- assertThat(bluetoothDevices).containsExactlyElementsIn(addresses);
- }
-
- @Test
- public void updateVolumeForDevice_setVolumeForA2DPTo25_selectedRouteVolumeIsUpdated() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- mAudioPoliciesBluetoothRouteController.updateVolumeForDevices(
- AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, 25);
-
- MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
- assertThat(selectedRoute.getVolume()).isEqualTo(25);
- }
-
- private void sendBluetoothDevicesChangedBroadcast() {
- Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
- mContext.sendBroadcast(intent);
- }
-
- private static Set<String> extractAddressesListFrom(Collection<MediaRoute2Info> routes) {
- Set<String> addresses = new HashSet<>();
-
- for (MediaRoute2Info route: routes) {
- addresses.add(route.getAddress());
- }
-
- return addresses;
- }
-
- private static Set<BluetoothDevice> generateFakeBluetoothDevicesSet(String... addresses) {
- Set<BluetoothDevice> devices = new HashSet<>();
-
- for (String address: addresses) {
- devices.add(ShadowBluetoothDevice.newInstance(address));
- }
-
- return devices;
- }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 97e5826..a2e80f0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -104,7 +104,7 @@
468.5f,
};
- private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
+ private static final int[] DISPLAY_LEVELS_INT = {
9,
30,
45,
@@ -118,6 +118,20 @@
255
};
+ private static final float[] DISPLAY_LEVELS = {
+ 0.03f,
+ 0.11f,
+ 0.17f,
+ 0.24f,
+ 0.3f,
+ 0.37f,
+ 0.46f,
+ 0.57f,
+ 0.7f,
+ 0.87f,
+ 1
+ };
+
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
@@ -155,23 +169,23 @@
DisplayWhiteBalanceController mMockDwbc;
@Test
- public void testSimpleStrategyMappingAtControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingAtControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]);
+ PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_INT[i]);
assertEquals(expectedLevel,
simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
}
}
@Test
- public void testSimpleStrategyMappingBetweenControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -179,14 +193,42 @@
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
assertTrue("Desired brightness should be between adjacent control points.",
- backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
- && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+ backlight > DISPLAY_LEVELS_INT[i - 1]
+ && backlight < DISPLAY_LEVELS_INT[i]);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingAtControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]),
+ /* tolerance= */ 0.0001f);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingBetweenControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 1; i < LUX_LEVELS.length; i++) {
+ final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+ final float brightness = simple.getBrightness(lux);
+ assertTrue("Desired brightness should be between adjacent control points.",
+ brightness > DISPLAY_LEVELS[i - 1] && brightness < DISPLAY_LEVELS[i]);
}
}
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -201,14 +243,14 @@
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
- final int n = DISPLAY_LEVELS_BACKLIGHT.length;
+ final int n = DISPLAY_LEVELS_INT.length;
final float expectedBrightness =
- (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
+ (float) DISPLAY_LEVELS_INT[n - 1] / PowerManager.BRIGHTNESS_ON;
assertEquals(expectedBrightness,
strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@@ -322,7 +364,7 @@
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -363,13 +405,13 @@
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra backlight level
final int[] backlight = Arrays.copyOf(
- DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1);
+ DISPLAY_LEVELS_INT, DISPLAY_LEVELS_INT.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
res = createResources(backlight);
ddc = createDdc(DISPLAY_RANGE_NITS,
@@ -410,7 +452,7 @@
LUX_LEVELS, DISPLAY_LEVELS_NITS);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
}
@@ -546,16 +588,24 @@
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(EMPTY_FLOAT_ARRAY);
return mockDdc;
}
private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
float[] luxLevelsFloat, float[] brightnessLevelsNits) {
+ return createDdc(nitsArray, backlightArray, luxLevelsFloat, brightnessLevelsNits,
+ EMPTY_FLOAT_ARRAY);
+ }
+
+ private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
+ float[] luxLevelsFloat, float[] brightnessLevelsNits, float[] brightnessLevels) {
DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
when(mockDdc.getNits()).thenReturn(nitsArray);
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(brightnessLevels);
return mockDdc;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 0bcbeb9..31d7e88 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -47,8 +47,10 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.Before;
import org.junit.Test;
@@ -92,10 +94,14 @@
@Mock
private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -110,10 +116,6 @@
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{45.32f, 75.43f}, ZERO_DELTA);
// Test thresholds
assertEquals(10, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(),
@@ -606,7 +608,7 @@
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
// Test thresholds
assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -671,6 +673,9 @@
assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+
+ assertEquals(BrightnessSynchronizer.brightnessIntToFloat(35),
+ mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
}
@Test
@@ -716,6 +721,38 @@
assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.04f, ZERO_DELTA);
}
+ @Test
+ public void testBrightnessCapForWearBedtimeMode() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+ assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertArrayEquals(new float[]{0.0f, 80},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.2f, 0.3f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(), SMALL_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels_FeatureFlagOff() throws IOException {
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
+ setupDisplayDeviceConfigFromConfigResourceFile();
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertNull(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels());
+ assertArrayEquals(new float[]{0, 110, 500},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{2, 200, 600},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1037,8 +1074,8 @@
+ "<screenBrightnessRampDecreaseMaxIdleMillis>"
+ "5000"
+ "</screenBrightnessRampDecreaseMaxIdleMillis>\n";
-
}
+
private String getContent() {
return getContent(getValidLuxThrottling(), getValidProxSensor(),
/* includeIdleMode= */ true);
@@ -1089,16 +1126,18 @@
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ (includeIdleMode ? getRampSpeedsIdle() : "")
- + "<displayBrightnessMapping>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>50</lux>\n"
- + "<nits>45.32</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>80</lux>\n"
- + "<nits>75.43</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "</displayBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.2</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>80</first>\n"
+ + "<second>0.3</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ getPowerThrottlingConfig()
+ "<highBrightnessMode enabled=\"true\">\n"
@@ -1355,6 +1394,9 @@
+ "<majorVersion>2</majorVersion>\n"
+ "<minorVersion>0</minorVersion>\n"
+ "</usiVersion>\n"
+ + "<screenBrightnessCapForWearBedtimeMode>"
+ + "0.1"
+ + "</screenBrightnessCapForWearBedtimeMode>"
+ "</displayConfiguration>\n";
}
@@ -1372,7 +1414,7 @@
private void setupDisplayDeviceConfigFromDisplayConfigFile(String content) throws IOException {
Path tempFile = Files.createTempFile("display_config", ".tmp");
Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
- mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext, mFlags);
mDisplayDeviceConfig.initFromFile(tempFile.toFile());
}
@@ -1381,25 +1423,15 @@
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits))
.thenReturn(screenBrightnessNits);
- TypedArray screenBrightnessBacklight = createFloatTypedArray(new
- float[]{0.0f, 120.0f, 255.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessBacklight))
- .thenReturn(screenBrightnessBacklight);
when(mResources.getIntArray(com.android.internal.R.array
.config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
-
TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
float[]{2.0f, 200.0f, 600.0f});
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
.thenReturn(screenBrightnessLevelNits);
- int[] screenBrightnessLevelLux = new int[]{0, 110, 500};
+ int[] screenBrightnessLevelLux = new int[]{110, 500};
when(mResources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels))
.thenReturn(screenBrightnessLevelLux);
@@ -1457,7 +1489,12 @@
R.integer.config_autoBrightnessDarkeningLightDebounce))
.thenReturn(4000);
- mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
+ when(mResources.getInteger(
+ R.integer.config_screenBrightnessCapForWearBedtimeMode))
+ .thenReturn(35);
+
+ mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, /* useConfigXml= */ true,
+ mFlags);
}
private TypedArray createFloatTypedArray(float[] vals) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
index 4fd8f26..dc6abf1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -57,6 +57,9 @@
@Mock
private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private DisplayAdapter mMockDisplayAdapter;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -67,34 +70,39 @@
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@@ -102,8 +110,9 @@
private static class FakeDisplayDevice extends DisplayDevice {
private final DisplayDeviceInfo mDisplayDeviceInfo;
- FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
- super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+ FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter) {
+ super(displayAdapter, /* displayToken= */ null, /* uniqueId= */ "",
+ InstrumentationRegistry.getInstrumentation().getContext());
mDisplayDeviceInfo = displayDeviceInfo;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 55f56e9..02e3ef4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -212,7 +212,8 @@
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return mMockVirtualDisplayAdapter;
}
@@ -251,7 +252,8 @@
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
@Override
@@ -263,7 +265,7 @@
@Override
public void destroyDisplay(IBinder displayToken) {
}
- });
+ }, flags);
}
@Override
@@ -329,6 +331,7 @@
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Mock DisplayManagerFlags mMockFlags;
@@ -788,7 +791,8 @@
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return null; // return null for the adapter. This should cause a failure.
}
@@ -3194,7 +3198,7 @@
private DisplayDeviceInfo mDisplayDeviceInfo;
FakeDisplayDevice() {
- super(null, null, "", mContext);
+ super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext);
}
public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 8270845..f36854b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1455,8 +1455,8 @@
// mMockContext and values will be loaded from mMockResources.
@Override
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay, flags);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 43d2b8f..28ec896 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -119,8 +119,8 @@
@Mock IThermalService mIThermalServiceMock;
@Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
- @Mock
- DisplayManagerFlags mFlagsMock;
+ @Mock DisplayManagerFlags mFlagsMock;
+ @Mock DisplayAdapter mDisplayAdapterMock;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@@ -1075,7 +1075,8 @@
private int mState;
TestDisplayDevice() {
- super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
+ super(mDisplayAdapterMock, /* displayToken= */ null,
+ "test_display_" + sUniqueTestDisplayId++, mContextMock);
mInfo = new DisplayDeviceInfo();
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 9f91916..5676a38 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -32,9 +32,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -51,8 +54,12 @@
private TestInjector mInjector;
private TestLooper mTestLooper;
+ @Mock
+ private DisplayAdapter mDisplayAdapter;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mInjector = new TestInjector();
mTestLooper = new TestLooper();
Handler handler = new Handler(mTestLooper.getLooper());
@@ -62,8 +69,8 @@
@Test
public void testLoadBrightness() {
final String uniqueDisplayId = "test:123";
- final DisplayDevice testDisplayDevice = new DisplayDevice(
- null, null, uniqueDisplayId, null) {
+ final DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -100,7 +107,8 @@
public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
final String uniqueDisplayId = "test:123";
final DisplayDevice testDisplayDevice =
- new DisplayDevice(null, null, uniqueDisplayId, null) {
+ new DisplayDevice(mDisplayAdapter, /* displayToken= */ null, uniqueDisplayId,
+ /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -273,7 +281,8 @@
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -319,7 +328,8 @@
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return false;
@@ -386,7 +396,8 @@
@Test
public void testStoreAndRestoreResolution() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -422,7 +433,8 @@
@Test
public void testStoreAndRestoreRefreshRate() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -455,7 +467,8 @@
@Test
public void testBrightnessInitialisesWithInvalidFloat() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
index b7cbac5..5c50acb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -72,27 +72,14 @@
@Test
public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
- }
-
- @Test
- public void testFindHighestRefreshRate() {
- int displayId = 13;
- when(mDisplayManagerMock.getDisplay(displayId)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, displayId),
- /* delta= */ 0);
- }
-
- @Test
- public void testFindHighestRefreshRate_DisplayIsNull() {
when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
assertEquals(DEFAULT_REFRESH_RATE,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ assertEquals(120,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 73e7ba0..c01b15c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -28,6 +28,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
@@ -59,13 +60,17 @@
private VirtualDisplayAdapter mVirtualDisplayAdapter;
+ @Mock
+ private DisplayManagerFlags mFeatureFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mHandler = new TestHandler(null);
mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
- mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory);
+ mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory,
+ mFeatureFlags);
when(mMockCallback.asBinder()).thenReturn(mMockBinder);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index ff2b1f4..9aa6136 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -262,7 +262,8 @@
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ Context context) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
new file mode 100644
index 0000000..3458b08
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 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.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF;
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class BrightnessWearBedtimeModeClamperTest {
+
+ private static final float BRIGHTNESS_CAP = 0.3f;
+
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private final TestInjector mInjector = new TestInjector();
+ private BrightnessWearBedtimeModeClamper mClamper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext,
+ mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
+ mTestHandler.flush();
+ }
+
+ @Test
+ public void testBrightnessCap() {
+ assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ }
+
+ @Test
+ public void testBedtimeModeOn() {
+ setBedtimeModeEnabled(true);
+ assertTrue(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testBedtimeModeOff() {
+ setBedtimeModeEnabled(false);
+ assertFalse(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testType() {
+ assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+ }
+
+ @Test
+ public void testOnDisplayChanged() {
+ float newBrightnessCap = 0.61f;
+
+ mClamper.onDisplayChanged(() -> newBrightnessCap);
+ mTestHandler.flush();
+
+ assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ private void setBedtimeModeEnabled(boolean enabled) {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
+ enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
+ mInjector.notifyBedtimeModeChanged();
+ mTestHandler.flush();
+ }
+
+ private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector {
+
+ private ContentObserver mObserver;
+
+ @Override
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mObserver = observer;
+ }
+
+ private void notifyBedtimeModeChanged() {
+ if (mObserver != null) {
+ mObserver.dispatchChange(/* selfChange= */ false,
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
+ }
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index d085923..60a0c03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -290,7 +290,6 @@
};
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
- private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
@@ -1551,12 +1550,9 @@
public void testPeakRefreshRate_FlagEnabled() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
- float highestRefreshRate1 = 130;
- float highestRefreshRate2 = 132;
- doReturn(highestRefreshRate1).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- doReturn(highestRefreshRate2).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1567,14 +1563,10 @@
setPeakRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate1);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate2);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ highestRefreshRate);
}
@Test
@@ -1592,54 +1584,19 @@
setPeakRefreshRate(peakRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ peakRefreshRate);
- }
-
- @Test
- public void testPeakRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setPeakRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ peakRefreshRate);
}
@Test
public void testMinRefreshRate_FlagEnabled() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
- float highestRefreshRate1 = 130;
- float highestRefreshRate2 = 132;
- doReturn(highestRefreshRate1).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- doReturn(highestRefreshRate2).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1650,12 +1607,9 @@
setMinRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ highestRefreshRate1,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ highestRefreshRate2,
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@@ -1674,44 +1628,13 @@
setMinRefreshRate(minRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@Test
- public void testMinRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setMinRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- }
-
- @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3406,7 +3329,7 @@
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private final DisplayInfo mDisplayInfo;
- private final Map<Integer, Display> mDisplays;
+ private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private final DisplayManagerInternal mDisplayManagerInternal;
private final StatusBarManagerInternal mStatusBarManagerInternal;
@@ -3427,8 +3350,7 @@
mDisplayInfo.defaultModeId = MODE_ID;
mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
800, 600, /* refreshRate= */ 60)};
- mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID),
- DISPLAY_ID_2, createDisplay(DISPLAY_ID_2));
+ mDisplay = createDisplay(DISPLAY_ID);
mDisplayManagerInternal = displayManagerInternal;
mStatusBarManagerInternal = statusBarManagerInternal;
mSensorManagerInternal = sensorManagerInternal;
@@ -3459,12 +3381,12 @@
@Override
public Display getDisplay(int displayId) {
- return mDisplays.get(displayId);
+ return mDisplay;
}
@Override
public Display[] getDisplays() {
- return mDisplays.values().toArray(new Display[0]);
+ return new Display[] { mDisplay };
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index be29163..1a3a6a3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,6 +46,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -61,6 +62,7 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
@@ -128,6 +130,8 @@
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
+ private ContentResolver mContentResolver;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -332,7 +336,7 @@
anyString(), any(Executor.class),
any(DeviceConfig.OnPropertiesChangedListener.class)));
doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock
- -> mock(DeviceConfig.Properties.class))
+ -> new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_DEVICE_IDLE).build())
.when(() -> DeviceConfig.getProperties(
anyString(), ArgumentMatchers.<String>any()));
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
@@ -347,6 +351,7 @@
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
mInjector = new InjectorForTest(getContext());
+ doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
doReturn(mWearModeManagerInternal)
.when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -366,7 +371,8 @@
mDeviceIdleController.setLightEnabledForTest(true);
// Get the same Constants object that mDeviceIdleController got.
- mConstants = mInjector.getConstants(mDeviceIdleController);
+ mConstants = mInjector.getConstants(mDeviceIdleController,
+ mInjector.getHandler(mDeviceIdleController), mContentResolver);
final ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
ArgumentCaptor.forClass(TelephonyCallback.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
new file mode 100644
index 0000000..c5e6824
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 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.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.flags.Flags;
+import android.location.provider.ProviderRequest;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+import com.android.server.timedetector.TimeDetectorInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+@Presubmit
+@androidx.test.filters.SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssLocationProviderTest {
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.WARN)
+ .mockStatic(Settings.Global.class)
+ .build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private @Mock Context mContext;
+ private @Mock LocationManagerInternal mLocationManagerInternal;
+ private @Mock LocationManager mLocationManager;
+ private @Mock TimeDetectorInternal mTimeDetectorInternal;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private @Mock GnssMetrics mGnssMetrics;
+ private @Mock PowerManager mPowerManager;
+ private @Mock TelephonyManager mTelephonyManager;
+ private @Mock AppOpsManager mAppOpsManager;
+ private @Mock AlarmManager mAlarmManager;
+ private @Mock PowerManager.WakeLock mWakeLock;
+ private @Mock ContentResolver mContentResolver;
+ private @Mock UserManager mUserManager;
+ private @Mock UserHandle mUserHandle;
+ private Set<UserHandle> mUserHandleSet = new HashSet<>();
+
+ private GnssNative mGnssNative;
+
+ private GnssLocationProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn("mypackage").when(mContext).getPackageName();
+ doReturn("attribution").when(mContext).getAttributionTag();
+ doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+ doReturn(mPowerManager).when(mContext).getSystemService("power");
+ doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+ doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
+ doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
+ doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+ mUserHandleSet.add(mUserHandle);
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
+ doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
+ doReturn(mContentResolver).when(mContext).getContentResolver();
+ doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+ LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+ LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
+ FakeGnssHal fakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(fakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(
+ GnssNative.create(injector, mMockConfiguration)));
+ doReturn(true).when(mGnssNative).init();
+ GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
+ true).build();
+ doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
+
+ mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
+ mGnssNative.register();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(LocationManagerInternal.class);
+ LocalServices.removeServiceForTest(TimeDetectorInternal.class);
+ }
+
+ @Test
+ public void testStartNavigating() {
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_sameRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set the same request
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative, never()).stop();
+ verify(mGnssNative, times(1)).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_differentRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set a different request
+ providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).stop();
+ verify(mGnssNative, times(2)).start();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index 931b38d..f8fe97e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -22,12 +22,14 @@
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.pm.UserInfo
import android.os.Build
+import android.os.UserHandle
import android.os.UserHandle.USER_SYSTEM
import android.util.Log
import com.android.server.testutils.any
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -177,4 +179,13 @@
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_INTERNAL_ERROR)
}
+
+ @Test
+ fun deletePackageLIFWithNonExistantPackage_isFalse() {
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
+ val result = dph.deletePackageLIF("a.nonexistent.package", UserHandle.of(USER_SYSTEM), true,
+ intArrayOf(0), 0, PackageRemovedInfo(), true)
+ assertFalse(result)
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 7148b16..2e0ba00 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -82,6 +82,15 @@
.isEqualTo(20 * MINUTE_IN_MS);
assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
.isEqualTo(40 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(20 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(20 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(20 * MINUTE_IN_MS);
assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
.isWithin(PRECISION).of(2.0);
assertThat(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 07c486c..079ea2c7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -193,14 +193,14 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
- 5321, 7432, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
+ 5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
POWER_MODEL_UNDEFINED,
956, 1167, 1478,
true, 3554, 3776, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
444, 1110);
} else if (uidBatteryConsumer.getUid() == APP_UID2) {
assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
- 1111, 2222, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
+ 1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
BatteryConsumer.POWER_MODEL_POWER_PROFILE,
555, 666, 777,
true, 1777, 1888, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
@@ -269,7 +269,7 @@
.setStatsEndTimestamp(3000);
addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
- 1000, 2000,
+ 1000, 1500, 500,
300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800,
1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -312,13 +312,13 @@
.setStatsEndTimestamp(5000);
addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
- 4321, 5432,
+ 4321, 5400, 32,
123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION,
456, 567, 678,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
- 1111, 2222,
+ 1111, 2220, 2,
333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
@@ -338,7 +338,8 @@
private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
- int timeInStateForeground, int timeInStateBackground, double screenPower,
+ int timeInProcessStateForeground, int timeInProcessStateBackground,
+ int timeInProcessStateForegroundService, double screenPower,
int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower,
int cpuDuration, int customComponentDuration, double cpuPowerForeground,
int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground,
@@ -348,8 +349,10 @@
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
uidBuilder
.setPackageWithHighestDrain(packageWithHighestDrain)
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
+ .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, timeInProcessStateForeground)
+ .setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, timeInProcessStateBackground)
+ .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
+ timeInProcessStateForegroundService)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
.setConsumedPower(
@@ -446,7 +449,7 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
- 1000, 2000, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
+ 1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
BatteryConsumer.POWER_MODEL_POWER_PROFILE,
500, 600, 800,
true, 1777, 1888, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -496,8 +499,9 @@
}
private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
- double consumedPower, String packageWithHighestDrain, int timeInStateForeground,
- int timeInStateBackground, int screenPower, int screenPowerModel, double cpuPower,
+ double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground,
+ int timeInProcessStateBackground, int timeInProcessStateForegroundService,
+ int screenPower, int screenPowerModel, double cpuPower,
int cpuPowerModel, double customComponentPower, int cpuDuration,
int customComponentDuration, boolean processStateDataIncluded,
double totalPowerForeground, double totalPowerBackground, double totalPowerFgs,
@@ -509,9 +513,16 @@
assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
packageWithHighestDrain);
assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+ UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+ UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(
+ timeInProcessStateBackground + timeInProcessStateForegroundService);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_BACKGROUND)).isEqualTo(timeInProcessStateBackground);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
assertThat(uidBatteryConsumer.getPowerModel(
diff --git a/services/tests/servicestests/src/com/android/server/SecurityStateTest.java b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
new file mode 100644
index 0000000..fc91e47
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 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;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import static com.android.server.SecurityStateManagerService.KERNEL_RELEASE_PATTERN;
+import static com.android.server.SecurityStateManagerService.VENDOR_SECURITY_PATCH_PROPERTY_KEY;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.regex.Matcher;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SecurityStateTest {
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Context mMockContext;
+
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Mock
+ private Resources mMockResources;
+
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER_VERSION = "2023-12-01";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE = "com.google.android.gms";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE_VERSION = "2023-12-05";
+ private static final String[] SECURITY_STATE_PACKAGES =
+ new String[]{DEFAULT_SECURITY_STATE_PACKAGE};
+
+ @Before
+ public void setUp() throws Exception {
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockContext.getString(R.string.config_defaultModuleMetadataProvider))
+ .thenReturn(DEFAULT_MODULE_METADATA_PROVIDER);
+ when(mMockPackageManager.getPackageInfo(anyString(), anyInt()))
+ .thenReturn(new PackageInfo());
+ PackageInfo moduleMetadataPackageInfo = new PackageInfo();
+ moduleMetadataPackageInfo.versionName = DEFAULT_MODULE_METADATA_PROVIDER_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_MODULE_METADATA_PROVIDER, 0))
+ .thenReturn(moduleMetadataPackageInfo);
+ PackageInfo securityStatePackageInfo = new PackageInfo();
+ securityStatePackageInfo.versionName = DEFAULT_SECURITY_STATE_PACKAGE_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_SECURITY_STATE_PACKAGE, 0))
+ .thenReturn(securityStatePackageInfo);
+ when(mMockResources.getStringArray(R.array.config_securityStatePackages))
+ .thenReturn(SECURITY_STATE_PACKAGES);
+ }
+
+ @Test
+ public void testGetGlobalSecurityState_returnsBundle() {
+ SecurityStateManagerService securityState = new SecurityStateManagerService(mMockContext);
+
+ Bundle bundle = securityState.getGlobalSecurityState();
+
+ assertEquals(bundle.getString(KEY_SYSTEM_SPL), Build.VERSION.SECURITY_PATCH);
+ assertEquals(bundle.getString(KEY_VENDOR_SPL),
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ String kernelVersion = "";
+ if (matcher.matches()) {
+ kernelVersion = matcher.group(1);
+ }
+ assertEquals(bundle.getString(KEY_KERNEL_VERSION), kernelVersion);
+ assertEquals(bundle.getString(DEFAULT_MODULE_METADATA_PROVIDER),
+ DEFAULT_MODULE_METADATA_PROVIDER_VERSION);
+ assertEquals(bundle.getString(DEFAULT_SECURITY_STATE_PACKAGE),
+ DEFAULT_SECURITY_STATE_PACKAGE_VERSION);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogTest.java b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
new file mode 100644
index 0000000..34ac47e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2009 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Handler;
+import android.os.SimpleClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.Watchdog.HandlerChecker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.ZoneOffset;
+
+/** Test class for {@link Watchdog}. */
+@RunWith(AndroidJUnit4.class)
+public class WatchdogTest {
+ private static final int TIMEOUT_MS = 10;
+
+ private TestClock mClock;
+ private Handler mHandler;
+ private HandlerChecker mChecker;
+
+ @Before
+ public void setUp() {
+ mClock = new TestClock();
+ mHandler = mock(Handler.class);
+ mChecker =
+ new HandlerChecker(mHandler, "monitor thread", new Object(), mClock) {
+ @Override
+ public boolean isHandlerPolling() {
+ return false;
+ }
+ };
+ }
+
+ @Test
+ public void checkerPausedUntilResume() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.pauseLocked("pausing");
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ mChecker.resumeLocked("resuming");
+ mChecker.scheduleCheckLocked(10);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkerPausedUntilDeadline() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.pauseForLocked(10, "pausing");
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ mClock.advanceBy(5);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout. Watchdog should not be paused anymore.
+ mClock.advanceBy(6);
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkerPausedDuringScheduledRun() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ mClock.advanceBy(5);
+ mChecker.pauseForLocked(10, "pausing");
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout. Watchdog should not be paused anymore.
+ mClock.advanceBy(11);
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void blockedThread() {
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(mChecker.getCompletionStateLocked(), Watchdog.WAITING);
+
+ mClock.advanceBy(6);
+ assertEquals(Watchdog.WAITED_UNTIL_PRE_WATCHDOG, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout.
+ mClock.advanceBy(6);
+ assertEquals(Watchdog.OVERDUE, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkNothingBlocked() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ // scheduleCheckLocked calls #postAtFrontOfQueue which will call mChecker.run().
+ mChecker.run();
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+ verify(monitor).monitor();
+ }
+
+ private static class TestClock extends SimpleClock {
+ long mNowMillis = 1;
+
+ TestClock() {
+ super(ZoneOffset.UTC);
+ }
+
+ @Override
+ public long millis() {
+ return mNowMillis;
+ }
+
+ public void advanceBy(long millis) {
+ mNowMillis += millis;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 82efdd3..800350a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -85,9 +85,9 @@
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -156,7 +156,7 @@
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
- @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationConnectionManager mMockMagnificationConnectionManager;
@Mock private MagnificationController mMockMagnificationController;
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
@@ -180,8 +180,8 @@
UserManagerInternal.class, mMockUserManagerInternal);
mInputFilter = Mockito.mock(FakeInputFilter.class);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationMgr);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
when(mMockMagnificationController.supportWindowMagnification()).thenReturn(true);
@@ -530,7 +530,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -547,7 +547,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -565,7 +565,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -583,7 +583,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -602,7 +602,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -616,7 +616,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -630,7 +630,8 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -644,7 +645,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@SmallTest
@@ -659,7 +660,8 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -674,7 +676,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index a02807f..7829fcc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -39,9 +39,9 @@
import android.graphics.Region;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import org.junit.Before;
import org.junit.Test;
@@ -66,21 +66,21 @@
@Mock
private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock
- private WindowMagnificationManager mMockWindowMagnificationManager;
+ private MagnificationConnectionManager mMockMagnificationConnectionManager;
FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
- WindowMagnificationManagerStub mWindowMagnificationManagerStub;
+ MagnificationManagerStub mMagnificationManagerStub;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
mMockFullScreenMagnificationController);
- mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
- mMockWindowMagnificationManager);
+ mMagnificationManagerStub = new MagnificationManagerStub(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationManager);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
}
@@ -194,7 +194,7 @@
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ }).when(mMockMagnificationConnectionManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
any());
final Region result = new Region();
@@ -286,7 +286,7 @@
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
- verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false,
+ verify(mMockMagnificationConnectionManager).disableWindowMagnification(TEST_DISPLAY, false,
null);
}
@@ -296,7 +296,7 @@
mMagnificationProcessor.resetAllIfNeeded(connectionId);
verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId));
- verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId));
+ verify(mMockMagnificationConnectionManager).resetAllIfNeeded(eq(connectionId));
}
@Test
@@ -450,7 +450,7 @@
.setActivated(false).build();
mMagnificationProcessor.setMagnificationConfig(TEST_DISPLAY, config, false, SERVICE_ID);
- verify(mMockWindowMagnificationManager)
+ verify(mMockMagnificationConnectionManager)
.disableWindowMagnification(eq(TEST_DISPLAY), anyBoolean());
}
@@ -481,11 +481,11 @@
mFullScreenMagnificationControllerStub.resetAndStubMethods();
mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
config.getCenterX(), config.getCenterY(), false, SERVICE_ID);
- mWindowMagnificationManagerStub.deactivateIfNeed();
+ mMagnificationManagerStub.deactivateIfNeed();
} else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
- mWindowMagnificationManagerStub.resetAndStubMethods();
- mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
- config.getCenterX(), config.getCenterY());
+ mMagnificationManagerStub.resetAndStubMethods();
+ mMockMagnificationConnectionManager.enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY());
mFullScreenMagnificationControllerStub.deactivateIfNeed();
}
}
@@ -568,26 +568,26 @@
}
}
- private static class WindowMagnificationManagerStub {
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private static class MagnificationManagerStub {
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
private float mScale = 1.0f;
private float mCenterX = 0;
private float mCenterY = 0;
private boolean mIsEnabled = false;
- WindowMagnificationManagerStub(
- WindowMagnificationManager windowMagnificationManager) {
- mWindowMagnificationManager = windowMagnificationManager;
+ MagnificationManagerStub(
+ MagnificationConnectionManager magnificationConnectionManager) {
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
private void stubMethods() {
- doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+ doAnswer(invocation -> mScale).when(mMagnificationConnectionManager).getScale(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+ doAnswer(invocation -> mCenterX).when(mMagnificationConnectionManager).getCenterX(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+ doAnswer(invocation -> mCenterY).when(mMagnificationConnectionManager).getCenterY(
TEST_DISPLAY);
- doAnswer(invocation -> mIsEnabled).when(mWindowMagnificationManager)
+ doAnswer(invocation -> mIsEnabled).when(mMagnificationConnectionManager)
.isWindowMagnifierEnabled(TEST_DISPLAY);
Answer enableWindowMagnificationStubAnswer = invocation -> {
@@ -598,10 +598,10 @@
return true;
};
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat());
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat(), any(), anyInt());
Answer disableWindowMagnificationStubAnswer = invocation -> {
@@ -609,15 +609,15 @@
return true;
};
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean());
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean(), any());
}
public void resetAndStubMethods() {
- Mockito.reset(mWindowMagnificationManager);
+ Mockito.reset(mMagnificationConnectionManager);
stubMethods();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index fd2cf6d..3b39160 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -540,18 +540,23 @@
twoFingerTap();
assertIn(STATE_ACTIVATED);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
goFromStateIdleTo(STATE_ACTIVATED);
+ reset(mMockMagnificationLogger);
twoFingerTap();
twoFingerTap();
twoFingerTap();
assertIn(STATE_IDLE);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(false);
}
@Test
@@ -564,6 +569,8 @@
twoFingerTapAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@@ -576,6 +583,8 @@
twoFingerSwipeAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
new file mode 100644
index 0000000..3843e25
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2019 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.accessibility.magnification;
+
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import static java.lang.Float.NaN;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class MagnificationConnectionManagerTest {
+
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int SERVICE_ID = 1;
+
+ private MockWindowMagnificationConnection mMockConnection;
+ @Mock
+ private Context mContext;
+ @Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
+ private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ @Mock
+ private MagnificationAnimationCallback mAnimationCallback;
+ @Mock
+ private MagnificationConnectionManager.Callback mMockCallback;
+ private MockContentResolver mResolver;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+ mResolver = new MockContentResolver();
+ mMockConnection = new MockWindowMagnificationConnection();
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+ mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
+
+ when(mContext.getContentResolver()).thenReturn(mResolver);
+ stubSetConnection(false);
+
+ mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ Settings.Secure.putFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
+ CURRENT_USER_ID);
+ }
+
+ private void stubSetConnection(boolean needDelay) {
+ doAnswer((InvocationOnMock invocation) -> {
+ final boolean connect = (Boolean) invocation.getArguments()[0];
+ // Simulates setConnection() called by another process.
+ if (needDelay) {
+ final Context context = ApplicationProvider.getApplicationContext();
+ context.getMainThreadHandler().postDelayed(
+ () -> {
+ mMagnificationConnectionManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }, 10);
+ } else {
+ mMagnificationConnectionManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }
+ return true;
+ }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ verify(mMockConnection.getConnection()).setConnectionCallback(
+ any(IWindowMagnificationConnectionCallback.class));
+ }
+
+ @Test
+ public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+ 0);
+ }
+
+ /**
+ * This test simulates {@link MagnificationConnectionManager#setConnection} is called by thread
+ * A and then the former connection is called by thread B. In this situation we should keep the
+ * new connection.
+ */
+ @Test
+ public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ MockWindowMagnificationConnection secondConnection =
+ new MockWindowMagnificationConnection();
+
+ mMagnificationConnectionManager.setConnection(secondConnection.getConnection());
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+ verify(secondConnection.asBinder(), never()).unlinkToDeath(
+ secondConnection.getDeathRecipient(), 0);
+ }
+
+ @Test
+ public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.setConnection(null);
+
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.getConnection()).setConnectionCallback(null);
+ }
+
+ @Test
+ public void enableWithAnimation_hasConnection_enableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void enableWithCallback_hasConnection_enableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
+ mAnimationCallback, SERVICE_ID);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), eq(0f), eq(0f),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
+ }
+
+ @Test
+ public void disable_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ notNull());
+ }
+
+ @Test
+ public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mAnimationCallback);
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
+ }
+
+ @Test
+ public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void getPersistedScale() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ assertEquals(mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY), 2.5f);
+ }
+
+ @Test
+ public void persistScale_setValue_expectedValueInProvider() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+ assertEquals(Settings.Secure.getFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+ CURRENT_USER_ID), 2.5f);
+ }
+
+ @Test
+ public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ final float persistedScale =
+ mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 1.0f);
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+ assertEquals(Settings.Secure.getFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+ CURRENT_USER_ID), persistedScale);
+ }
+
+ @Test
+ public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY), 2.5f);
+ }
+
+ @Test
+ public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
+
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
+ MagnificationScaleProvider.MAX_SCALE);
+ }
+
+ @FlakyTest(bugId = 297879435)
+ @Test
+ public void logTrackingTypingFocus_processScroll_logDuration() {
+ MagnificationConnectionManager spyMagnificationConnectionManager = spy(
+ mMagnificationConnectionManager);
+ spyMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3.0f, 50f, 50f);
+ spyMagnificationConnectionManager.onImeWindowVisibilityChanged(
+ TEST_DISPLAY, /* shown */ true);
+
+ spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
+
+ verify(spyMagnificationConnectionManager).logTrackingTypingFocus(anyLong());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
+ throws RemoteException {
+ final float distanceX = 10f;
+ final float distanceY = 10f;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.inset(-10, -10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+ @Test
+ public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+ // Enabling a window magnifier again will turn on the tracking typing focus functionality.
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+ mMagnificationConnectionManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
+ }
+
+ @Test
+ public void showMagnificationButton_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.showMagnificationButton(TEST_DISPLAY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ mMagnificationConnectionManager.removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ }
+
+ @Test
+ public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
+ }
+
+ @Test
+ public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ final float testScale = 3f;
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+ CURRENT_USER_ID, TEST_DISPLAY, testScale);
+ verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
+ eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
+ }
+
+ @Test
+ public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+ new Rect(0, 0, 500, 500));
+ PointF[] pointersLocation = new PointF[2];
+ pointersLocation[0] = new PointF(600, 700);
+ pointersLocation[1] = new PointF(300, 400);
+ MotionEvent event = generatePointersDownEvent(pointersLocation);
+
+ assertEquals(mMagnificationConnectionManager.pointersInWindow(TEST_DISPLAY, event), 1);
+ }
+
+ @Test
+ public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
+ final float newScale = 4.0f;
+ final boolean updatePersistence = true;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ mMockConnection.getConnectionCallback().onPerformScaleAction(
+ TEST_DISPLAY, newScale, updatePersistence);
+
+ verify(mMockCallback).onPerformScaleAction(
+ eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
+ }
+
+ @Test
+ public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
+
+ verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
+ }
+
+ @Test
+ public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void
+ requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
+ throws RemoteException {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+ verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
+ }
+
+ @Test
+ public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
+ }
+
+ @Test
+ public void isConnected_requestConnection_expectedValue() throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ assertTrue(mMagnificationConnectionManager.isConnected());
+
+ mMagnificationConnectionManager.requestConnection(false);
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ }
+
+ @Test
+ public void requestConnection_registerAndUnregisterBroadcastReceiver() {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+
+ @Test
+ public void requestConnectionToNull_expectedGetterResults() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
+
+ mMagnificationConnectionManager.requestConnection(false);
+
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
+ public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
+ throws RemoteException {
+ stubSetConnection(true);
+ mMagnificationConnectionManager.requestConnection(true);
+
+ assertTrue(mMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3f, 1, 1));
+
+ // Invoke enableWindowMagnification if the connection is connected.
+ verify(mMockConnection.getConnection()).enableWindowMagnification(
+ eq(TEST_DISPLAY), eq(3f),
+ eq(1f), eq(1f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ }
+
+ @Test
+ public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
+ final int serviceId2 = SERVICE_ID + 1;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, serviceId2);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ }
+
+ @Test
+ public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+ mMagnificationConnectionManager.mScreenStateReceiver.onReceive(mContext,
+ new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplay_expectedValues() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
+ }
+
+ @Test
+ public void magnifierGetters_disabled_expectedValues() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
+ public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ mMagnificationConnectionManager.onDisplayRemoved(TEST_DISPLAY);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
+ }
+
+ @Test
+ public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
+
+ Mockito.reset(mMockCallback);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
+ anyBoolean());
+ }
+
+ private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
+ final int len = pointersLocation.length;
+
+ final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
+ for (int i = 0; i < len; i++) {
+ MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+ pointerProperty.id = i;
+ pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ pp[i] = pointerProperty;
+ }
+
+ final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
+ for (int i = 0; i < len; i++) {
+ MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+ pointerCoord.x = pointersLocation[i].x;
+ pointerCoord.y = pointersLocation[i].y;
+ pc[i] = pointerCoord;
+ }
+
+ return MotionEvent.obtain(
+ /* downTime */ SystemClock.uptimeMillis(),
+ /* eventTime */ SystemClock.uptimeMillis(),
+ /* action */ MotionEvent.ACTION_POINTER_DOWN,
+ /* pointerCount */ pc.length,
+ /* pointerProperties */ pp,
+ /* pointerCoords */ pc,
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 1.0f,
+ /* yPrecision */ 1.0f,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_TOUCHSCREEN,
+ /* flags */ 0);
+ }
+
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index cfd0289..8f85f11 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -39,7 +39,7 @@
/**
* Tests for MagnificationConnectionWrapper. We don't test {@code
* MagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
- * {@link WindowMagnificationManagerTest}.
+ * {@link MagnificationConnectionManagerTest}.
*/
public class MagnificationConnectionWrapperTest {
@@ -73,9 +73,9 @@
}
@Test
- public void setScale() throws RemoteException {
- mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
- verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mConnectionWrapper.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+ verify(mConnection).setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index d4c6fad..e8cdf35 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -131,7 +131,7 @@
private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
private MockWindowMagnificationConnection mMockConnection;
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockContentResolver mMockResolver;
private MagnificationController mMagnificationController;
private final WindowMagnificationMgrCallbackDelegate
@@ -205,13 +205,14 @@
));
mScreenMagnificationController.register(TEST_DISPLAY);
- mWindowMagnificationManager = spy(new WindowMagnificationManager(mContext, globalLock,
- mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
+ mMagnificationConnectionManager = spy(
+ new MagnificationConnectionManager(mContext, globalLock,
+ mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
- mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider,
+ mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
ConcurrentUtils.DIRECT_EXECUTOR));
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -254,8 +255,10 @@
mCallbackArgumentCaptor.getValue().onResult(true);
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -297,8 +300,10 @@
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -316,7 +321,7 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when activating the window mode again.
// The third time is triggered when the transition is completed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
}
@@ -330,7 +335,7 @@
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -350,7 +355,7 @@
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -386,11 +391,11 @@
mTransitionCallBack);
// Enable window magnification while animating.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
Float.NaN, Float.NaN, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -408,8 +413,10 @@
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
mMockConnection.invokeCallbacks();
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -438,7 +445,7 @@
animate, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
@@ -564,7 +571,7 @@
mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
- verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ verify(mMagnificationConnectionManager).onDisplayRemoved(TEST_DISPLAY);
verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY);
}
@@ -573,7 +580,7 @@
mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID);
verify(mScreenMagnificationController).resetAllIfNeeded(false);
- verify(mWindowMagnificationManager).disableAllWindowMagnifiers();
+ verify(mMagnificationConnectionManager).disableAllWindowMagnifiers();
verify(mScaleProvider).onUserChanged(SECOND_USER_ID);
}
@@ -584,7 +591,7 @@
mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
@@ -594,11 +601,11 @@
// The first time is trigger when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -625,8 +632,8 @@
mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
- verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
- verify(mWindowMagnificationManager, never()).persistScale(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(newScale));
+ verify(mMagnificationConnectionManager, never()).persistScale(eq(TEST_DISPLAY));
}
@Test
@@ -669,7 +676,7 @@
assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
assertEquals(config.getScale(), actualConfig.getScale(), 0);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(config.getScale()));
}
@@ -677,11 +684,11 @@
public void onSourceBoundChanged_windowEnabled_notifyMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- reset(mWindowMagnificationManager);
+ reset(mMagnificationConnectionManager);
mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, TEST_RECT);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(DEFAULT_SCALE));
}
@@ -780,11 +787,11 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -799,10 +806,11 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager, times(2))
+ .removeMagnificationButton(eq(TEST_DISPLAY));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -816,7 +824,7 @@
public void deactivateWindowMagnification_windowActivated_triggerCallbackAndLogUsage()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
verify(mMagnificationController).onWindowMagnificationActivationState(
eq(TEST_DISPLAY), eq(false));
@@ -828,7 +836,7 @@
public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
mMagnificationController.setMagnificationFollowTypingEnabled(false);
- verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+ verify(mMagnificationConnectionManager).setMagnificationFollowTypingEnabled(eq(false));
verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
}
@@ -850,7 +858,7 @@
verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -867,7 +875,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -880,7 +888,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -895,7 +903,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -970,7 +978,8 @@
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- verify(mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
+ verify(mMagnificationConnectionManager)
+ .disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
}
@Test
@@ -983,11 +992,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1001,11 +1010,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1018,11 +1027,11 @@
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1035,11 +1044,11 @@
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1053,11 +1062,11 @@
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1071,9 +1080,9 @@
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1082,11 +1091,11 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1100,11 +1109,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when fullscreen mode activation state is updated.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1113,10 +1122,10 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1126,8 +1135,8 @@
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1142,10 +1151,10 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1163,10 +1172,10 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1174,9 +1183,9 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1185,7 +1194,7 @@
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1260,17 +1269,17 @@
private void activateMagnifier(int displayId, int mode, float centerX, float centerY)
throws RemoteException {
- final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ final boolean windowMagnifying = mMagnificationConnectionManager.isWindowMagnifierEnabled(
displayId);
if (windowMagnifying) {
- mWindowMagnificationManager.disableWindowMagnification(displayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
mMockConnection.invokeCallbacks();
}
if (mode == MODE_FULLSCREEN) {
mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
centerX, centerY, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
}
@@ -1304,10 +1313,10 @@
}
private static class WindowMagnificationMgrCallbackDelegate implements
- WindowMagnificationManager.Callback {
- private WindowMagnificationManager.Callback mCallback;
+ MagnificationConnectionManager.Callback {
+ private MagnificationConnectionManager.Callback mCallback;
- public void setDelegate(WindowMagnificationManager.Callback callback) {
+ public void setDelegate(MagnificationConnectionManager.Callback callback) {
mCallback = callback;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 612a091..c4be51f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -92,7 +92,7 @@
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockWindowMagnificationConnection mMockConnection;
private SpyWindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
private WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
@@ -104,23 +104,23 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
- mock(WindowMagnificationManager.Callback.class), mMockTrace,
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+ mock(MagnificationConnectionManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new SpyWindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
+ mContext, mMagnificationConnectionManager, mMockTrace, mMockCallback,
/** detectSingleFingerTripleTap= */ true, /** detectTwoFingerTripleTap= */ true,
/** detectShortcutTrigger= */ true, DISPLAY_0);
mMockWindowMagnificationGestureHandler =
mWindowMagnificationGestureHandler.getMockGestureHandler();
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
}
@After
public void tearDown() {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, true);
}
@Test
@@ -378,7 +378,7 @@
}
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT: {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, false);
}
break;
case STATE_TWO_FINGERS_DOWN: {
@@ -423,7 +423,7 @@
}
private boolean isWindowMagnifierEnabled(int displayId) {
- return mWindowMagnificationManager.isWindowMagnifierEnabled(displayId);
+ return mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
private static String stateToString(int state) {
@@ -495,13 +495,14 @@
private final WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
SpyWindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap, boolean detectTwoFingerTripleTap,
boolean detectShortcutTrigger, int displayId) {
- super(context, windowMagnificationMgr, trace, callback, detectSingleFingerTripleTap,
- detectTwoFingerTripleTap, detectShortcutTrigger, displayId);
+ super(context, magnificationConnectionManager, trace, callback,
+ detectSingleFingerTripleTap, detectTwoFingerTripleTap,
+ detectShortcutTrigger, displayId);
mMockWindowMagnificationGestureHandler = mock(WindowMagnificationGestureHandler.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
deleted file mode 100644
index 24ad976..0000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (C) 2019 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.accessibility.magnification;
-
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import static java.lang.Float.NaN;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnectionCallback;
-import android.view.accessibility.MagnificationAnimationCallback;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.accessibility.AccessibilityTraceManager;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * Tests for WindowMagnificationManager.
- */
-public class WindowMagnificationManagerTest {
-
- private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
- private static final int SERVICE_ID = 1;
-
- private MockWindowMagnificationConnection mMockConnection;
- @Mock
- private Context mContext;
- @Mock
- private AccessibilityTraceManager mMockTrace;
- @Mock
- private StatusBarManagerInternal mMockStatusBarManagerInternal;
- @Mock
- private MagnificationAnimationCallback mAnimationCallback;
- @Mock
- private WindowMagnificationManager.Callback mMockCallback;
- private MockContentResolver mResolver;
- private WindowMagnificationManager mWindowMagnificationManager;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
- LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
- LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
- mResolver = new MockContentResolver();
- mMockConnection = new MockWindowMagnificationConnection();
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
- mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
-
- when(mContext.getContentResolver()).thenReturn(mResolver);
- stubSetConnection(false);
-
- mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- Settings.Secure.putFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
- CURRENT_USER_ID);
- }
-
- private void stubSetConnection(boolean needDelay) {
- doAnswer((InvocationOnMock invocation) -> {
- final boolean connect = (Boolean) invocation.getArguments()[0];
- // Simulates setConnection() called by another process.
- if (needDelay) {
- final Context context = ApplicationProvider.getApplicationContext();
- context.getMainThreadHandler().postDelayed(
- () -> {
- mWindowMagnificationManager.setConnection(
- connect ? mMockConnection.getConnection() : null);
- }, 10);
- } else {
- mWindowMagnificationManager.setConnection(
- connect ? mMockConnection.getConnection() : null);
- }
- return true;
- }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
- }
-
- @Test
- public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
- }
-
- @Test
- public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
- verify(mMockConnection.getConnection()).setConnectionCallback(
- any(IWindowMagnificationConnectionCallback.class));
- }
-
- @Test
- public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mMockConnection.getDeathRecipient().binderDied();
-
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
- 0);
- }
-
- /**
- * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
- * and then the former connection is called by thread B. In this situation we should keep the
- * new connection.
- */
- @Test
- public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- MockWindowMagnificationConnection secondConnection =
- new MockWindowMagnificationConnection();
-
- mWindowMagnificationManager.setConnection(secondConnection.getConnection());
- mMockConnection.getDeathRecipient().binderDied();
-
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
- verify(secondConnection.asBinder(), never()).unlinkToDeath(
- secondConnection.getDeathRecipient(), 0);
- }
-
- @Test
- public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.setConnection(null);
-
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.getConnection()).setConnectionCallback(null);
- }
-
- @Test
- public void enableWithAnimation_hasConnection_enableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void enableWithCallback_hasConnection_enableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
- mAnimationCallback, SERVICE_ID);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), eq(0f), eq(0f),
- any(IRemoteMagnificationAnimationCallback.class));
- verify(mAnimationCallback).onResult(true);
- }
-
- @Test
- public void disable_hasConnectionAndEnabled_disableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
- notNull());
- }
-
- @Test
- public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
- mAnimationCallback);
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
- any(IRemoteMagnificationAnimationCallback.class));
- verify(mAnimationCallback).onResult(true);
- }
-
- @Test
- public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void getPersistedScale() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f);
- }
-
- @Test
- public void persistScale_setValue_expectedValueInProvider() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
- assertEquals(Settings.Secure.getFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
- CURRENT_USER_ID), 2.5f);
- }
-
- @Test
- public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- final float persistedScale = mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 1.0f);
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
- assertEquals(Settings.Secure.getFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
- CURRENT_USER_ID), persistedScale);
- }
-
- @Test
- public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), 2.5f);
- }
-
- @Test
- public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
-
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY),
- MagnificationScaleProvider.MAX_SCALE);
- }
-
- @FlakyTest(bugId = 297879435)
- @Test
- public void logTrackingTypingFocus_processScroll_logDuration() {
- WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
- spyWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- spyWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, /* shown */ true);
-
- spyWindowMagnificationManager.processScroll(TEST_DISPLAY, 10f, 10f);
-
- verify(spyWindowMagnificationManager).logTrackingTypingFocus(anyLong());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
- throws RemoteException {
- final float distanceX = 10f;
- final float distanceY = 10f;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.inset(-10, -10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
- @Test
- public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
- // Enabling a window magnifier again will turn on the tracking typing focus functionality.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
- mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
- verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
- }
-
- @Test
- public void showMagnificationButton_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
- mWindowMagnificationManager.removeMagnificationButton(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
- }
-
- @Test
- public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
- }
-
- @Test
- public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- final float testScale = 3f;
- mWindowMagnificationManager.onUserMagnificationScaleChanged(
- CURRENT_USER_ID, TEST_DISPLAY, testScale);
- verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
- eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
- }
-
- @Test
- public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
- mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
- new Rect(0, 0, 500, 500));
- PointF[] pointersLocation = new PointF[2];
- pointersLocation[0] = new PointF(600, 700);
- pointersLocation[1] = new PointF(300, 400);
- MotionEvent event = generatePointersDownEvent(pointersLocation);
-
- assertEquals(mWindowMagnificationManager.pointersInWindow(TEST_DISPLAY, event), 1);
- }
-
- @Test
- public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
- final float newScale = 4.0f;
- final boolean updatePersistence = true;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- mMockConnection.getConnectionCallback().onPerformScaleAction(
- TEST_DISPLAY, newScale, updatePersistence);
-
- verify(mMockCallback).onPerformScaleAction(
- eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
- }
-
- @Test
- public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
-
- verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
- }
-
- @Test
- public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mMockConnection.getDeathRecipient().binderDied();
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void
- requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
- throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- assertTrue(mWindowMagnificationManager.requestConnection(false));
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
- }
-
- @Test
- public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
- }
-
- @Test
- public void isConnected_requestConnection_expectedValue() throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- assertTrue(mWindowMagnificationManager.isConnected());
-
- mWindowMagnificationManager.requestConnection(false);
- assertFalse(mWindowMagnificationManager.isConnected());
- }
-
- @Test
- public void requestConnection_registerAndUnregisterBroadcastReceiver() {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
-
- assertTrue(mWindowMagnificationManager.requestConnection(false));
- verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
- }
-
- @Test
- public void requestConnectionToNull_expectedGetterResults() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
-
- mWindowMagnificationManager.requestConnection(false);
-
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
- final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
- assertTrue(bounds.isEmpty());
- }
-
- @Test
- public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
- throws RemoteException {
- stubSetConnection(true);
- mWindowMagnificationManager.requestConnection(true);
-
- assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1));
-
- // Invoke enableWindowMagnification if the connection is connected.
- verify(mMockConnection.getConnection()).enableWindowMagnification(
- eq(TEST_DISPLAY), eq(3f),
- eq(1f), eq(1f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- }
-
- @Test
- public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
- final int serviceId2 = SERVICE_ID + 1;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, serviceId2);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- }
-
- @Test
- public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
- mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
- new Intent(Intent.ACTION_SCREEN_OFF));
-
- verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplay_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
- eq(100f), eq(200f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
- eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
- }
-
- @Test
- public void magnifierGetters_disabled_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
- final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
- assertTrue(bounds.isEmpty());
- }
-
- @Test
- public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
- mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
- }
-
- @Test
- public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
-
- Mockito.reset(mMockCallback);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
- anyBoolean());
- }
-
- private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
- final int len = pointersLocation.length;
-
- final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
- for (int i = 0; i < len; i++) {
- MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
- pointerProperty.id = i;
- pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
- pp[i] = pointerProperty;
- }
-
- final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
- for (int i = 0; i < len; i++) {
- MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
- pointerCoord.x = pointersLocation[i].x;
- pointerCoord.y = pointersLocation[i].y;
- pc[i] = pointerCoord;
- }
-
- return MotionEvent.obtain(
- /* downTime */ SystemClock.uptimeMillis(),
- /* eventTime */ SystemClock.uptimeMillis(),
- /* action */ MotionEvent.ACTION_POINTER_DOWN,
- /* pointerCount */ pc.length,
- /* pointerProperties */ pp,
- /* pointerCoords */ pc,
- /* metaState */ 0,
- /* buttonState */ 0,
- /* xPrecision */ 1.0f,
- /* yPrecision */ 1.0f,
- /* deviceId */ 0,
- /* edgeFlags */ 0,
- /* source */ InputDevice.SOURCE_TOUCHSCREEN,
- /* flags */ 0);
- }
-
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 749b07d..9c8276a 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -19,6 +19,16 @@
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
+
+import static junit.framework.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -34,11 +44,15 @@
import android.media.LoudnessCodecInfo;
import android.media.PlayerBase;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
+import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -84,8 +98,7 @@
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
verify(mDispatcher).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid), any());
}
@@ -96,8 +109,7 @@
mLoudnessHelper.unregisterLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/false,
- CODEC_METADATA_TYPE_MPEG_D)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/false, CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(0)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -108,11 +120,9 @@
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
- mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
+ mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(2)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -124,11 +134,10 @@
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
+ List.of(getLoudnessInfo(/*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
- mLoudnessHelper.addLoudnessCodecInfo(newPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ mLoudnessHelper.addLoudnessCodecInfo(newPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -140,12 +149,10 @@
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
//does not trigger dispatch since active apc list does not contain newPiid
mLoudnessHelper.startLoudnessCodecUpdates(newPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -157,9 +164,8 @@
@Test
public void updateCodecParameters_noStartedPiids_noDispatch() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
- mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
@@ -170,8 +176,8 @@
@Test
public void updateCodecParameters_removedCodecInfo_noDispatch() throws Exception {
- final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
- /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+ final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+ CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -186,8 +192,8 @@
@Test
public void updateCodecParameters_stoppedPiids_noDispatch() throws Exception {
- final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
- /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+ final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+ CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -200,6 +206,108 @@
any());
}
+ @Test
+ public void checkParcelableBundle_forMpeg4CodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+ }
+
+ @Test
+ public void checkParcelableBundle_forMpegDCodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+ }
+
private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
@@ -220,11 +328,15 @@
return apcList;
}
- private static LoudnessCodecInfo getLoudnessInfo(int mediaCodecHash, boolean isDownmixing,
- int metadataType) {
+ private static LoudnessCodecInputProperties createInputProperties(
+ int metadataType, boolean isDownmixing, @DeviceSplRange int splRange) {
+ return new LoudnessCodecInputProperties.Builder().setMetadataType(
+ metadataType).setIsDownmixing(isDownmixing).setDeviceSplRange(splRange).build();
+ }
+
+ private static LoudnessCodecInfo getLoudnessInfo(boolean isDownmixing, int metadataType) {
LoudnessCodecInfo info = new LoudnessCodecInfo();
info.isDownmixing = isDownmixing;
- info.mediaCodecHashCode = mediaCodecHash;
info.metadataType = metadataType;
return info;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index b5ba322..9213601a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -413,18 +413,24 @@
public void getDeviceIdForDisplayId_invalidDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_defaultDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
@@ -433,6 +439,8 @@
assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
.isEqualTo(mDeviceImpl.getDeviceId());
+ assertThat(mLocalService.getDeviceIdForDisplayId(DISPLAY_ID_1))
+ .isEqualTo(mDeviceImpl.getDeviceId());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 01922e0..edfe1b4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -40,6 +40,7 @@
import android.testing.TestableLooper;
import android.view.Surface;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +78,11 @@
when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
}
+ @After
+ public void tearDown() throws Exception {
+ mVirtualCameraController.close();
+ }
+
@Test
public void registerCamera_registersCamera() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
@@ -95,6 +101,8 @@
public void unregisterCamera_unregistersCamera() throws Exception {
VirtualCameraConfig config = createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+ mVirtualCameraController.registerCamera(config);
+
mVirtualCameraController.unregisterCamera(config);
verify(mVirtualCameraServiceMock).unregisterCamera(any());
@@ -107,9 +115,10 @@
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+ mVirtualCameraController.close();
+
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
- mVirtualCameraController.close();
verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
configurationCaptor.capture());
List<VirtualCameraConfiguration> virtualCameraConfigurations =
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
deleted file mode 100644
index 5aef7a3..0000000
--- a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2023 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.media;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.MediaRoute2Info;
-import android.os.RemoteException;
-
-import com.android.internal.R;
-import com.android.server.audio.AudioService;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(JUnit4.class)
-public class AudioPoliciesDeviceRouteControllerTest {
-
- private static final String ROUTE_NAME_DEFAULT = "default";
- private static final String ROUTE_NAME_DOCK = "dock";
- private static final String ROUTE_NAME_HEADPHONES = "headphones";
-
- private static final int VOLUME_SAMPLE_1 = 25;
-
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private AudioManager mAudioManager;
- @Mock
- private AudioService mAudioService;
- @Mock
- private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
-
- @Captor
- private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
-
- private AudioPoliciesDeviceRouteController mController;
-
- private IAudioRoutesObserver.Stub mAudioRoutesObserver;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT);
-
- // Setting built-in speaker as default speaker.
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
- when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
- .thenReturn(audioRoutesInfo);
-
- mController = new AudioPoliciesDeviceRouteController(
- mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener);
-
- mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue();
- }
-
- @Test
- public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
- MediaRoute2Info route2Info = mController.getSelectedRoute();
-
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
- }
-
- @Test
- public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void getDeviceRoute_selectDevice_returnsSelectedRoute() {
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- }
-
- @Test
- public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- }
-
- @Test
- public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(null);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void selectRoute_selectWiredRoute_returnsTrue() {
- assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue();
- }
-
- @Test
- public void selectRoute_selectBluetoothRoute_returnsFalse() {
- assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse();
- }
-
- @Test
- public void selectRoute_unselectRoute_returnsTrue() {
- assertThat(mController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.updateVolume(VOLUME_SAMPLE_1);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
- }
-
- @Test
- public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.updateVolume(VOLUME_SAMPLE_1);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
- }
-
- /**
- * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)}
- * from {@link AudioService}. This happens when there is a wired route change,
- * like a wired headset being connected.
- *
- * @param audioRoutesInfo updated state of connected wired device
- */
- private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) {
- try {
- // this is a captured observer implementation
- // from WiredRoutesController's AudioService#startWatchingRoutes call
- mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo);
- } catch (RemoteException exception) {
- // Should not happen since the object is mocked.
- assertWithMessage("An unexpected RemoteException happened.").fail();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index 14b121d..0961b7d 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -19,6 +19,7 @@
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
import android.content.Context;
+import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -56,7 +57,8 @@
@RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
DeviceRouteController deviceRouteController =
- DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+ DeviceRouteController.createInstance(
+ mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
Truth.assertThat(deviceRouteController).isInstanceOf(LegacyDeviceRouteController.class);
}
@@ -65,7 +67,8 @@
@RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
DeviceRouteController deviceRouteController =
- DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+ DeviceRouteController.createInstance(
+ mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
Truth.assertThat(deviceRouteController)
.isInstanceOf(AudioPoliciesDeviceRouteController.class);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 6ae2658..cd29c80 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -24,11 +24,13 @@
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
+import android.app.usage.Flags;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.LongSparseArray;
@@ -183,6 +185,17 @@
case Event.LOCUS_ID_SET:
event.mLocusId = "locus" + (i % 7); //"random" locus
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ // "random" user interaction extras.
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ "fake.namespace.category" + (i % 13));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION,
+ "fakeaction" + (i % 13));
+ event.mExtras = extras;
+ }
+ break;
}
mIntervalStats.addEvent(event);
@@ -295,6 +308,18 @@
assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
"Usage event " + debugId);
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ PersistableBundle extras1 = e1.getExtras();
+ PersistableBundle extras2 = e2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ "Usage event " + debugId);
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ "Usage event " + debugId);
+ }
+ break;
}
// fallthrough
case 4: // test fields added in version 4
diff --git a/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
new file mode 100644
index 0000000..377e4c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link UserSettingDeviceConfigMediator}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UserSettingDeviceConfigMediatorTest {
+ @Test
+ public void testDeviceConfigOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 1)
+ .setFloat("float", .5f)
+ .setBoolean("boolean", true)
+ .setLong("long", 123456789)
+ .setString("string", "abc123")
+ .build();
+
+ mediator.setDeviceConfigProperties(properties);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the properties
+ mediator.setDeviceConfigProperties(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123";
+
+ mediator.setSettingsString(settings);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the settings
+ mediator.setSettingsString(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOverridesAll() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides all, anything in DeviceConfig should be ignored,
+ // even if settings doesn't have a value for it.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(123, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString("");
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString(null);
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+
+ @Test
+ public void testSettingsOverridesIndividual() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides individual, anything in DeviceConfig that doesn't exist in
+ // settings should be used.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index d000605..32082e3 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -28,6 +28,7 @@
import android.os.Build;
import android.os.Bundle;
import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.suitebuilder.annotation.MediumTest;
@@ -1449,4 +1450,21 @@
checkPreparationPhasesForPackage(currentSdkPackage.packageName,
1 /* first preparation phase */);
}
+
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
+ String nonDefaultPackage = "nonDefaultPackage";
+ String defaultPackage1 = "defaultPackage1";
+ String defaultPackage2 = "defaultPackage2";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+ new WebViewProviderInfo(defaultPackage1, "", true, false, null),
+ new WebViewProviderInfo(defaultPackage2, "", true, false, null)
+ };
+ setupWithPackages(packages);
+ assertEquals(
+ defaultPackage1, mWebViewUpdateServiceImpl.getDefaultWebViewPackage().packageName);
+ }
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f2a1fe8..c3074bb 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -93,8 +93,6 @@
android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
- <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
-
<activity android:name="com.android.server.wm.SurfaceSyncGroupTests$TestActivity"
android:screenOrientation="locked"
android:turnScreenOn="true"
@@ -122,6 +120,13 @@
<activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity"
android:exported="true">
</activity>
+
+ <activity android:name="com.android.server.wm.utils.TestActivity"
+ android:screenOrientation="locked"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true" />
</application>
<instrumentation
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index f8ebead..46e87dc 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -30,4 +30,8 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+ </target_preparer>
</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 2c35cf0..8d236ed 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -110,24 +110,6 @@
}
/**
- * META + SPACE to switch keyboard layout.
- */
- @Test
- public void testMetaSpace() {
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SPACE}, 0);
- mPhoneWindowManager.assertSwitchKeyboardLayout(1);
- }
-
- /**
- * META + SHIFT + SPACE to switch keyboard layout backwards.
- */
- @Test
- public void testMetaShiftSpace() {
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0);
- mPhoneWindowManager.assertSwitchKeyboardLayout(-1);
- }
-
- /**
* CTRL + ALT + Z to enable accessibility service.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 71098aa..360fdf3 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -132,13 +132,6 @@
{"LANGUAGE_SWITCH key -> Switch Keyboard Language",
new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
- {"Meta + Space -> Switch Keyboard Language",
- new int[]{META_KEY, KeyEvent.KEYCODE_SPACE},
- KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON},
- {"Meta + Shift + Space -> Switch Keyboard Language",
- new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE},
- KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE,
- META_ON | SHIFT_ON},
{"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON},
{"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 9f58491..9efbe35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,8 +25,6 @@
import static com.android.server.wm.utils.LastCallVerifier.lastCall;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,11 +34,14 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import com.android.server.testutils.StubTransaction;
import com.android.server.wm.utils.MockAnimationAdapter;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -122,7 +123,8 @@
}
}
- static class MockAnimationAdapterFactory extends SmoothDimmer.AnimationAdapterFactory {
+ static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory {
+ @Override
public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
SurfaceAnimationRunner runner) {
return sTestAnimation;
@@ -175,8 +177,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
final int blur = 50;
mHost.addChild(mChild, 0);
@@ -195,8 +197,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, alpha, 20);
@@ -210,8 +212,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -230,8 +232,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -290,8 +292,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 2);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -310,8 +312,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 0);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -330,8 +332,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimmerWithBlurUpdatesTransaction_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final int blurRadius = 50;
@@ -344,4 +346,120 @@
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
+
+ /**
+ * mChild is requesting the dim values to be set directly. In this case, dim won't play the
+ * standard animation, but directly apply mChild's requests to the dim surface
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsOpeningAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.3f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(mTransaction).setAlpha(dimLayer, 0.2f);
+ verify(mTransaction).setAlpha(dimLayer, 0.3f);
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ }
+
+ /**
+ * Same as testContainerDimsOpeningAnimationByItself, but this is a more specific case in which
+ * alpha is animated to 0. This corner case is needed to verify that the layer is removed anyway
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsClosingAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).remove(dimLayer);
+ }
+
+ /**
+ * Check the handover of the dim between two windows and the consequent dim animation in between
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingConsecutively() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(2)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
+
+ /**
+ * Two windows are trying to modify the dim at the same time, but only the last request before
+ * updateDims will be satisfied
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingAtTheSameTime() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index 8119fd4..3b9ed26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -23,15 +23,14 @@
import static org.junit.Assert.assertTrue;
-import android.app.Activity;
import android.app.Instrumentation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.Gravity;
import android.view.IWindow;
import android.view.SurfaceControl;
@@ -46,12 +45,12 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
+import com.android.server.wm.utils.TestActivity;
import org.junit.After;
import org.junit.Before;
@@ -59,11 +58,14 @@
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@RunWith(WindowTestRunner.class)
public class SurfaceControlViewHostTests {
+ private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
private static final String TAG = "SurfaceControlViewHostTests";
private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
@@ -76,6 +78,8 @@
private SurfaceControlViewHost mScvh1;
private SurfaceControlViewHost mScvh2;
+ private SurfaceView mSurfaceView;
+
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -96,15 +100,17 @@
mView1 = new Button(mActivity);
mView2 = new Button(mActivity);
- mInstrumentation.runOnMainSync(() -> {
- try {
- mActivity.attachToSurfaceView(sc);
- } catch (InterruptedException e) {
- }
+ CountDownLatch svReadyLatch = new CountDownLatch(1);
+ mActivity.runOnUiThread(() -> addSurfaceView(svReadyLatch));
+ assertTrue("Failed to wait for SV to get created",
+ svReadyLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+ .show(sc).apply();
+ mInstrumentation.runOnMainSync(() -> {
TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
mActivity.getResources().getConfiguration(), sc,
- mActivity.mSurfaceView.getHostToken());
+ mSurfaceView.getHostToken());
mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
wwm, "requestFocusWithMultipleWindows");
@@ -135,7 +141,7 @@
}
assertTrue("Failed to wait for view2", wasVisible);
- IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
+ IWindow window = IWindow.Stub.asInterface(mSurfaceView.getWindowToken());
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh1.getInputTransferToken(), true);
@@ -162,43 +168,30 @@
}
}
- public static class TestActivity extends Activity implements SurfaceHolder.Callback {
- private SurfaceView mSurfaceView;
- private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+ private void addSurfaceView(CountDownLatch svReadyLatch) {
+ final FrameLayout content = mActivity.getParentLayout();
+ mSurfaceView = new SurfaceView(mActivity);
+ mSurfaceView.setZOrderOnTop(true);
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+ Gravity.LEFT | Gravity.TOP);
+ mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ svReadyLatch.countDown();
+ }
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final FrameLayout content = new FrameLayout(this);
- mSurfaceView = new SurfaceView(this);
- mSurfaceView.setBackgroundColor(Color.BLACK);
- mSurfaceView.setZOrderOnTop(true);
- final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
- Gravity.LEFT | Gravity.TOP);
- content.addView(mSurfaceView, lp);
- setContentView(content);
- mSurfaceView.getHolder().addCallback(this);
- }
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSvReadyLatch.countDown();
- }
+ }
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
- int height) {
- }
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- }
-
- public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
- mSvReadyLatch.await();
- new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
- .show(sc).apply();
- }
+ }
+ });
+ content.addView(mSurfaceView, lp);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index 3cb4a1d..e65a9fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -19,6 +19,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import android.os.Handler;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
@@ -39,6 +41,9 @@
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
this::onBeforeSystemServicesCreated);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@WindowTestRunner.MethodWrapperRule
public final WindowManagerGlobalLockRule mLockRule =
new WindowManagerGlobalLockRule(mSystemServicesTestRule);
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 b1def8d..51f0404 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -444,7 +444,9 @@
SurfaceAnimationThread.dispose();
AnimationThread.dispose();
UiThread.dispose();
- mInputChannel.dispose();
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ }
tearDownLocalServices();
// Reset priority booster because animation thread has been changed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 8a90f12..06f29c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -25,7 +25,9 @@
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1759,6 +1761,40 @@
}
@Test
+ public void testApplyTransaction_createTaskFragmentDecorSurface() {
+ // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+
+ final TaskFragment tf = createTaskFragment(task);
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE).build();
+ mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+ assertApplyTransactionAllowed(mTransaction);
+
+ verify(task).moveOrCreateDecorSurfaceFor(tf);
+ }
+
+ @Test
+ public void testApplyTransaction_removeTaskFragmentDecorSurface() {
+ // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf = createTaskFragment(task);
+
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE).build();
+ mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+ assertApplyTransactionAllowed(mTransaction);
+
+ verify(task).removeDecorSurface();
+ }
+
+ @Test
public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
@@ -1966,7 +2002,8 @@
/** Setups the mock Task as the parent of the given TaskFragment. */
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
- doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true))
+ doReturn(new TaskFragmentParentInfo(
+ new Configuration(), DEFAULT_DISPLAY, true, true, null /* decorSurface */))
.when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
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 5e531b4..da7612b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -63,6 +63,7 @@
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.app.ActivityManager;
@@ -82,6 +83,7 @@
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.SurfaceControl;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -1619,6 +1621,185 @@
assertFalse(task.isDragResizing());
}
+ @Test
+ public void testMoveOrCreateDecorSurface() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+ // Decor surface should not be present initially.
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+
+ // Decor surface should be created.
+ clearInvocations(task);
+ task.moveOrCreateDecorSurfaceFor(fragment);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ assertNotNull(task.getTaskFragmentParentInfo().getDecorSurface());
+ assertEquals(fragment, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Decor surface should be removed.
+ clearInvocations(task);
+ task.removeDecorSurface();
+
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+ }
+
+ @Test
+ public void testMoveOrCreateDecorSurface_whenOwnerTaskFragmentRemoved() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+ task.moveOrCreateDecorSurfaceFor(fragment1);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ assertEquals(fragment1, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Transfer ownership
+ task.moveOrCreateDecorSurfaceFor(fragment2);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ assertEquals(fragment2, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Safe surface should be removed when the owner TaskFragment is removed.
+ task.removeChild(fragment2);
+
+ verify(task).removeDecorSurface();
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ }
+
+ @Test
+ public void testAssignChildLayers_decorSurfacePlacement() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord unembeddedActivity = task.getTopMostActivity();
+
+ final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ spyOn(unembeddedActivity);
+ spyOn(fragment1);
+ spyOn(fragment2);
+
+ // Initially, the decor surface should not be placed.
+ task.assignChildLayers(t);
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(fragment2).assignLayer(t, 2);
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed just above the owner TaskFragment.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.moveOrCreateDecorSurfaceFor(fragment1);
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be invisible if the owner TaskFragment is invisible.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(false).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, false);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed on below activity from a different UID.
+ doReturn(false).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 0);
+ verify(unembeddedActivity).assignLayer(t, 1);
+ verify(fragment1).assignLayer(t, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed below untrusted embedded TaskFragment.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(false).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should not be placed after removal.
+ task.removeDecorSurface();
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(fragment2).assignLayer(t, 2);
+ }
+
private Task getTestTask() {
return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index dade3b9..71447e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2015,6 +2015,9 @@
transition.collect(leafTaskA);
rootTaskA.moveToFront("test", leafTaskA);
+ // Test has order changes, a shallow check of order changes
+ assertTrue(transition.hasOrderChanges());
+
// All the tasks were already visible, so there shouldn't be any changes
ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
participants, changes);
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
new file mode 100644
index 0000000..c12dcdd
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * TestActivity that will ensure it dismisses keyguard and shows as a fullscreen activity.
+ */
+public class TestActivity extends Activity {
+ private static final int sTypeMask = systemBars() | displayCutout();
+ private FrameLayout mParentLayout;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mParentLayout = new FrameLayout(this);
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(mParentLayout, layoutParams);
+
+ WindowInsetsController windowInsetsController = getWindow().getInsetsController();
+ windowInsetsController.hide(sTypeMask);
+ WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ getWindow().setAttributes(params);
+ getWindow().setDecorFitsSystemWindows(false);
+
+ final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService(
+ KeyguardManager.class);
+ if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
+ keyguardManager.requestDismissKeyguard(this, null);
+ }
+ }
+
+ public FrameLayout getParentLayout() {
+ return mParentLayout;
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index bfb159f..dce4818 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -31,6 +31,7 @@
import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -42,7 +43,9 @@
import android.app.usage.EventStats;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -575,6 +578,23 @@
continue;
}
break;
+ case USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ String category = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mCategoryToken);
+ String action = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mActionToken);
+ if (TextUtils.isEmpty(category) || TextUtils.isEmpty(action)) {
+ this.events.remove(i);
+ dataOmitted = true;
+ continue;
+ }
+ event.mExtras = new PersistableBundle();
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, category);
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+ event.mUserInteractionExtrasToken = null;
+ }
+ break;
}
}
if (dataOmitted) {
@@ -692,13 +712,30 @@
event.mPackage, event.mLocusId);
}
break;
+ case USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ final String category = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ final String action = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (!TextUtils.isEmpty(category) && !TextUtils.isEmpty(action)) {
+ event.mUserInteractionExtrasToken =
+ new Event.UserInteractionEventExtrasToken();
+ event.mUserInteractionExtrasToken.mCategoryToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ category);
+ event.mUserInteractionExtrasToken.mActionToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ action);
+ }
+ }
+ break;
}
}
}
/**
* Obfuscates the data in this instance of interval stats.
- *
* @hide
*/
public void obfuscateData(PackagesTokenData packagesTokenData) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index 8138747..d865345 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -17,8 +17,10 @@
import android.app.usage.ConfigurationStats;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event.UserInteractionEventExtrasToken;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -26,6 +28,8 @@
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -282,6 +286,16 @@
event.mLocusIdToken = proto.readInt(
EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
break;
+ case (int) EventObfuscatedProto.INTERACTION_EXTRAS:
+ try {
+ final long interactionExtrasToken = proto.start(
+ EventObfuscatedProto.INTERACTION_EXTRAS);
+ event.mUserInteractionExtrasToken = parseUserInteractionEventExtras(proto);
+ proto.end(interactionExtrasToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some user interaction extras from proto.", e);
+ }
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
}
@@ -386,7 +400,7 @@
}
private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
- final UsageEvents.Event event) throws IllegalArgumentException {
+ final UsageEvents.Event event) throws IOException, IllegalArgumentException {
proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
@@ -429,6 +443,12 @@
event.mNotificationChannelIdToken + 1);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ writeUserInteractionEventExtras(proto, EventObfuscatedProto.INTERACTION_EXTRAS,
+ event.mUserInteractionExtrasToken);
+ }
+ break;
}
}
@@ -703,6 +723,9 @@
case (int) PendingEventProto.TASK_ROOT_CLASS:
event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
break;
+ case (int) PendingEventProto.EXTRAS:
+ event.mExtras = parsePendingEventExtras(proto, PendingEventProto.EXTRAS);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
@@ -757,7 +780,7 @@
}
private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
if (event.mClass != null) {
proto.write(PendingEventProto.CLASS_NAME, event.mClass);
@@ -794,6 +817,11 @@
event.mNotificationChannelId);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ writePendingEventExtras(proto, PendingEventProto.EXTRAS, event.mExtras);
+ }
+ break;
}
}
@@ -888,4 +916,52 @@
proto.end(token);
}
}
+
+ private static UserInteractionEventExtrasToken parseUserInteractionEventExtras(
+ ProtoInputStream proto) throws IOException {
+ UserInteractionEventExtrasToken interactionExtrasToken =
+ new UserInteractionEventExtrasToken();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN:
+ interactionExtrasToken.mCategoryToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN) - 1;
+ break;
+ case (int) ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN:
+ interactionExtrasToken.mActionToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN) - 1;
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return interactionExtrasToken;
+ }
+ }
+ }
+
+ static void writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId,
+ UserInteractionEventExtrasToken interactionExtras) {
+ final long token = proto.start(fieldId);
+ proto.write(ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN,
+ interactionExtras.mCategoryToken + 1);
+ proto.write(ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN,
+ interactionExtras.mActionToken + 1);
+ proto.end(token);
+ }
+
+ /**
+ * Populates the extra details for pending interaction event from the protobuf stream.
+ */
+ private static PersistableBundle parsePendingEventExtras(ProtoInputStream proto, long fieldId)
+ throws IOException {
+ return PersistableBundle.readFromStream(new ByteArrayInputStream(proto.readBytes(fieldId)));
+ }
+
+ /**
+ * Write the extra details for pending interaction event to a protobuf stream.
+ */
+ static void writePendingEventExtras(ProtoOutputStream proto, long fieldId,
+ PersistableBundle eventExtras) throws IOException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ eventExtras.writeToStream(baos);
+ proto.write(fieldId, baos.toByteArray());
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4c56f33..0e1e0c8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -83,6 +83,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -91,6 +92,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -193,6 +195,11 @@
private static final char TOKEN_DELIMITER = '/';
+ // The maximum length for extras {@link UsageStatsManager#EXTRA_EVENT_CATEGORY},
+ // {@link UsageStatsManager#EXTRA_EVENT_ACTION} in a {@link UsageEvents.Event#mExtras}.
+ // The value will be truncated at this limit.
+ private static final int MAX_TEXT_LENGTH = 127;
+
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
@@ -1814,6 +1821,13 @@
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
+ private String getTrimmedString(String input) {
+ if (input != null && input.length() > MAX_TEXT_LENGTH) {
+ return input.substring(0, MAX_TEXT_LENGTH);
+ }
+ return input;
+ }
+
/**
* Called by the Binder stub.
*/
@@ -2253,6 +2267,32 @@
}
}
+ private void reportUserInteractionInnerHelper(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
+ }
+ } else {
+ if (!isCallingUidSystem()) {
+ throw new SecurityException("Only system is allowed to call"
+ + " reportUserInteraction");
+ }
+ }
+
+ // Verify if this package exists before reporting an event for it.
+ if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
+ throw new IllegalArgumentException("Package " + packageName + "not exist!");
+ }
+
+ final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = packageName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
long endTime, String callingPackage, int userId) {
@@ -2686,23 +2726,36 @@
@Override
public void reportUserInteraction(String packageName, int userId) {
+ reportUserInteractionInnerHelper(packageName, userId, null);
+ }
+
+ @Override
+ public void reportUserInteractionWithBundle(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
Objects.requireNonNull(packageName);
- if (Flags.reportUsageStatsPermission()) {
- if (!canReportUsageStats()) {
- throw new SecurityException(
- "Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportUserInteraction");
- }
- } else {
- if (!isCallingUidSystem()) {
- throw new SecurityException("Only system is allowed to call"
- + " reportUserInteraction");
- }
+ if (extras == null || extras.size() == 0) {
+ throw new IllegalArgumentException("Emtry extras!");
}
- final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
- event.mPackage = packageName;
- reportEventOrAddToQueue(userId, event);
+ // Only category/action are allowed now, other unknown keys will be trimmed.
+ // Also, empty category/action is not meanful.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ if (TextUtils.isEmpty(category)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ }
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_ACTION);
+ }
+
+ PersistableBundle extrasCopy = new PersistableBundle();
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+
+ reportUserInteractionInnerHelper(packageName, userId, extrasCopy);
}
@Override
@@ -3160,6 +3213,24 @@
}
@Override
+ public void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ if (extras != null && extras.size() != 0) {
+ // Truncate the value if necessary.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+ }
+
+ Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = pkgName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
+ @Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
userId, SystemClock.elapsedRealtime());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b67ab6..3bc7752 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1110,6 +1110,10 @@
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
+
+ if ((event.mEventType == Event.USER_INTERACTION) && (event.mExtras != null)) {
+ pw.print(event.mExtras.toString());
+ }
pw.printHexPair("flags", event.mFlags);
pw.println();
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 356e1fa..8354d98 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -39,5 +39,5 @@
private fun ClassNodes.isAidlClass(className: String): Boolean {
return hasClass(className) &&
hasClass("$className\$Stub") &&
- hasClass("$className\$Proxy")
+ hasClass("$className\$Stub\$Proxy")
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 214de59..3956893 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -223,16 +223,16 @@
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -243,7 +243,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -262,7 +262,8 @@
SourceFile: "IPretendingAidl.java"
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
Compiled from "IPretendingAidl.java"
public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
@@ -303,6 +304,7 @@
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
Compiled from "IPretendingAidl.java"
public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
@@ -315,11 +317,11 @@
}
SourceFile: "IPretendingAidl.java"
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index e01f49b..4f8c408e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -205,16 +205,16 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -225,7 +225,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -242,7 +242,8 @@
0 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -288,6 +289,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -306,8 +308,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -315,8 +317,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 5246355..5ff3cde 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -289,13 +289,13 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 4
private static {};
@@ -303,17 +303,17 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String <init>
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -324,14 +324,14 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String addTwo
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -346,7 +346,8 @@
11 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -412,6 +413,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -439,8 +441,8 @@
x: return
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -448,8 +450,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
index 583e13c..0a07c2b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
@@ -25,11 +25,12 @@
public static int addOne(int a) {
return a + 1;
}
- }
- public static class Proxy {
- public static int addTwo(int a) {
- return a + 2;
+ public static class Proxy {
+ public static int addTwo(int a) {
+ return a + 2;
+ }
}
}
+
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 0d52791..d350105 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -289,6 +289,6 @@
@Test
public void testAidlHeuristics() {
assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
- assertThat(IPretendingAidl.Proxy.addTwo(1)).isEqualTo(3);
+ assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
}
}