diff options
424 files changed, 7363 insertions, 4276 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 158d914575c6..e6c94d896e50 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.ActivityManagerInternal; 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; 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.Trace; 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.DeviceIdleConstraintTracker; 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 @@ public class DeviceIdleController extends SystemService * 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 @@ public class DeviceIdleController extends SystemService /** * 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 class DeviceIdleController extends SystemService */ 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 @@ public class DeviceIdleController extends SystemService } 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } } - 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 d2150b80761f..8381d1a322e0 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.annotation.Nullable; 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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { 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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { 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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { // 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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { 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 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { 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 a4043dd8ba78..61096b9fa179 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.content.ContentResolver; 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 @@ public abstract class EconomicPolicy { } 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 @@ public abstract class EconomicPolicy { 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 91a291fe20db..69e57365b6e0 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.annotation.Nullable; 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 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { 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 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { 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 309135411137..805ecd43e3c7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9854,7 +9854,7 @@ package android.content { 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); @@ -10601,6 +10601,7 @@ package android.content { 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"; @@ -10613,6 +10614,7 @@ package android.content { 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"; @@ -11101,6 +11103,7 @@ package android.content { 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"; @@ -12366,6 +12369,7 @@ package android.content.pm { 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); @@ -12422,6 +12426,9 @@ package android.content.pm { 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); @@ -12443,6 +12450,10 @@ package android.content.pm { 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 @@ -12458,6 +12469,13 @@ package android.content.pm { 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 { @@ -12621,6 +12639,7 @@ package android.content.pm { 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; @@ -12650,6 +12669,7 @@ package android.content.pm { 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; @@ -12772,6 +12792,7 @@ package android.content.pm { 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); @@ -13013,6 +13034,7 @@ package android.content.pm { 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 @@ -22034,6 +22056,19 @@ package android.media { 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); @@ -27302,6 +27337,13 @@ package android.media.tv { } +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 { @@ -32840,7 +32882,7 @@ package android.os { 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); @@ -33379,6 +33421,13 @@ package android.os { 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 fc23f9bc43ff..e2b2879c1e42 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3614,7 +3614,6 @@ package android.content { 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"; @@ -3862,16 +3861,9 @@ package android.content.pm { 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"; @@ -3882,23 +3874,12 @@ package android.content.pm { 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 { @@ -3948,14 +3929,12 @@ package android.content.pm { 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 { @@ -3987,7 +3966,6 @@ package android.content.pm { 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); @@ -4105,7 +4083,6 @@ package android.content.pm { 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 4f8e8dd813a1..014ddd41f8d4 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -3484,9 +3484,20 @@ class ContextImpl extends Context { // 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 6009c29ae53c..24a51573b48a 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -159,7 +159,8 @@ public class ResourcesManager { * 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 @@ public class ResourcesManager { * 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 79a5879b5cc0..9cf732abb86a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -137,6 +137,8 @@ import android.media.projection.MediaProjectionManager; 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.IHardwarePropertiesManager; 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.PerformanceHintManager; 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 @@ public final class SystemServiceRegistry { 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 @@ public final class SystemServiceRegistry { 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 6a03c17159d3..ce1d43d10c34 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -180,6 +180,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { @Override public void injectInputEventToInputFilter(InputEvent event) throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } mAccessibilityManager.injectInputEventToInputFilter(event); } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 0ae00cd7d133..9eb73b32a2a7 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -837,6 +837,7 @@ public final class UsageEvents implements Parcelable { if (mEventCount != mEventsToWrite.size()) { Log.w(TAG, "Partial usage event list received: " + mEventCount + " != " + mEventsToWrite.size()); + mEventCount = mEventsToWrite.size(); } } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 51a7f1ce8c5f..6e451479c5a4 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -558,7 +558,7 @@ public class AppWidgetManager { } }).toArray(ComponentName[]::new)); } catch (Exception e) { - Log.e(TAG, "Nofity service of inheritance info", e); + Log.e(TAG, "Notify service of inheritance info", e); } }); } diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 4b2cee698df2..697c25c2a1ec 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -730,10 +730,7 @@ public final class AttributionSource implements Parcelable { /** * 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 1c6c7b5baa58..b75c64dcc3c1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -72,6 +72,7 @@ import android.net.Uri; 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 @@ public abstract class Context { DEVICE_LOCK_SERVICE, VIRTUALIZATION_SERVICE, GRAMMATICAL_INFLECTION_SERVICE, + SECURITY_STATE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) @@ -5818,6 +5820,17 @@ public abstract class Context { /** * 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 abstract class Context { 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 c7a86fbe0171..38bcfa220af4 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5353,11 +5353,11 @@ public class Intent implements Parcelable, Cloneable { * 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 1cfdb8b37fcd..5736a6d8cb4a 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -20,7 +20,6 @@ import android.annotation.CurrentTimeMillisLong; 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 @@ public class PackageInfo implements Parcelable { /** * 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 6df1f600c3ef..d35c3922e9b7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -354,10 +354,7 @@ public class PackageInstaller { /** * 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 @@ public class PackageInstaller { * 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 @@ public class PackageInstaller { * 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 @@ public class PackageInstaller { * * <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 @@ public class PackageInstaller { * * <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 @@ public class PackageInstaller { * <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 @@ public class PackageInstaller { * 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 @@ public class PackageInstaller { * 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 @@ public class PackageInstaller { * @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 @@ public class PackageInstaller { * * @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 @@ public class PackageInstaller { * @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 @@ public class PackageInstaller { * <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 c7091ad99199..70e6f9864eb6 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -177,12 +177,10 @@ public class PackageItemInfo { /** * 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 6775f9b8d84d..a22fe3f1452b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1263,18 +1263,14 @@ public abstract class PackageManager { /** * 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 @@ public abstract class PackageManager { * * @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 a565f6825e7a..d21b81854584 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -94,3 +94,10 @@ flag { 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 1b8eb0748737..40592a151fa7 100644 --- a/core/java/android/content/res/flags.aconfig +++ b/core/java/android/content/res/flags.aconfig @@ -15,3 +15,12 @@ flag { 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 d786d9a20189..18c95bfbb297 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -66,6 +66,13 @@ public final class SensorPrivacyManager { + ".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/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java index d4ce0ebbc528..5cbb0bbadc4c 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 ed3100251040..eabe13b0c54f 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -123,6 +123,12 @@ public final class BatteryUsageStats implements Parcelable, Closeable { 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 @@ public final class BatteryUsageStats implements Parcelable, Closeable { 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 000000000000..8ae624d0371d --- /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 da647e2b78cb..161951ead77f 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 @@ public final class Message implements Parcelable { * @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 000000000000..4fa61e0ca782 --- /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 a78f221fc962..c085f334457a 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -57,6 +57,13 @@ flag { } 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 6853892348d9..78a12f75a508 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1738,23 +1738,6 @@ public class StorageManager { 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/provider/Settings.java b/core/java/android/provider/Settings.java index ff6ec29bb8ac..8f18c5f58c7e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14975,6 +14975,38 @@ public final class Settings { 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/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7a6c2929c706..cac5387116a1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -991,7 +991,7 @@ public final class ViewRootImpl implements ViewParent, // 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 a11c6d0ce956..a404bd6f8c97 100644 --- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl +++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl @@ -54,7 +54,7 @@ oneway interface IWindowMagnificationConnection { * @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 e17773159ec5..c6bd20cec07d 100644 --- a/core/java/android/webkit/IWebViewUpdateService.aidl +++ b/core/java/android/webkit/IWebViewUpdateService.aidl @@ -79,4 +79,9 @@ interface IWebViewUpdateService { * 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/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index 4e0f9a51c0a0..0ec9ffe6390b 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -108,6 +108,18 @@ public final class TaskFragmentOperation implements Parcelable { */ 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 @@ public final class TaskFragmentOperation implements Parcelable { 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 e6eeca4b4801..a77c23475c60 100644 --- a/core/java/android/window/TaskFragmentParentInfo.java +++ b/core/java/android/window/TaskFragmentParentInfo.java @@ -22,6 +22,9 @@ import android.app.WindowConfiguration; 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 @@ public class TaskFragmentParentInfo implements Parcelable { 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 @@ public class TaskFragmentParentInfo implements Parcelable { mDisplayId = info.mDisplayId; mVisible = info.mVisible; mHasDirectActivity = info.mHasDirectActivity; + mDecorSurface = info.mDecorSurface; } /** The {@link Configuration} of the parent Task */ @@ -92,7 +99,13 @@ public class TaskFragmentParentInfo implements Parcelable { 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 @@ public class TaskFragmentParentInfo implements Parcelable { + ", displayId=" + mDisplayId + ", visible=" + mVisible + ", hasDirectActivity=" + mHasDirectActivity + + ", decorSurface=" + mDecorSurface + "}"; } @@ -128,7 +142,8 @@ public class TaskFragmentParentInfo implements Parcelable { 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 @@ public class TaskFragmentParentInfo implements Parcelable { 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 @@ public class TaskFragmentParentInfo implements Parcelable { dest.writeInt(mDisplayId); dest.writeBoolean(mVisible); dest.writeBoolean(mHasDirectActivity); + dest.writeTypedObject(mDecorSurface, flags); } private TaskFragmentParentInfo(Parcel in) { @@ -153,6 +170,7 @@ public class TaskFragmentParentInfo implements Parcelable { 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/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java index f5fe12eb66c0..e55c64199f45 100644 --- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java +++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java @@ -37,22 +37,11 @@ public class RefreshRateSettingsUtils { * @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/res/res/values/config.xml b/core/res/res/values/config.xml index 8bbc80907ad6..1d4e01a95591 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4993,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 3d43004db98f..4b0fa4ba5173 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" /> 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 aaaa3c7740c5..ac1f7d0e345f 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 @@ public class BatteryUsageStatsPulledTest { 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 @@ public class BatteryUsageStatsPulledTest { 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 @@ public class BatteryUsageStatsPulledTest { 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 @@ public class BatteryUsageStatsPulledTest { 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 @@ public class BatteryUsageStatsPulledTest { // 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 @@ public class BatteryUsageStatsPulledTest { // 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/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 87c167c0df07..4a9cb7180a3f 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 @@ public class ResourcesManagerTest extends TestCase { } @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 @@ public class ResourcesManagerTest extends TestCase { 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/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 5e16bcee1a0e..dd703f5eefb9 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -33,7 +33,6 @@ import android.system.keystore2.ResponseCode; 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 @@ public class KeyStore2 { 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)); + } + if (mBinder == null) { + throw new IllegalStateException( + "Could not connect to Keystore service. Keystore may have crashed or not been" + + " initialized"); } - return Objects.requireNonNull(mBinder); + 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 50cfd941adb3..4c2433fab2f8 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 @@ public class OverlayPresentationTest { 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 02031a67e7e3..8c274a26177d 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 class SplitControllerTest { 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 e56c8ab686e7..7b77235f66f7 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 @@ public class TaskContainerTest { 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 @@ public class TaskContainerTest { 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/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index f5b877a70b84..a3eb429b1d7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -412,6 +412,23 @@ public class BubbleExpandedView extends LinearLayout { setLayoutDirection(LAYOUT_DIRECTION_LOCALE); } + + /** Updates the width of the task view if it changed. */ + void updateTaskViewContentWidth() { + if (mTaskView != null) { + int width = getContentWidth(); + if (mTaskView.getWidth() != width) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width, MATCH_PARENT); + mTaskView.setLayoutParams(lp); + } + } + } + + private int getContentWidth() { + boolean isStackOnLeft = mPositioner.isStackOnLeft(mStackView.getStackPosition()); + return mPositioner.getTaskViewContentWidth(isStackOnLeft); + } + /** * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need * to be called after view inflate. @@ -438,7 +455,12 @@ public class BubbleExpandedView extends LinearLayout { mController.getTaskViewTransitions(), mController.getSyncTransactionQueue()); mTaskView = new TaskView(mContext, mTaskViewTaskController); mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener); - mExpandedViewContainer.addView(mTaskView); + + // set a fixed width so it is not recalculated as part of a rotation. the width will be + // updated manually after the rotation. + FrameLayout.LayoutParams lp = + new FrameLayout.LayoutParams(getContentWidth(), MATCH_PARENT); + mExpandedViewContainer.addView(mTaskView, lp); bringChildToFront(mTaskView); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 1efd9df3a1d9..baa52a0b5626 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -375,6 +375,13 @@ public class BubblePositioner { } } + /** Returns the width of the task view content. */ + public int getTaskViewContentWidth(boolean onLeft) { + int[] paddings = getExpandedViewContainerPadding(onLeft, /* isOverflow = */ false); + int pointerOffset = showBubblesVertically() ? getPointerSize() : 0; + return mPositionRect.width() - paddings[0] - paddings[2] - pointerOffset; + } + /** Gets the y position of the expanded view if it was top-aligned. */ public float getExpandedViewYTopAligned() { final int top = getAvailableRect().top; 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 8f904c42d247..91a8ce726c42 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 @@ public class BubbleStackView extends FrameLayout return; } - final boolean clickedBubbleIsCurrentlyExpandedBubble = - clickedBubble.getKey().equals(mExpandedBubble.getKey()); + final boolean clickedBubbleIsCurrentlyExpandedBubble = mExpandedBubble != null + && clickedBubble.getKey().equals(mExpandedBubble.getKey()); if (isExpanded()) { mExpandedAnimationController.onGestureFinished(); @@ -3288,6 +3288,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble, mPositioner.showBubblesVertically() ? p.y : p.x)); mExpandedViewContainer.setTranslationX(0f); + mExpandedBubble.getExpandedView().updateTaskViewContentWidth(); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); updatePointerPosition(false /* forIme */); 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 271a3b26305d..63afd3e7a2ca 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 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { && 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/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 8cbcde320795..53ec20192f2b 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.View; 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 @@ class DragResizeInputListener implements AutoCloseable { 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 @@ class DragResizeInputListener implements AutoCloseable { 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 @@ class DragResizeInputListener implements AutoCloseable { 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 @@ class DragResizeInputListener implements AutoCloseable { .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 @@ class DragResizeInputListener implements AutoCloseable { 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 6429b00a2a58..fafd37bba14b 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/location/api/system-current.txt b/location/api/system-current.txt index a1d6ab5798e1..b1cf96d41497 100644 --- a/location/api/system-current.txt +++ b/location/api/system-current.txt @@ -113,13 +113,13 @@ package android.location { } 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 65af3928bd02..2f0835ab8af5 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 @@ public final class GnssMeasurementRequest implements Parcelable { * * @hide */ + @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @SystemApi public @NonNull WorkSource getWorkSource() { return mWorkSource; @@ -298,6 +301,7 @@ public final class GnssMeasurementRequest implements Parcelable { * * @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 c471a2749617..b6055e818f8c 100644 --- a/location/java/android/location/flags/gnss.aconfig +++ b/location/java/android/location/flags/gnss.aconfig @@ -5,4 +5,18 @@ flag { 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 d14775fa9ad9..5c8758a6aa73 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -739,7 +739,7 @@ interface IAudioService { 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 92f337244daf..de9d87c0b28c 100644 --- a/media/java/android/media/LoudnessCodecConfigurator.java +++ b/media/java/android/media/LoudnessCodecConfigurator.java @@ -30,10 +30,10 @@ import androidx.annotation.GuardedBy; 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 @@ import java.util.concurrent.atomic.AtomicBoolean; * 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 @@ public class LoudnessCodecConfigurator { /** * 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 @@ public class LoudnessCodecConfigurator { * @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 @@ public class LoudnessCodecConfigurator { * 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 @@ public class LoudnessCodecConfigurator { * @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 @@ public class LoudnessCodecConfigurator { * 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 @@ public class LoudnessCodecConfigurator { * 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,31 +245,32 @@ public class LoudnessCodecConfigurator { 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 (piid != PLAYER_PIID_INVALID) { - mLcDispatcher.addLoudnessCodecInfo(piid, mcInfo); + 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, mediaCodec.hashCode(), mcInfo); + } } /** @@ -297,37 +281,44 @@ public class LoudnessCodecConfigurator { * <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 @@ public class LoudnessCodecConfigurator { * * @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 @@ public class LoudnessCodecConfigurator { } /** @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 @@ public class LoudnessCodecConfigurator { 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 be881b11e545..b546a81b0498 100644 --- a/media/java/android/media/LoudnessCodecDispatcher.java +++ b/media/java/android/media/LoudnessCodecDispatcher.java @@ -27,12 +27,16 @@ import android.os.PersistableBundle; 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 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub { 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 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub { @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 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub { 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 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub { } /** @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 fd695179057d..0ac5646a3047 100644 --- a/media/java/android/media/LoudnessCodecInfo.aidl +++ b/media/java/android/media/LoudnessCodecInfo.aidl @@ -23,7 +23,7 @@ package android.media; * * {@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 @@ parcelable LoudnessCodecInfo { 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 aa5a290346b0..2b52c4b107b6 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 @@ import android.util.Log; /** * 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 @@ public class TvAdManager { /** * 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 a73d1ff72a17..018eaf6dc32f 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 @@ flag { 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 65a9799431e7..c9e36b7f10bd 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 @@ public class LoudnessCodecConfiguratorTest { 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 fe26dc3d7feb..991fe41bb7f3 100644 --- a/packages/CredentialManager/Android.bp +++ b/packages/CredentialManager/Android.bp @@ -16,10 +16,12 @@ android_app { 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 42f1207c69cb..325d3f819f14 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.mapper.toGet 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.parseCancelUiRequest(packageManager: PackageManager): Request? = } } -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 83183b5f58eb..3ef65b052560 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.TAG 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 @@ class CredentialManagerClientImpl @Inject constructor( 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 000000000000..f063074b39b4 --- /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 34710704facb..000000000000 --- 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 d4bca2add6cb..f1f1f7ca842e 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 index cc92f60de048..3f85192d937f 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 index ee36989b3da0..6d59f11d5643 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/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt index 2289ed7320ca..763646233324 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 @@ sealed class Request private constructor( 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 000000000000..d6189eb15ff3 --- /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/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt new file mode 100644 index 000000000000..7ee50d7c4e73 --- /dev/null +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt @@ -0,0 +1,36 @@ +/* + * 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 com.android.credentialmanager.model.EntryInfo + +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 000000000000..d9eee868c17f --- /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 000000000000..01c394fc0d74 --- /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 000000000000..9725881e1c97 --- /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 000000000000..6da414617752 --- /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/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt new file mode 100644 index 000000000000..a68bf7413b25 --- /dev/null +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt @@ -0,0 +1,36 @@ +/* + * 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 com.android.credentialmanager.model.EntryInfo + +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 bce86c477e77..6c5a984f20f7 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.getValue 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 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum 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 @@ class CredentialSelectorViewModel( 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 @@ class CredentialSelectorViewModel( /**************************************************************************/ /***** 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 @@ class CredentialSelectorViewModel( ) } - 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 f0fa6c5c4dd2..fc3970de2cee 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.graphics.drawable.Drawable 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 @@ class GetFlowUtils { 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 @@ class GetFlowUtils { 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 @@ class CreateFlowUtils { 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 @@ class CreateFlowUtils { 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 281696dec717..20d2f09ced8f 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.CustomCredentialEntry 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { } 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { presentationBuilder.build()) .build()) .setAuthentication(pendingIntent.intentSender) - .setAuthenticationExtras(primaryEntry.fillInIntent.extras) + .setAuthenticationExtras(fillInIntent.extras) .build()) datasetAdded = true } @@ -322,8 +323,8 @@ class CredentialAutofillService : AutofillService() { } 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 d45b6f687193..14a91651753b 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.compose.ui.unit.dp 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.SheetContainerCard 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 @@ fun PasskeyIntroCard( @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 @@ fun NonDefaultUsageConfirmationCard( @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 @@ fun CreationSelectionCard( @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 @@ fun MoreAboutPasskeysIntroCard( @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 @@ fun MoreOptionsInfoRow( 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 e9e8c2e0ccbf..8b0ba87fa9be 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 @@ class DisabledProviderInfo( 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 @@ data class RequestDisplayInfo( * 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 72d030b3e657..4ed84b908865 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.compose.ui.unit.dp 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.HeadlineIcon 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 @@ fun PrimarySelectionCard( 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 PrimarySelectionCard( 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 @@ fun HeadlineProviderIconAndName( @Composable fun ActionChips( providerInfoList: List<ProviderInfo>, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, isFirstSection: Boolean, ) { val actionChips = providerInfoList.flatMap { it.actionEntryList } @@ -460,7 +465,7 @@ fun ActionChips( @Composable fun RemoteEntryCard( remoteEntry: RemoteEntryInfo, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, isFirstSection: Boolean, ) { CredentialListSectionHeader( @@ -486,7 +491,7 @@ fun RemoteEntryCard( @Composable fun LockedCredentials( authenticationEntryList: List<AuthenticationEntryInfo>, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, isFirstSection: Boolean, ) { CredentialListSectionHeader( @@ -508,7 +513,7 @@ fun LockedCredentials( @Composable fun PerUserNameCredentials( perUserNameCredentialEntryList: PerUserNameCredentialEntryList, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, isFirstSection: Boolean, ) { CredentialListSectionHeader( @@ -532,7 +537,7 @@ fun PerUserNameCredentials( @Composable fun CredentialEntryRow( credentialEntryInfo: CredentialEntryInfo, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, enforceOneLine: Boolean = false, onTextLayout: (TextLayoutResult) -> Unit = {}, ) { @@ -571,7 +576,7 @@ fun CredentialEntryRow( @Composable fun AuthenticationEntryRow( authenticationEntryInfo: AuthenticationEntryInfo, - onEntrySelected: (BaseEntry) -> Unit, + onEntrySelected: (EntryInfo) -> Unit, enforceOneLine: Boolean = false, ) { Entry( @@ -596,7 +601,7 @@ fun AuthenticationEntryRow( @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 447a9d2aaa8d..46bebc4073ab 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 @@ internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): Cred 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 @@ data class ProviderDisplayInfo( 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 @@ fun toProviderDisplayInfo( 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 @@ fun toProviderDisplayInfo( 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 @@ internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<Cre 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 6ededf3411bf..d8a6019b68a2 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 f2f878e8ac2f..14b992a0d0e9 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 @@ fun Request.Get.toGet(): CredentialSelectorUiState.Get { // 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 c28df3e8895a..b64f58192d23 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 @@ fun SinglePasswordScreen( } 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 fb72c544c978..26bee1f8d191 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.ViewModel 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 @@ class SinglePasswordScreenViewModel @Inject constructor( 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 @@ class SinglePasswordScreenViewModel @Inject constructor( _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 @@ class SinglePasswordScreenViewModel @Inject constructor( fun onOKClick() { _uiState.value = SinglePasswordScreenUiState.PasswordSelected( - intentSenderRequest = password.getIntentSenderRequest() + intentSenderRequest = entryInfo.getIntentSenderRequest() ) } @@ -94,9 +95,9 @@ class SinglePasswordScreenViewModel @Inject constructor( ) { 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 @@ sealed class SinglePasswordScreenUiState { 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 38bd7d5f3944..6213b34034af 100644 --- a/packages/PackageInstaller/Android.bp +++ b/packages/PackageInstaller/Android.bp @@ -49,6 +49,7 @@ android_app { "androidx.fragment_fragment", "androidx.lifecycle_lifecycle-livedata", "androidx.lifecycle_lifecycle-extensions", + "android.content.pm.flags-aconfig-java", ], lint: { @@ -75,6 +76,7 @@ android_app { "androidx.fragment_fragment", "androidx.lifecycle_lifecycle-livedata", "androidx.lifecycle_lifecycle-extensions", + "android.content.pm.flags-aconfig-java", ], aaptflags: ["--product tablet"], @@ -103,6 +105,7 @@ android_app { "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/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index e2107ebe2525..418705845065 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 @@ public class InstallStart extends Activity { 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 @@ public class InstallStart extends Activity { finish(); return; } - mPackageManager = getPackageManager(); mUserManager = getSystemService(UserManager.class); Intent intent = getIntent(); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java index 34062a4cbde6..ba627e9e9202 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 @@ public class UninstallerActivity extends Activity { 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 @@ public class UninstallerActivity extends Activity { // 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/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index 80b944f5749f..60bf48c8b75e 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -25,11 +25,11 @@ plugins { 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 4335173dd706..acd90f3c4f4d 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -57,13 +57,13 @@ dependencies { 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 26372b6bfd32..c395558b769c 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 @@ fun SettingsTheme(content: @Composable () -> Unit) { MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) { CompositionLocalProvider( LocalColorScheme provides settingsColorScheme(isDarkTheme), - LocalRippleTheme provides SettingsRippleTheme, + LocalContentColor provides MaterialTheme.colorScheme.onSurface, ) { content() } @@ -52,19 +50,3 @@ object SettingsTheme { @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 0a2d9fc3372e..e41126f03c60 100644 --- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java @@ -140,12 +140,6 @@ public class PrimarySwitchPreference extends RestrictedPreference { } } - @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 8b0f19d455d2..29ea25e13835 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -22,7 +22,6 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 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 @@ public class RestrictedPreferenceHelper { } 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 f03263b71138..107d5f8a8ae9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -728,14 +728,4 @@ public class Utils { 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 efed8c3c1ef4..85e87691ac85 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -235,6 +235,7 @@ public class SettingsBackupTest { 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 d78038ecee61..7cf562f48ff3 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -242,184 +242,10 @@ filegroup { } 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 @@ android_robolectric_test { "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/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS index e1a7a0f84ad0..902ba907a100 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 000000000000..5fd3b485e9ed --- /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 17620651128f..3e84597a375d 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -164,3 +164,9 @@ flag { 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 0b1338305076..eb0688914b9d 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.material3.TextField 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 @@ internal fun PasswordBouncer( 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 index 3fbcf6d77f82..3fbcf6d77f82 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/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 index 74c922561343..74c922561343 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/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 index d41c2497b230..d41c2497b230 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ b/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 index 80d45bcc23dd..80d45bcc23dd 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/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 index 543b2910bbda..543b2910bbda 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/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 index 156e06843d15..156e06843d15 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/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 index 7bb6ef1c8895..7bb6ef1c8895 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ b/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 index 94c3bde29597..94c3bde29597 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/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 index 7b1f302da6e8..7b1f302da6e8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt +++ b/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 index 64ddbc7828ac..64ddbc7828ac 100644 --- 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 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 index 56d3d260d196..56d3d260d196 100644 --- 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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index 602f3dc29491..602f3dc29491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/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 index 714461b715d6..714461b715d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java +++ b/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 index 15633d1baed1..15633d1baed1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +++ b/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 index 39f0d570cb26..39f0d570cb26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +++ b/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 index a1b801cd3d3f..a1b801cd3d3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/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 index f5b6f14a627c..f5b6f14a627c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/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 index e2cab29c473c..e2cab29c473c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/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 index cd9189bef7f1..cd9189bef7f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java +++ b/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 index 5239966f1923..5239966f1923 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java +++ b/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 index 2ea803c6aa8f..2ea803c6aa8f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ b/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 index 98d8b054716c..98d8b054716c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ b/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 index 79f062536404..79f062536404 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/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 index 8b374ae54127..8b374ae54127 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt +++ b/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 index 9fbe09619ff1..9fbe09619ff1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ b/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 index b391b5a45799..b391b5a45799 100644 --- 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 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 index f0d26b6bbb78..f0d26b6bbb78 100644 --- 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 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 index 1e8073246f98..83fb17fa50e4 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 @@ class BouncerInteractorTest : SysuiTestCase() { @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 index 4aea4f329858..4aea4f329858 100644 --- 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 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 index bdf5041f8a38..bdf5041f8a38 100644 --- 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 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 index 8c53c0e3f267..8c53c0e3f267 100644 --- 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 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 index 63c992bd7854..45c186dc3a77 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.data.repository.FakeAuthenticationRep 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 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { 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 index 75d6a007b4aa..75d6a007b4aa 100644 --- 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 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 index 90e0c19b7c65..90e0c19b7c65 100644 --- 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 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 index 9b1e9585979a..937c703d6775 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.ext.junit.runners.AndroidJUnit4 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 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { 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 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { 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 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { 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 index 862c39c9d4cc..862c39c9d4cc 100644 --- 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 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 index c30e405ab911..c30e405ab911 100644 --- 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 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 index 55016bb1fc07..55016bb1fc07 100644 --- 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 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 index 182712a13174..182712a13174 100644 --- 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 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 index 16cfa2398fd5..16cfa2398fd5 100644 --- 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 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 index a49629252520..a49629252520 100644 --- 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 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 index 97ac8c62d69d..97ac8c62d69d 100644 --- 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 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 index 910097eece52..910097eece52 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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java index 37540621557f..37540621557f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java +++ b/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 index 8a35ef11a364..8a35ef11a364 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt +++ b/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 index 2c6c793c97f5..2c6c793c97f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt +++ b/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 index 2af6566e993a..2af6566e993a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/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 index d379dc6f3dc1..d379dc6f3dc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java +++ b/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 index e5f997257cfa..e5f997257cfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/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 index 6d5cd49b8af6..6d5cd49b8af6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/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 index 7ff345cdcf7e..7ff345cdcf7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java +++ b/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 index 39db2beb4c44..39db2beb4c44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/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 index 315a24bfd945..315a24bfd945 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java +++ b/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 index e0c6ab20c6e1..e0c6ab20c6e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +++ b/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 index 480754c17616..480754c17616 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java +++ b/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 index 3d1efa59a11b..3d1efa59a11b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/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 index 6aa821f15ab1..6aa821f15ab1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java +++ b/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 index 017fdbe8ac01..017fdbe8ac01 100644 --- 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 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 index 4ee4a60fbeaf..4ee4a60fbeaf 100644 --- 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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt index 0538227abd3f..0538227abd3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt +++ b/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 index 977f1db44258..977f1db44258 100644 --- 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 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 index 548b5646f5d4..548b5646f5d4 100644 --- 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 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 index 4ae144c03314..4ae144c03314 100644 --- 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 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 index 7d68cc0a3560..7d68cc0a3560 100644 --- 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 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 index 26fcb234843d..26fcb234843d 100644 --- 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 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 index 99a01858471c..99a01858471c 100644 --- 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 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 index a1c9f87ee7bc..a1c9f87ee7bc 100644 --- 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 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 index b15352bfe6ab..b15352bfe6ab 100644 --- 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 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 index 521dea34513e..521dea34513e 100644 --- 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 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 index 02db0d7a9a50..02db0d7a9a50 100644 --- 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 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 index a9b9c9011636..a9b9c9011636 100644 --- 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 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 index 4587ea6dbdc8..4587ea6dbdc8 100644 --- 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 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 index 45aca175657e..45aca175657e 100644 --- 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 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 index 2b7221ec192c..2b7221ec192c 100644 --- 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 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 index ae6c5b7b36b0..ae6c5b7b36b0 100644 --- 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 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 index 567e0a9717fc..567e0a9717fc 100644 --- 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 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 index 6c4bb372bc3a..6c4bb372bc3a 100644 --- 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 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 index 7242cb20dc77..7242cb20dc77 100644 --- 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 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 index ee47c58f4002..ee47c58f4002 100644 --- 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 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 index 706f94e412ac..706f94e412ac 100644 --- 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 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 index 98f0211587ea..98f0211587ea 100644 --- 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 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 index bc4bae0ed959..bc4bae0ed959 100644 --- 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 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 index 4f7d9444020c..4f7d9444020c 100644 --- 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 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 index b483085cf1e5..b483085cf1e5 100644 --- 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 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 index fd125e099f1b..fd125e099f1b 100644 --- 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 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 index cf2012989624..cf2012989624 100644 --- 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 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 index d07836d3abce..d07836d3abce 100644 --- 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 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 index ba72b4c95a44..ba72b4c95a44 100644 --- 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 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 index 3536d5c77c93..3536d5c77c93 100644 --- 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 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 index d0772270ed5e..d0772270ed5e 100644 --- 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 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 index 6cab023d59b0..6cab023d59b0 100644 --- 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 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 index d277fcab3690..d277fcab3690 100644 --- 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 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 index 3db676d68f42..3db676d68f42 100644 --- 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 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 index 070e07a75d23..070e07a75d23 100644 --- 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 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 index ff8a9bd019fb..ff8a9bd019fb 100644 --- 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 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 index f7c3b213730c..f7c3b213730c 100644 --- 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 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 index 9516c2181ac0..9516c2181ac0 100644 --- 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 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 index 36e860e37ffa..36e860e37ffa 100644 --- 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 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 index d4a9fabd6806..d4a9fabd6806 100644 --- 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 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 index 4454a3cb15fc..4454a3cb15fc 100644 --- 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 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 index d153e9d1d361..d153e9d1d361 100644 --- 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 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 index ec139e4c515e..ec139e4c515e 100644 --- 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 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 index 4fae532d4174..4fae532d4174 100644 --- 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 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 index 9e2d1f885e2d..9e2d1f885e2d 100644 --- 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 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 index 0116bd9575d8..0116bd9575d8 100644 --- 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 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 index e7ea9a66450c..e7ea9a66450c 100644 --- 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 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 index 20fd3601f9ef..20fd3601f9ef 100644 --- 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 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 index 19ac63c36cab..19ac63c36cab 100644 --- 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 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 index d645ee34619b..d645ee34619b 100644 --- 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 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 index 83ff35d8022d..83ff35d8022d 100644 --- 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 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 index adccc84e494b..adccc84e494b 100644 --- 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 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 index 41a7ec03408d..41a7ec03408d 100644 --- 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 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 index 8c896a6a1709..8c896a6a1709 100644 --- 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 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 index 7e0e7d1f46e8..7e0e7d1f46e8 100644 --- 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 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 index 0d9711588a1f..0d9711588a1f 100644 --- 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 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 index f73cab8a10a3..f73cab8a10a3 100644 --- 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 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 index 558e7694b54c..558e7694b54c 100644 --- 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 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 index bd1c310ab8de..bd1c310ab8de 100644 --- 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 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 index 9861606fd1b1..9861606fd1b1 100644 --- 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 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 index 2bdc154dd885..2bdc154dd885 100644 --- 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 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 index 92c2d743c262..92c2d743c262 100644 --- 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 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 index 937744db500e..937744db500e 100644 --- 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 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 index 81bde8188f5e..81bde8188f5e 100644 --- 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 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 index 89ba69fce9ad..89ba69fce9ad 100644 --- 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 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 index 4a221134ce67..4a221134ce67 100644 --- 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 diff --git a/packages/SystemUI/tests/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 index cf076c557765..cf076c557765 100644 --- a/packages/SystemUI/tests/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 diff --git a/packages/SystemUI/tests/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 index eebb145ef384..eebb145ef384 100644 --- a/packages/SystemUI/tests/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 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 fab290da2953..7b2ac90b9766 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.domain.model.FlashlightTile 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 @@ import org.junit.runner.RunWith 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 @@ class FlashlightMapperTest : SysuiTestCase() { @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 @@ class FlashlightMapperTest : SysuiTestCase() { @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 820c0564c1a7..8791877f8863 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.domain.model.LocationTileMode 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 @@ import org.junit.runner.RunWith 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 @@ class LocationTileMapperTest : SysuiTestCase() { @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 @@ class LocationTileMapperTest : SysuiTestCase() { @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 index 5eca8caa7d15..5eca8caa7d15 100644 --- 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 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 index 3a0ebdbd6a17..3a0ebdbd6a17 100644 --- 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 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 index 22fb152aee44..22fb152aee44 100644 --- 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 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 index 42e27ba12f42..42e27ba12f42 100644 --- 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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index c953743fd272..18b7168098ab 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.data.repository.FakeAuthenticationRep 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 @@ -777,11 +778,11 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { 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 index ddeb05b39e53..ddeb05b39e53 100644 --- 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 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 index 7ae501d05fcd..7ae501d05fcd 100644 --- 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 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 index 7f4bbbe36768..7f4bbbe36768 100644 --- 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 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 index 8be4eeb7be7a..8be4eeb7be7a 100644 --- 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 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 index adc1c61da50a..3cb97e369a67 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 @@ -479,7 +479,7 @@ class SceneContainerStartableTest : SysuiTestCase() { 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 index c89cd9e0c1f1..c89cd9e0c1f1 100644 --- 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 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 index 77ddf15c41ad..77ddf15c41ad 100644 --- 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 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 index e2640af136a7..e2640af136a7 100644 --- 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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt index cb83e7c7adbc..cb83e7c7adbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt +++ b/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 index e09385934991..e09385934991 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt +++ b/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 index 886c61ae29dd..886c61ae29dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt +++ b/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 index 0b5aea7d8683..0b5aea7d8683 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt +++ b/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 index c669c6f6fb1c..c669c6f6fb1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java +++ b/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 index 638925d0a705..638925d0a705 100644 --- 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 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 index 5f4d7bf6f371..5f4d7bf6f371 100644 --- 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 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 index 75d1869adc7c..75d1869adc7c 100644 --- 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 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 index a80238167b85..a80238167b85 100644 --- 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 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 index 28d632d9fcea..28d632d9fcea 100644 --- 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 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 index 106b54891948..106b54891948 100644 --- 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 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 index d06a6e26b4ce..d06a6e26b4ce 100644 --- 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 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 index ce00250467f6..ce00250467f6 100644 --- 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 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 index cf20ba87e8c2..cf20ba87e8c2 100644 --- 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 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 index 7fbbfc77300e..7fbbfc77300e 100644 --- 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 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 index 791a028eef4e..791a028eef4e 100644 --- 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 diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java index b704f3c89330..1edb551eb944 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 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { private final DisplayTracker mDisplayTracker; private final AccessibilityLogger mA11yLogger; - private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl; + private MagnificationConnectionImpl mMagnificationConnectionImpl; private SysUiState mSysUiState; @VisibleForTesting @@ -220,7 +220,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { } @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 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { 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 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { @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 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { if (magnificationSettingsController != null) { magnificationSettingsController.closeMagnificationSettings(); } - if (mWindowMagnificationConnectionImpl != null) { - mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode); + if (mMagnificationConnectionImpl != null) { + mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode); } } } @@ -500,12 +500,12 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { } 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 index 5666851f560f..5f0d496dd5d1 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 @@ import com.android.systemui.dagger.qualifiers.Main; * * @see IWindowMagnificationConnection */ -class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub { +class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub { private static final String TAG = "WindowMagnificationConnectionImpl"; @@ -40,7 +40,7 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S 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 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S } @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/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index b598631c3b57..7c46339ec103 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 @@ constructor( 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 @@ constructor( 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 80248744c25a..e379dab918ef 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 @@ sealed class AuthMethodBouncerViewModel( */ 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 @@ sealed class AuthMethodBouncerViewModel( /** * 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 @@ sealed class AuthMethodBouncerViewModel( } /** - * 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 a15698e1f90c..45d181285df7 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.bouncer.domain.interactor.BouncerInteractor 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 @@ class PasswordBouncerViewModel( 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 @@ class PasswordBouncerViewModel( 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/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index bd73d60cda29..62a0b0ebc08c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -131,13 +131,16 @@ constructor( when (toState) { KeyguardState.DREAMING -> TO_DREAMING_DURATION KeyguardState.AOD -> TO_AOD_DURATION + KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION else -> DEFAULT_DURATION }.inWholeMilliseconds } } + companion object { private val DEFAULT_DURATION = 500.milliseconds val TO_DREAMING_DURATION = 933.milliseconds val TO_AOD_DURATION = 1300.milliseconds + val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 152d2172ee4c..cbfd17ff7ae4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -380,6 +380,8 @@ constructor( KeyguardState.DREAMING -> TO_DREAMING_DURATION KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION KeyguardState.AOD -> TO_AOD_DURATION + KeyguardState.DOZING -> TO_DOZING_DURATION + KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION else -> DEFAULT_DURATION }.inWholeMilliseconds } @@ -388,7 +390,9 @@ constructor( companion object { const val TAG = "FromLockscreenTransitionInteractor" private val DEFAULT_DURATION = 400.milliseconds + val TO_DOZING_DURATION = 500.milliseconds val TO_DREAMING_DURATION = 933.milliseconds + val TO_DREAMING_HOSTED_DURATION = 933.milliseconds val TO_OCCLUDED_DURATION = 450.milliseconds val TO_AOD_DURATION = 500.milliseconds val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 706aba3c0505..f7d1543e4650 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -72,6 +72,10 @@ constructor( val fromDreamingTransition: Flow<TransitionStep> = repository.transitions.filter { step -> step.from == DREAMING } + /** LOCKSCREEN->(any) transition information. */ + val fromLockscreenTransition: Flow<TransitionStep> = + repository.transitions.filter { step -> step.from == LOCKSCREEN } + /** (any)->Lockscreen transition information */ val anyStateToLockscreenTransition: Flow<TransitionStep> = repository.transitions.filter { step -> step.to == LOCKSCREEN } @@ -113,9 +117,16 @@ constructor( val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED) + /** GONE->LOCKSCREEN transition information. */ + val goneToLockscreenTransition: Flow<TransitionStep> = repository.transition(GONE, LOCKSCREEN) + /** LOCKSCREEN->AOD transition information. */ val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) + /** LOCKSCREEN->DOZING transition information. */ + val lockscreenToDozingTransition: Flow<TransitionStep> = + repository.transition(LOCKSCREEN, DOZING) + /** LOCKSCREEN->DREAMING transition information. */ val lockscreenToDreamingTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, DREAMING) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index 99025acef70d..abd79ab793d5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -30,9 +30,7 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.animation.Interpolators import com.android.settingslib.Utils -import com.android.systemui.res.R import com.android.systemui.animation.Expandable import com.android.systemui.animation.view.LaunchableImageView import com.android.systemui.common.shared.model.Icon @@ -40,6 +38,7 @@ import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.doOnEnd import kotlinx.coroutines.flow.Flow @@ -48,9 +47,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -/** - * This is only for a SINGLE Quick affordance - */ +/** This is only for a SINGLE Quick affordance */ object KeyguardQuickAffordanceViewBinder { private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L @@ -135,28 +132,12 @@ object KeyguardQuickAffordanceViewBinder { vibratorHelper: VibratorHelper?, ) { if (!viewModel.isVisible) { - view.alpha = 1f - view - .animate() - .alpha(0f) - .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) - .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS) - .withEndAction { view.isInvisible = true } - .start() + view.isInvisible = true return } if (!view.isVisible) { view.isVisible = true - if (viewModel.animateReveal) { - view.alpha = 0f - view - .animate() - .alpha(1f) - .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) - .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS) - .start() - } } IconViewBinder.bind(viewModel.icon, view) @@ -216,13 +197,14 @@ object KeyguardQuickAffordanceViewBinder { view.isClickable = viewModel.isClickable if (viewModel.isClickable) { if (viewModel.useLongPress) { - val onTouchListener = KeyguardQuickAffordanceOnTouchListener( - view, - viewModel, - messageDisplayer, - vibratorHelper, - falsingManager, - ) + val onTouchListener = + KeyguardQuickAffordanceOnTouchListener( + view, + viewModel, + messageDisplayer, + vibratorHelper, + falsingManager, + ) view.setOnTouchListener(onTouchListener) view.setOnClickListener { messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short) @@ -241,9 +223,7 @@ object KeyguardQuickAffordanceViewBinder { KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds shakeAnimator.interpolator = CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles) - shakeAnimator.doOnEnd { - view.translationX = 0f - } + shakeAnimator.doOnEnd { view.translationX = 0f } shakeAnimator.start() vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake) @@ -268,18 +248,18 @@ object KeyguardQuickAffordanceViewBinder { alphaFlow: Flow<Float>, ) { combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha -> - if (isDimmed) DIM_ALPHA else alpha - } + if (isDimmed) DIM_ALPHA else alpha + } .collect { view.alpha = it } } private fun loadFromResources(view: View): ConfigurationBasedDimensions { return ConfigurationBasedDimensions( buttonSizePx = - Size( - view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width), - view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height), - ), + Size( + view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width), + view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height), + ), ) } @@ -337,11 +317,9 @@ object KeyguardQuickAffordanceViewBinder { } override fun onLongClickUseDefaultHapticFeedback(view: View) = false - } private data class ConfigurationBasedDimensions( val buttonSizePx: Size, ) - -}
\ No newline at end of file +} 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 a2e930c49511..59c798bfca1e 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 @@ -84,6 +84,7 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking /** Renders the preview of the lock screen. */ @@ -158,7 +159,6 @@ constructor( init { if (keyguardBottomAreaRefactor()) { - keyguardRootViewModel.enablePreviewMode() quickAffordancesCombinedViewModel.enablePreviewMode( initiallySelectedSlotId = bundle.getString( @@ -287,6 +287,10 @@ constructor( return } + if (smartSpaceView != null) { + parentView.removeView(smartSpaceView) + } + smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView) val topPadding: Int = @@ -334,26 +338,27 @@ constructor( ), ) } - @OptIn(ExperimentalCoroutinesApi::class) private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) { val keyguardRootView = KeyguardRootView(previewContext, null) - disposables.add( - KeyguardRootViewBinder.bind( - keyguardRootView, - keyguardRootViewModel, - configuration, - featureFlags, - occludingAppDeviceEntryMessageViewModel, - chipbarCoordinator, - screenOffAnimationController, - shadeInteractor, - null, // clock provider only needed for burn in - null, // jank monitor not required for preview mode - null, // device entry haptics not required for preview mode - null, // device entry haptics not required for preview mode + if (!keyguardBottomAreaRefactor()) { + disposables.add( + KeyguardRootViewBinder.bind( + keyguardRootView, + keyguardRootViewModel, + configuration, + featureFlags, + occludingAppDeviceEntryMessageViewModel, + chipbarCoordinator, + screenOffAnimationController, + shadeInteractor, + null, // clock provider only needed for burn in + null, // jank monitor not required for preview mode + null, // device entry haptics not required preview mode + null, // device entry haptics not required for preview mode + ) ) - ) + } rootView.addView( keyguardRootView, FrameLayout.LayoutParams( @@ -362,12 +367,13 @@ constructor( ), ) + setUpUdfps(previewContext, rootView) + disposables.add( PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) { if (keyguardBottomAreaRefactor()) { setupShortcuts(keyguardRootView) } - setUpUdfps(previewContext, rootView) if (!shouldHideClock) { setUpClock(previewContext, rootView) @@ -387,30 +393,30 @@ constructor( } private fun setupShortcuts(keyguardRootView: ConstraintLayout) { - keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { + keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView -> shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( - it, - quickAffordancesCombinedViewModel.startButton, - keyguardRootViewModel.alpha, - falsingManager, - vibratorHelper, - ) { - indicationController.showTransientIndication(it) + view = imageView, + viewModel = quickAffordancesCombinedViewModel.startButton, + alpha = flowOf(1f), + falsingManager = falsingManager, + vibratorHelper = vibratorHelper, + ) { message -> + indicationController.showTransientIndication(message) } ) } - keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { + keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { imageView -> shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( - it, - quickAffordancesCombinedViewModel.endButton, - keyguardRootViewModel.alpha, - falsingManager, - vibratorHelper, - ) { - indicationController.showTransientIndication(it) + view = imageView, + viewModel = quickAffordancesCombinedViewModel.endButton, + alpha = flowOf(1f), + falsingManager = falsingManager, + vibratorHelper = vibratorHelper, + ) { message -> + indicationController.showTransientIndication(message) } ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt index 55df46679f6d..cd46d6cf2188 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt @@ -61,7 +61,7 @@ constructor( KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), keyguardQuickAffordancesCombinedViewModel.startButton, - keyguardRootViewModel.alpha, + keyguardQuickAffordancesCombinedViewModel.transitionAlpha, falsingManager, vibratorHelper, ) { @@ -71,7 +71,7 @@ constructor( KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.end_button), keyguardQuickAffordancesCombinedViewModel.endButton, - keyguardRootViewModel.alpha, + keyguardQuickAffordancesCombinedViewModel.transitionAlpha, falsingManager, vibratorHelper, ) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt index 0f6a966aad2e..2a68f26d3ae7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt @@ -25,14 +25,12 @@ import androidx.constraintlayout.widget.ConstraintSet.LEFT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT import com.android.systemui.Flags.keyguardBottomAreaRefactor -import com.android.systemui.res.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.statusbar.KeyguardIndicationController import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject @@ -61,7 +59,7 @@ constructor( KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), keyguardQuickAffordancesCombinedViewModel.startButton, - keyguardRootViewModel.alpha, + keyguardQuickAffordancesCombinedViewModel.transitionAlpha, falsingManager, vibratorHelper, ) { @@ -71,7 +69,7 @@ constructor( KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.end_button), keyguardQuickAffordancesCombinedViewModel.endButton, - keyguardRootViewModel.alpha, + keyguardQuickAffordancesCombinedViewModel.transitionAlpha, falsingManager, vibratorHelper, ) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index 14de01b41867..1864437a7d11 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -55,6 +55,14 @@ constructor( onStep = { 1f }, ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 167.milliseconds, + startTime = 67.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) + val deviceEntryBackgroundViewAlpha: Flow<Float> = deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps -> if (isUdfps) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt index 27fb8a3d2473..a728a2810916 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -41,6 +42,13 @@ constructor( transitionFlow = interactor.dozingToLockscreenTransition, ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 150.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) + override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt new file mode 100644 index 000000000000..58235ae02abe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.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.systemui.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class DreamingHostedToLockscreenTransitionViewModel +@Inject +constructor( + interactor: KeyguardTransitionInteractor, +) { + + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = TO_LOCKSCREEN_DURATION, + transitionFlow = interactor.dreamingLockscreenHostedToLockscreenTransition + ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index a3b8b85fc53d..f943bdfa7550 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -96,6 +96,14 @@ constructor( onStep = { it }, ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + startTime = 233.milliseconds, + duration = 250.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) + val deviceEntryBackgroundViewAlpha = deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps -> if (isUdfps) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt new file mode 100644 index 000000000000..5804a205445c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.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.systemui.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class GoneToLockscreenTransitionViewModel +@Inject +constructor( + interactor: KeyguardTransitionInteractor, +) { + + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = TO_LOCKSCREEN_DURATION, + transitionFlow = interactor.goneToLockscreenTransition + ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 02ea5508f34f..188be244be4a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -23,6 +23,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceIn import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -32,6 +33,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge @OptIn(ExperimentalCoroutinesApi::class) class KeyguardQuickAffordancesCombinedViewModel @@ -39,6 +41,22 @@ class KeyguardQuickAffordancesCombinedViewModel constructor( private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor, private val keyguardInteractor: KeyguardInteractor, + shadeInteractor: ShadeInteractor, + aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, + dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, + dreamingHostedToLockscreenTransitionViewModel: DreamingHostedToLockscreenTransitionViewModel, + dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, + goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel, + occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel, + offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel, + primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, + lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel, + lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel, + lockscreenToDreamingHostedTransitionViewModel: LockscreenToDreamingHostedTransitionViewModel, + lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel, + lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel, + lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel, + lockscreenToPrimaryBouncerTransitionViewModel: LockscreenToPrimaryBouncerTransitionViewModel, ) { data class PreviewMode( @@ -60,6 +78,39 @@ constructor( private val selectedPreviewSlotId = MutableStateFlow(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START) + /** alpha while fading the quick affordances out */ + private val fadeInAlpha: Flow<Float> = + merge( + aodToLockscreenTransitionViewModel.shortcutsAlpha, + dozingToLockscreenTransitionViewModel.shortcutsAlpha, + dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha, + dreamingToLockscreenTransitionViewModel.shortcutsAlpha, + goneToLockscreenTransitionViewModel.shortcutsAlpha, + occludedToLockscreenTransitionViewModel.shortcutsAlpha, + offToLockscreenTransitionViewModel.shortcutsAlpha, + primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha, + ) + + /** alpha while fading the quick affordances in */ + private val fadeOutAlpha: Flow<Float> = + merge( + lockscreenToAodTransitionViewModel.shortcutsAlpha, + lockscreenToDozingTransitionViewModel.shortcutsAlpha, + lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha, + lockscreenToDreamingTransitionViewModel.shortcutsAlpha, + lockscreenToGoneTransitionViewModel.shortcutsAlpha, + lockscreenToOccludedTransitionViewModel.shortcutsAlpha, + lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha, + shadeInteractor.qsExpansion.map { 1 - it }, + ) + + /** The source of truth of alpha for all of the quick affordances on lockscreen */ + val transitionAlpha: Flow<Float> = + merge( + fadeInAlpha, + fadeOutAlpha, + ) + /** * Whether quick affordances are "opaque enough" to be considered visible to and interactive by * the user. If they are not interactive, user input should not be allowed on them. @@ -73,7 +124,7 @@ constructor( * interactive/clickable unless "fully opaque" to avoid issues like in b/241830987. */ private val areQuickAffordancesFullyOpaque: Flow<Boolean> = - keyguardInteractor.keyguardAlpha + transitionAlpha .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD } .distinctUntilChanged() @@ -89,7 +140,7 @@ constructor( * Notifies that a slot with the given ID has been selected in the preview experience that is * rendering in the wallpaper picker. This is ignored for the real lock screen experience. * - * @see [KeyguardRootViewModel.enablePreviewMode] + * @see [enablePreviewMode] */ fun onPreviewSlotSelected(slotId: String) { selectedPreviewSlotId.value = slotId diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 524fa1ede90a..f63afebb60ab 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -47,13 +47,11 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart @@ -74,16 +72,6 @@ constructor( private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, screenOffAnimationController: ScreenOffAnimationController, ) { - - data class PreviewMode(val isInPreviewMode: Boolean = false) - - /** - * Whether this view-model instance is powering the preview experience that renders exclusively - * in the wallpaper picker application. This should _always_ be `false` for the real lock screen - * experience. - */ - private val previewMode = MutableStateFlow(PreviewMode()) - var clockControllerProvider: Provider<ClockController>? = null /** System insets that keyguard needs to stay out of */ @@ -103,14 +91,7 @@ constructor( keyguardInteractor.notificationContainerBounds /** An observable for the alpha level for the entire keyguard root view. */ - val alpha: Flow<Float> = - previewMode.flatMapLatest { - if (it.isInPreviewMode) { - flowOf(1f) - } else { - keyguardInteractor.keyguardAlpha.distinctUntilChanged() - } - } + val alpha: Flow<Float> = keyguardInteractor.keyguardAlpha.distinctUntilChanged() private fun burnIn(): Flow<BurnInModel> { val dozingAmount: Flow<Float> = @@ -147,55 +128,29 @@ constructor( val lockscreenStateAlpha: Flow<Float> = aodToLockscreenTransitionViewModel.lockscreenAlpha /** For elements that appear and move during the animation -> AOD */ - val burnInLayerAlpha: Flow<Float> = - previewMode.flatMapLatest { - if (it.isInPreviewMode) { - flowOf(1f) - } else { - goneToAodTransitionViewModel.enterFromTopAnimationAlpha - } - } + val burnInLayerAlpha: Flow<Float> = goneToAodTransitionViewModel.enterFromTopAnimationAlpha val translationY: Flow<Float> = - previewMode.flatMapLatest { - if (it.isInPreviewMode) { - flowOf(0f) - } else { - keyguardInteractor.configurationChange.flatMapLatest { _ -> - val enterFromTopAmount = - context.resources.getDimensionPixelSize( - R.dimen.keyguard_enter_from_top_translation_y - ) - combine( - keyguardInteractor.keyguardTranslationY.onStart { emit(0f) }, - burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) }, - goneToAodTransitionViewModel - .enterFromTopTranslationY(enterFromTopAmount) - .onStart { emit(0f) }, - ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY -> - // All 3 values need to be combined for a smooth translation - keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY - } - } + keyguardInteractor.configurationChange.flatMapLatest { _ -> + val enterFromTopAmount = + context.resources.getDimensionPixelSize( + R.dimen.keyguard_enter_from_top_translation_y + ) + combine( + keyguardInteractor.keyguardTranslationY.onStart { emit(0f) }, + burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) }, + goneToAodTransitionViewModel.enterFromTopTranslationY(enterFromTopAmount).onStart { + emit(0f) + }, + ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY -> + // All 3 values need to be combined for a smooth translation + keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY } } - val translationX: Flow<Float> = - previewMode.flatMapLatest { - if (it.isInPreviewMode) { - flowOf(0f) - } else { - burnIn().map { it.translationX.toFloat() } - } - } + val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() } - val scale: Flow<Pair<Float, Boolean>> = - previewMode.flatMapLatest { previewMode -> - burnIn().map { - val scale = if (previewMode.isInPreviewMode) 1f else it.scale - Pair(scale, it.scaleClockOnly) - } - } + val scale: Flow<Pair<Float, Boolean>> = burnIn().map { Pair(it.scale, it.scaleClockOnly) } /** Is the notification icon container visible? */ val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> = @@ -238,20 +193,7 @@ constructor( } .distinctUntilChanged() - /** - * Puts this view-model in "preview mode", which means it's being used for UI that is rendering - * the lock screen preview in wallpaper picker / settings and not the real experience on the - * lock screen. - */ - fun enablePreviewMode() { - previewMode.value = PreviewMode(true) - } - fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) { - // Notifications should not be visible in preview mode - if (previewMode.value.isInPreviewMode) { - return - } keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom)) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt index 2bf12e8e33b2..8e8fd75cc1c0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt @@ -57,6 +57,15 @@ constructor( onFinish = { 0f }, ), ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) + override val deviceEntryParentViewAlpha: Flow<Float> = deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { isUdfpsEnrolledAndEnabled -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt new file mode 100644 index 000000000000..263ed11503ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt @@ -0,0 +1,47 @@ +/* + * 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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class LockscreenToDozingTransitionViewModel +@Inject +constructor( + interactor: KeyguardTransitionInteractor, +) { + + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = TO_DOZING_DURATION, + transitionFlow = interactor.lockscreenToDozingTransition + ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt new file mode 100644 index 000000000000..17015056bda0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt @@ -0,0 +1,47 @@ +/* + * 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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class LockscreenToDreamingHostedTransitionViewModel +@Inject +constructor( + interactor: KeyguardTransitionInteractor, +) { + + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = TO_DREAMING_HOSTED_DURATION, + transitionFlow = interactor.lockscreenToDreamingLockscreenHostedTransition + ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt index 52296137a3d6..401c0ff76c29 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -62,6 +62,14 @@ constructor( onStep = { 1f - it }, ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) + override val deviceEntryParentViewAlpha: Flow<Float> = shadeDependentFlows.transitionFlow( flowWhenShadeIsNotExpanded = lockscreenAlpha, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt index 59e5aa845051..cfb4bf59c8a8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt @@ -23,6 +23,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -43,6 +44,14 @@ constructor( transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) + override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt index d49bc4994b0f..a6136f95d0f6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -50,6 +50,14 @@ constructor( onStep = { 1f - it }, ) + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { 1 - it }, + onFinish = { 0f }, + onCancel = { 1f }, + ) + /** Lockscreen views y-translation */ fun lockscreenTranslationY(translatePx: Int): Flow<Float> { return transitionAnimation.createFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index f04b67a1d4d4..07dd4ef49c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -26,6 +26,7 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to @@ -46,6 +47,11 @@ constructor( interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER), ) + val shortcutsAlpha: Flow<Float> = + interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map { + 1 - it.value + } + override val deviceEntryParentViewAlpha: Flow<Float> = shadeDependentFlows.transitionFlow( flowWhenShadeIsNotExpanded = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index 0bdc85d05106..58be0934beca 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -58,6 +58,13 @@ constructor( ) } + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) + /** Lockscreen views alpha */ val lockscreenAlpha: Flow<Float> = transitionAnimation.createFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt new file mode 100644 index 000000000000..c3bc799435a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt @@ -0,0 +1,45 @@ +/* + * 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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class OffToLockscreenTransitionViewModel +@Inject +constructor( + interactor: KeyguardTransitionInteractor, +) { + + private val transitionAnimation = + KeyguardTransitionAnimationFlow( + transitionDuration = 250.milliseconds, + transitionFlow = interactor.offToLockscreenTransition + ) + + val shortcutsAlpha: Flow<Float> = + transitionAnimation.createFlow( + duration = 250.milliseconds, + onStep = { it }, + onCancel = { 0f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt index 3cf793ab9dc8..7ef8374023fb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map /** * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to @@ -57,6 +58,11 @@ constructor( } } + val shortcutsAlpha: Flow<Float> = + interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map { + it.value + } + override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 11db69b69f13..6c930b1f3d17 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 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr 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/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 69fe46aa9009..529d68407ce9 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 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy // 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 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy } 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/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt index c390695b1911..cfb544226c83 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 com.android.systemui.res.R 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/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt index b2b226464ee5..881a6bd156d2 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 com.android.systemui.res.R 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 8e53723a5a6b..7e7034d65efd 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 com.android.systemui.res.R 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 f9e0b160acd6..23e0cb66bb6a 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 @@ data class QSTileState( 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 1c5330ecd24f..d42fde617394 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 @@ constructor( 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), diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index e7481ccd0efd..b98093e50920 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.Handler; 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 @@ public class ScreenRecordDialog extends SystemUIDialog { 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 9f4ea27b9ee6..d13edf01cc4a 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.MetricsLogger; 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 @@ public class BrightnessDialog extends Activity { 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 @@ public class BrightnessDialog extends Activity { 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/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index 1b096b592a4a..c1a630f48232 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 @@ public final class KeyboardShortcutListSearch { 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 @@ public final class KeyboardShortcutListSearch { 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 @@ public final class KeyboardShortcutListSearch { 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/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 6d8ec44ad55e..c615887d5c25 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 @@ public final class StatusBarTouchableRegionManager implements Dumpable { 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 @@ public final class StatusBarTouchableRegionManager implements Dumpable { }); 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 @@ public final class StatusBarTouchableRegionManager implements Dumpable { } } + 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 @@ public final class StatusBarTouchableRegionManager implements Dumpable { // 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 bbba19d61b5a..87df180353b1 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 @@ public interface DevicePostureController extends CallbackController<Callback> { /** 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 8f1ac812da71..422aa4d4aa60 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 @@ import javax.inject.Inject; /** 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 @@ public class DevicePostureControllerImpl implements DevicePostureController { 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(); + } + + @Override + public void onBaseStateChanged(int state) { + Assert.isMainThread(); + mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN); + + if (useBaseState()) { + sendUpdatePosture(); + } + } - ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl", + private void sendUpdatePosture() { + ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl", l -> { - l.onPostureChanged(mCurrentDevicePosture); + l.onPostureChanged(getDevicePosture()); return Unit.INSTANCE; }); + } }); } @@ -100,6 +119,14 @@ public class DevicePostureControllerImpl implements DevicePostureController { @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 e576f36d573a..279e5ef1f38c 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 @@ package com.android.systemui.statusbar.policy; 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 @@ public interface FlashlightController extends CallbackController<FlashlightListe 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 053148709e69..968981197b83 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.content.Context 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 @@ class UnfoldTransitionModule { @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 d8799e16ebdb..43952824f9a7 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 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { } @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/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 67c4e2688cd0..0c30d10ea563 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -51,6 +51,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences @@ -58,8 +59,10 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth -import kotlin.math.max import kotlin.math.min +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope @@ -73,6 +76,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @@ -85,6 +89,47 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger + @Mock private lateinit var shadeInteractor: ShadeInteractor + @Mock + private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel + @Mock + private lateinit var dozingToLockscreenTransitionViewModel: + DozingToLockscreenTransitionViewModel + @Mock + private lateinit var dreamingHostedToLockscreenTransitionViewModel: + DreamingHostedToLockscreenTransitionViewModel + @Mock + private lateinit var dreamingToLockscreenTransitionViewModel: + DreamingToLockscreenTransitionViewModel + @Mock + private lateinit var goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel + @Mock + private lateinit var occludedToLockscreenTransitionViewModel: + OccludedToLockscreenTransitionViewModel + @Mock + private lateinit var offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel + @Mock + private lateinit var primaryBouncerToLockscreenTransitionViewModel: + PrimaryBouncerToLockscreenTransitionViewModel + @Mock + private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel + @Mock + private lateinit var lockscreenToDozingTransitionViewModel: + LockscreenToDozingTransitionViewModel + @Mock + private lateinit var lockscreenToDreamingHostedTransitionViewModel: + LockscreenToDreamingHostedTransitionViewModel + @Mock + private lateinit var lockscreenToDreamingTransitionViewModel: + LockscreenToDreamingTransitionViewModel + @Mock + private lateinit var lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel + @Mock + private lateinit var lockscreenToOccludedTransitionViewModel: + LockscreenToOccludedTransitionViewModel + @Mock + private lateinit var lockscreenToPrimaryBouncerTransitionViewModel: + LockscreenToPrimaryBouncerTransitionViewModel private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel @@ -97,6 +142,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var keyguardInteractor: KeyguardInteractor + private val intendedAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(1f) + // the viewModel does a `map { 1 - it }` on this value, which is why it's different + private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f) + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -191,6 +240,31 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { userHandle = UserHandle.SYSTEM, ) + intendedAlphaMutableStateFlow.value = 1f + intendedShadeAlphaMutableStateFlow.value = 0f + whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha) + .thenReturn(intendedAlphaMutableStateFlow) + whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha) + .thenReturn(emptyFlow()) + whenever(dreamingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(goneToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(occludedToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(offToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha) + .thenReturn(emptyFlow()) + whenever(lockscreenToAodTransitionViewModel.shortcutsAlpha) + .thenReturn(intendedAlphaMutableStateFlow) + whenever(lockscreenToDozingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha) + .thenReturn(emptyFlow()) + whenever(lockscreenToDreamingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(lockscreenToGoneTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) + whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha) + .thenReturn(emptyFlow()) + whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow) + underTest = KeyguardQuickAffordancesCombinedViewModel( quickAffordanceInteractor = @@ -210,7 +284,27 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { backgroundDispatcher = testDispatcher, appContext = mContext, ), - keyguardInteractor = keyguardInteractor + keyguardInteractor = keyguardInteractor, + shadeInteractor = shadeInteractor, + aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, + dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, + dreamingHostedToLockscreenTransitionViewModel = + dreamingHostedToLockscreenTransitionViewModel, + dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, + goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, + occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel, + offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel, + primaryBouncerToLockscreenTransitionViewModel = + primaryBouncerToLockscreenTransitionViewModel, + lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel, + lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel, + lockscreenToDreamingHostedTransitionViewModel = + lockscreenToDreamingHostedTransitionViewModel, + lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel, + lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel, + lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel, + lockscreenToPrimaryBouncerTransitionViewModel = + lockscreenToPrimaryBouncerTransitionViewModel ) } @@ -526,15 +620,15 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Test fun isClickable_falseWhenAlphaBelowThreshold() = testScope.runTest { + intendedAlphaMutableStateFlow.value = + KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - + .1f + // the viewModel does a `map { 1 - it }` on this value, which is why it's different + intendedShadeAlphaMutableStateFlow.value = + KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + + .1f repository.setKeyguardShowing(true) val latest = collectLastValue(underTest.startButton) - repository.setKeyguardAlpha( - max( - 0f, - KeyguardQuickAffordancesCombinedViewModel - .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - 0.1f - ), - ) val testConfig = TestConfig( @@ -561,9 +655,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Test fun isClickable_falseWhenAlphaAtZero() = testScope.runTest { + intendedAlphaMutableStateFlow.value = 0f + intendedShadeAlphaMutableStateFlow.value = 1f repository.setKeyguardShowing(true) val latest = collectLastValue(underTest.startButton) - repository.setKeyguardAlpha(0f) val testConfig = TestConfig( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index e6d6cf263d69..a57feda64723 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -163,23 +163,6 @@ class KeyguardRootViewModelTest : SysuiTestCase() { } @Test - fun alpha_inPreviewMode_doesNotChange() = - testScope.runTest { - val value = collectLastValue(underTest.alpha) - underTest.enablePreviewMode() - - assertThat(value()).isEqualTo(1f) - repository.setKeyguardAlpha(0.1f) - assertThat(value()).isEqualTo(1f) - repository.setKeyguardAlpha(0.5f) - assertThat(value()).isEqualTo(1f) - repository.setKeyguardAlpha(0.2f) - assertThat(value()).isEqualTo(1f) - repository.setKeyguardAlpha(0f) - assertThat(value()).isEqualTo(1f) - } - - @Test fun translationAndScaleFromBurnInNotDozing() = testScope.runTest { val translationX by collectLastValue(underTest.translationX) 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 6e487cdd65b5..88c728fd1b66 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 androidx.test.rule.ActivityTestRule 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 @@ class BrightnessDialogTest : SysuiTestCase() { @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 @@ class BrightnessDialogTest : SysuiTestCase() { brightnessSliderControllerFactory, brightnessControllerFactory, mainExecutor, - accessibilityMgr + accessibilityMgr, + shadeInteractor ) }, /* initialTouchMode= */ false, @@ -82,6 +89,8 @@ class BrightnessDialogTest : SysuiTestCase() { .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 @@ class BrightnessDialogTest : SysuiTestCase() { 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 000000000000..ce471705ed85 --- /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 000000000000..4e61b89b9c3e --- /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/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt index 42d31b38ff76..f7fb01465a40 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 @@ package com.android.systemui.unfold 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.ScaleAwareTransitionProgressProvider 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 @@ class UnfoldSharedModule { 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 @@ internal class UnfoldSharedInternalModule { 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/shared/src/com/android/credentialmanager/model/Password.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt index 2fe4fd5a12e4..0e371fa6bfb7 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt @@ -5,7 +5,7 @@ * 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 + * 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, @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.credentialmanager.model +package com.android.systemui.unfold.dagger -import android.credentials.ui.Entry -import androidx.credentials.provider.PasswordCredentialEntry +import javax.inject.Qualifier -data class Password( - val providerId: String, - val entry: Entry, - val passwordCredentialEntry: PasswordCredentialEntry, -) +/** + * 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 000000000000..9bdf3d5d5307 --- /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 c70c171048e7..79bfa443b330 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 74538ac02289..6cac6a47c77b 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 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo 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 b5e8c849517b..2eecb4d3a86c 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.internal.util.Preconditions; 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub && (userState.getMagnificationCapabilitiesLocked() != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) || userHasMagnificationServicesLocked(userState); - getWindowMagnificationMgr().requestConnection(connect); + getMagnificationConnectionManager().requestConnection(connect); } /** @@ -4122,17 +4122,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @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/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index 3ea805bbb4a6..5a3c070819bd 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.lang.annotation.RetentionPolicy; 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 @@ public class WindowMagnificationManager implements 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 @@ public class WindowMagnificationManager implements 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 @@ public class WindowMagnificationManager implements "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 @@ public class WindowMagnificationManager implements } 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 @@ public class WindowMagnificationManager implements } } + // 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 @@ public class WindowMagnificationManager implements 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 @@ public class WindowMagnificationManager implements } 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 @@ public class WindowMagnificationManager implements } void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) { - if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId) - && mWindowMagnificationManager.isImeVisible(mDisplayId) + if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId) + && mMagnificationConnectionManager.isImeVisible(mDisplayId) && trackingTypingFocusEnabled) { startTrackingTypingFocusRecord(); } @@ -1206,7 +1218,7 @@ public class WindowMagnificationManager implements 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 @@ public class WindowMagnificationManager implements 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 @@ public class WindowMagnificationManager implements 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 f0c44d64f5ec..20538f167656 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 @@ class MagnificationConnectionWrapper { 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 effd8732d086..52e123a5e70c 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 @@ import java.util.concurrent.Executor; * <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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb @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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb } 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { screenMagnificationController.reset(displayId, animationEndCallback); } else { - windowMagnificationMgr.disableWindowMagnification(displayId, false, + magnificationConnectionManager.disableWindowMagnification(displayId, false, animationEndCallback); } } @@ -340,7 +341,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb } 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { return getFullScreenMagnificationController().getScale(displayId); } else { - return getWindowMagnificationMgr().getScale(displayId); + return getMagnificationConnectionManager().getScale(displayId); } } @@ -441,7 +444,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb @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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb @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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb } 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { mIsImeVisibleArray.put(displayId, shown); } - getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown); + getMagnificationConnectionManager().onImeWindowVisibilityChanged(displayId, shown); logMagnificationModeWithImeOnIfNeeded(displayId); } @@ -661,7 +666,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb /** * Updates the active user ID of {@link FullScreenMagnificationController} and {@link - * WindowMagnificationManager}. + * MagnificationConnectionManager}. * * @param userId the currently active user ID */ @@ -671,10 +676,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb } 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb if (fullMagnificationController != null) { fullMagnificationController.resetAllIfNeeded(false); } - if (windowMagnificationManager != null) { - windowMagnificationManager.disableAllWindowMagnifiers(); + if (magnificationConnectionManager != null) { + magnificationConnectionManager.disableAllWindowMagnifiers(); } } @@ -700,8 +705,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb * @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 @@ public class MagnificationController implements WindowMagnificationManager.Callb } /** - * 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } 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 @@ public class MagnificationController implements WindowMagnificationManager.Callb 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 5cf2a638fa3e..ed8f1ab3a1b2 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 @@ public class MagnificationProcessor { .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 @@ public class MagnificationProcessor { } } 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 @@ public class MagnificationProcessor { 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 @@ public class MagnificationProcessor { 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 class MagnificationProcessor { */ 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 @@ public class MagnificationProcessor { 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 94556282d0d3..6b48d2bacf9d 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 @@ class PanningScalingHandler extends 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 36e751181cfd..73c267a6e262 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private long mTripleTapAndHoldStartedTime = 0; public WindowMagnificationGestureHandler(@UiContext Context context, - WindowMagnificationManager windowMagnificationMgr, + MagnificationConnectionManager magnificationConnectionManager, AccessibilityTraceManager trace, Callback callback, boolean detectSingleFingerTripleTap, @@ -115,7 +115,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl "WindowMagnificationGestureHandler() , displayId = " + displayId + ")"); } mContext = context; - mWindowMagnificationMgr = windowMagnificationMgr; + mMagnificationConnectionManager = magnificationConnectionManager; mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context, (event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent, policyFlags)); @@ -128,18 +128,18 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl Slog.i(mLogTag, "onDestroy(); delayed = " + mDetectingState.toString()); } - mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); + mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true); resetToDetectState(); } @@ -176,7 +176,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl } 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl Slog.i(mLogTag, "onTripleTap()"); } toggleMagnification(up.getX(), up.getY(), - WindowMagnificationManager.WINDOW_POSITION_AT_CENTER); + MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER); } @VisibleForTesting @@ -232,9 +233,9 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @VisibleForTesting void releaseTripleTapAndHold() { if (!mViewportDraggingState.mEnabledBeforeDrag) { - mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); + mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true); } transitionTo(mDetectingState); if (mTripleTapAndHoldStartedTime != 0) { @@ -331,7 +332,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @Override public void onExit() { mPanningScalingHandler.setEnabled(false); - mWindowMagnificationMgr.persistScale(mDisplayId); + mMagnificationConnectionManager.persistScale(mDisplayId); clear(); } @@ -404,7 +405,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl 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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @Override public boolean shouldStopDetection(MotionEvent motionEvent) { - return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId) + return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId) && !mDetectSingleFingerTripleTap && !(mDetectTwoFingerTripleTap && Flags.enableMagnificationMultipleFingerMultipleTapGesture()); @@ -540,7 +541,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl "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 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl + ", 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/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java index 5665ad53924e..d089b05238e4 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.Binder; 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 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { 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(); @@ -71,8 +74,12 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { 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. @@ -89,17 +96,22 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { * @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()); - } - synchronized (mCameras) { - mCameras.remove(cameraConfig); + connectVirtualCameraServiceIfNeeded(); + + try { + synchronized (mServiceLock) { + mVirtualCameraService.unregisterCamera(binder); + } + mCameras.remove(binder); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } - } catch (RemoteException e) { - e.rethrowFromSystemServer(); } } @@ -108,7 +120,9 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { connectVirtualCameraServiceIfNeeded(); try { - return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder()); + synchronized (mServiceLock) { + return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -117,7 +131,9 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { @Override public void binderDied() { Slog.d(TAG, "Virtual camera service died."); - mVirtualCameraService = null; + synchronized (mServiceLock) { + mVirtualCameraService = null; + } synchronized (mCameras) { mCameras.clear(); } @@ -126,44 +142,49 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { /** 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() { - // 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."); + 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."); + } } } @@ -188,7 +209,24 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { 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/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 7fe06827f1b3..c258370dc12b 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 000000000000..98039be20897 --- /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/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 003046ab884f..60f087f53a7e 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.FileWriter; 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 @@ public class Watchdog implements Dumpable { // 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 @@ public class Watchdog implements Dumpable { } } - /** - * 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 @@ public class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { } 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 @@ public class Watchdog implements Dumpable { * 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 class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName); } } + + @Override + public String toString() { + return "CheckerHandler for " + mName; + } } final class RebootRequestReceiver extends BroadcastReceiver { @@ -445,31 +467,40 @@ public class Watchdog implements Dumpable { 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 class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { 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 @@ public class Watchdog implements Dumpable { } 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 @@ public class Watchdog implements Dumpable { // 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 @@ public class Watchdog implements Dumpable { } } - 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 @@ public class Watchdog implements Dumpable { } 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 @@ public class Watchdog implements Dumpable { 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 54c8ed38bb1c..b87d02d86c22 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_ACTIVITY_VISIBILITY_GRACE_ 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub || 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 e65603021708..1928780fa090 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 3f27ca024f15..5d47e35a8729 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -10667,8 +10667,8 @@ public class AudioService extends IAudioService.Stub /** @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) */ @@ -11372,6 +11372,8 @@ public class AudioService extends IAudioService.Stub 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"); @@ -11571,6 +11573,10 @@ public class AudioService extends IAudioService.Stub 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 f69b9f6523cc..de8901179028 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 @@ public class AudioServiceEvents { } } + 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 3c67e9dd116b..bbe819f22e3a 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_HEADPHONES; 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.SparseArray; 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.HashSet; 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { } 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { 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 @@ public class LoudnessCodecHelper { } } - 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 @@ public class LoudnessCodecHelper { } } - updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc, null)); + updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc)); + } + + /** 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(); } - /** Updates and dispatches the new loudness parameters for the {@code codecInfos} set. + 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/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index debf828abf0a..d848f4b6cce5 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 @@ public abstract class BrightnessMappingStrategy { // Display independent, mode dependent values float[] brightnessLevelsNits; + float[] brightnessLevels = null; float[] luxLevels; if (isForIdleMode) { brightnessLevelsNits = getFloatArray(resources.obtainTypedArray( @@ -130,11 +131,21 @@ public abstract class BrightnessMappingStrategy { } 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 @@ public abstract class BrightnessMappingStrategy { 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 @@ public abstract class BrightnessMappingStrategy { 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 @@ public abstract class BrightnessMappingStrategy { 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 70d4ad2c6e1f..c26118eac7a2 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.content.Context; 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 @@ abstract class DisplayAdapter { 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 @@ abstract class DisplayAdapter { 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 @@ abstract class DisplayAdapter { 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 2fdf90d7d286..3b05b47eb542 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 @@ abstract class DisplayDevice { } 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 e6aa8258041a..d97127c91fbf 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.HdrBrightnessData; 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.ThermalThrottling; 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 @@ import javax.xml.datatype.DatatypeConfigurationException; * <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> @@ -630,7 +634,6 @@ public class DisplayDeviceConfig { // 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 @@ -640,20 +643,25 @@ public class DisplayDeviceConfig { 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; @@ -849,9 +857,12 @@ public class DisplayDeviceConfig { */ private float mBrightnessCapForWearBedtimeMode; + private final DisplayManagerFlags mFlags; + @VisibleForTesting - DisplayDeviceConfig(Context context) { + DisplayDeviceConfig(Context context, DisplayManagerFlags flags) { mContext = context; + mFlags = flags; } /** @@ -867,9 +878,9 @@ public class DisplayDeviceConfig { * @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; @@ -884,28 +895,29 @@ public class DisplayDeviceConfig { * 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; } @@ -913,7 +925,7 @@ public class DisplayDeviceConfig { // 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) { @@ -966,18 +978,19 @@ public class DisplayDeviceConfig { } 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; } @@ -986,7 +999,7 @@ public class DisplayDeviceConfig { 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; } @@ -1605,6 +1618,13 @@ public class DisplayDeviceConfig { } /** + * @return Auto brightness brightening levels + */ + public float[] getAutoBrightnessBrighteningLevels() { + return mBrightnessLevels; + } + + /** * @return Default peak refresh rate of the associated display */ public int getDefaultPeakRefreshRate() { @@ -1857,6 +1877,7 @@ public class DisplayDeviceConfig { + mAutoBrightnessDarkeningLightDebounceIdle + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux) + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits) + + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels) + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable + "\n" @@ -1890,27 +1911,29 @@ public class DisplayDeviceConfig { } 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; } @@ -2615,8 +2638,23 @@ public class DisplayDeviceConfig { * 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 @@ -2624,18 +2662,6 @@ public class DisplayDeviceConfig { 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(); - } } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6a7c17d54980..2ab15e639d68 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 @@ public final class DisplayManagerService extends SystemService { // 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 @@ public final class DisplayManagerService extends SystemService { private void registerOverlayDisplayAdapterLocked() { registerDisplayAdapterLocked(new OverlayDisplayAdapter( - mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler)); + mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler, mFlags)); } private void registerWifiDisplayAdapterLocked() { @@ -1880,7 +1880,7 @@ public final class DisplayManagerService extends SystemService { || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) { mWifiDisplayAdapter = new WifiDisplayAdapter( mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, - mPersistentDataStore); + mPersistentDataStore, mFlags); registerDisplayAdapterLocked(mWifiDisplayAdapter); } } @@ -3288,8 +3288,10 @@ public final class DisplayManagerService extends SystemService { @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 ff9a1ab61b13..22898a65c5de 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final boolean mIsBootDisplayModeSupported; - private final DisplayManagerFlags mFlags; - private final DisplayNotificationManager mDisplayNotificationManager; private Context mOverlayContext; @@ -103,12 +101,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { // 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { + ", 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 @@ final class LocalDisplayAdapter extends DisplayAdapter { } 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 2ce7690ecc3f..22ff2d0eeffd 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 android.view.SurfaceControl; 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 @@ final class OverlayDisplayAdapter extends DisplayAdapter { // 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 edbd42465534..ec5ad7de11b3 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.Surface; 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 @@ public class VirtualDisplayAdapter extends DisplayAdapter { // 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 class VirtualDisplayAdapter extends DisplayAdapter { 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 7660cf8a1c3a..aa98cd85d38e 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 android.view.SurfaceControl; 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 @@ final class WifiDisplayAdapter extends DisplayAdapter { // 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/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index eb8798406152..c71f0cf2dd91 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -94,6 +94,10 @@ public class DisplayManagerFlags { 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(); @@ -187,6 +191,13 @@ public class DisplayManagerFlags { } /** + * @return Whether generic auto-brightness modes are enabled + */ + public boolean areAutoBrightnessModesEnabled() { + return mAutoBrightnessModesFlagState.isEnabled(); + } + + /** * dumps all flagstates * @param pw printWriter */ @@ -206,6 +217,7 @@ public class DisplayManagerFlags { 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 9dab76152dad..9dfa1ee41ad6 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 @@ -128,3 +128,11 @@ flag { 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 8eb03ec0d3bd..8fa838de3327 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.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT 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 @@ public class DisplayModeDirector { } @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 @@ public class DisplayModeDirector { 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 @@ public class DisplayModeDirector { } } - 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 @@ public class DisplayModeDirector { 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 @@ public class DisplayModeDirector { } } - @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 @@ public class DisplayModeDirector { 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 6f9b7d6328c7..27b01a5424b8 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.LocationListener; 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } 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 8cb334dc2260..000000000000 --- 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 8bc69c226d1a..a00999d08b5b 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.BroadcastReceiver; 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 @@ import java.util.Set; 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 @@ import java.util.Set; 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 @@ import java.util.Set; @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 @@ import java.util.Set; 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; - } - } - - /** - * 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); - } - } - } + @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; } - @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 @@ import java.util.Set; } } - @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 @@ import java.util.Set; } 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 @@ import java.util.Set; 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 @@ import java.util.Set; @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 6bdfae2dc02f..27df00f30531 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 = SystemMediaRoute2Provider.TAG; - private static final String TAG = "APDeviceRoutesController"; - - @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<>(); + + @NonNull private final AudioProductStrategy mStrategyForMedia; - @VisibleForTesting - /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context, + @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 = 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(); + } - mContext = context; - mOnDeviceRouteChangedListener = onDeviceRouteChangedListener; + @RequiresPermission( + anyOf = { + Manifest.permission.MODIFY_AUDIO_ROUTING, + Manifest.permission.QUERY_AUDIO_STATE + }) + @Override + public void start(UserHandle mUser) { + mBluetoothRouteController.start(mUser); + mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler); + mAudioManager.addOnDevicesForAttributesChangedListener( + AudioRoutingUtils.ATTRIBUTES_MEDIA, + new HandlerExecutor(mHandler), + mOnDevicesForAttributesChangedListener); + } - mAudioManager = audioManager; - mAudioService = audioService; + @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); + } - 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); - } + @Override + @NonNull + public synchronized MediaRoute2Info getSelectedRoute() { + return mSelectedRoute; + } - mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes); + @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 boolean selectRoute(@Nullable Integer type) { - if (type == null) { - mSelectedRoute = null; - return true; + 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; } - - if (!isDeviceRouteType(type)) { - return false; + 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); } + } - mSelectedRoute = createRouteFromAudioInfo(type); + @RequiresPermission( + anyOf = { + Manifest.permission.MODIFY_AUDIO_ROUTING, + Manifest.permission.QUERY_AUDIO_STATE + }) + @Override + public synchronized boolean updateVolume(int volume) { + // 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; } - @Override - @NonNull - public synchronized MediaRoute2Info getSelectedRoute() { - if (mSelectedRoute != null) { - return mSelectedRoute; + @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(); } - return mDeviceRoute; } - @Override - public synchronized boolean updateVolume(int volume) { - if (mDeviceVolume == volume) { - return false; + @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(); } - mDeviceVolume = volume; - - if (mSelectedRoute != null) { - mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute) - .setVolume(volume) - .build(); + 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; + } + } } - mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) - .setVolume(volume) - .build(); + 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); + } - return true; + 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(); } - @NonNull - private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { - int type = TYPE_BUILTIN_SPEAKER; - - 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; - } - } + private MediaRoute2InfoHolder createPlaceholderBuiltinSpeakerRoute() { + int type = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; + return MediaRoute2InfoHolder.createForAudioManagerRoute( + createMediaRoute2Info( + /* routeId= */ null, type, /* productName= */ null, /* address= */ null), + type); + } - return createRouteFromAudioInfo(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); } - @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; - } - - 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(); + /** + * 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(); } /** - * 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 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 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 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); } } - private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { + /** + * Holds route information about an {@link AudioDeviceInfo#getType() audio device info type}. + */ + 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 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 000000000000..13f11eb80ece --- /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 2b01001fd7d1..74fdf6ee1d7f 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 @@ import java.util.Objects; @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 @@ import java.util.Objects; */ 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 @@ import java.util.Objects; } @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 0fdaaa7604e5..9f175a9a0277 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,44 +45,65 @@ import com.android.media.flags.Flags; */ /* 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); } } + /** Returns the currently selected device (built-in or wired) route. */ + @NonNull + MediaRoute2Info getSelectedRoute(); + /** - * 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)}. + * Returns all available routes. * - * @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. + * <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}. */ - boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type); + List<MediaRoute2Info> getAvailableRoutes(); - /** Returns the currently selected device (built-in or wired) route. */ - @NonNull - MediaRoute2Info getSelectedRoute(); + /** + * 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. @@ -85,6 +114,18 @@ import com.android.media.flags.Flags; 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 ba3cecf7c091..041fceaf8d3d 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 @@ class LegacyBluetoothRouteController implements BluetoothRouteController { 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 65874e23dcdc..c0f28346705c 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.IAudioRoutesObserver; 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 @@ import java.util.Objects; private int mDeviceVolume; private MediaRoute2Info mDeviceRoute; - @VisibleForTesting /* package */ LegacyDeviceRouteController(@NonNull Context context, @NonNull AudioManager audioManager, @NonNull IAudioService audioService, @@ -100,9 +101,13 @@ import java.util.Objects; } @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 @@ import java.util.Objects; } @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 c8dba800a017..86d78334d546 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 @@ import java.util.Set; */ // 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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,33 +263,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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()); } - 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); + if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) { + for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) { + if (!TextUtils.equals(selectedDeviceRoute.getId(), route.getId())) { + builder.addTransferableRoute(route.getId()); + } + } + } + return builder.setProviderId(mUniqueId).build(); } } @@ -314,7 +286,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { 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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .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 3b3d79e7dee1..07e0ddfac76b 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 class DeletePackageHelper { 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 0a81b2b9fabb..5494bd9808c8 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 @@ final class InstallRequest { } @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 d0fdfa9bc775..9384c13e583b 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 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable // 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 73c422490330..e817df8c6e0c 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 e3f36385ef6c..5d9085144334 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.provider.Settings; 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.GuardedBy; 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 @@ public class BatterySaverPolicy extends ContentObserver implements 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 @@ public class BatterySaverPolicy extends ContentObserver implements // 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 dd39fb02573e..c17d6ab4c85b 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.FileDescriptor; 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 @@ public final class HintManagerService extends SystemService { 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 0656a6a7d3e9..59766ec7a175 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.Intent.FLAG_ACTIVITY_NO_USER_ACTION; 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 @@ public final class SensorPrivacyService extends SystemService { private final AppOpsManagerInternal mAppOpsManagerInternal; private final TelephonyManager mTelephonyManager; private final PackageManagerInternal mPackageManagerInternal; + private final NotificationManager mNotificationManager; private CameraPrivacyLightController mCameraPrivacyLightController; @@ -188,6 +190,7 @@ public final class SensorPrivacyService extends SystemService { 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 @@ public final class SensorPrivacyService extends SystemService { @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 @@ public final class SensorPrivacyService extends SystemService { 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 @@ public final class SensorPrivacyService extends SystemService { channel.enableVibration(false); channel.setBlockable(false); - notificationManager.createNotificationChannel(channel); + mNotificationManager.createNotificationChannel(channel); Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes); @@ -669,10 +679,11 @@ public final class SensorPrivacyService extends SystemService { 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 e6273d331fce..0467d0cd351d 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.BroadcastInfoRequest; 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 @@ public class TvInteractiveAppManagerService extends SystemService { Slogf.d(TAG, "onStart"); } publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService()); + publishBinderService(Context.TV_AD_SERVICE, new TvAdBinderService()); } @Override @@ -688,6 +690,12 @@ public class TvInteractiveAppManagerService extends SystemService { } 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 000000000000..e5423496fb0e --- /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 b12da61d0b3f..e9c40964aee4 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 @@ public class WebViewUpdateService extends SystemService { } @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 26c3fba23bbc..60dc4ff224bc 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 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface { 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 89cb4c802410..29782d9b8b88 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.AsyncTask; 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 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { 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 a9c3dc45842f..1772ef9c7405 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 @@ interface WebViewUpdateServiceInterface { 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 d6302e08fedb..bbaa6912417c 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 @@ class ActivityStarter { // 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 eafaf8cb3776..4625b4fe07ef 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 android.widget.Toast; 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { 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 @@ public class BackgroundActivityStartController { && 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; @@ -807,13 +816,29 @@ public class BackgroundActivityStartController { "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/Task.java b/services/core/java/com/android/server/wm/Task.java index de802b9b0e4d..5f082124dbcb 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 @@ class Task extends TaskFragment { // 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 @@ class Task extends TaskFragment { 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 @@ class Task extends TaskFragment { } // 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 @@ class Task extends TaskFragment { */ TaskFragmentParentInfo getTaskFragmentParentInfo() { return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), - shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity()); + shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(), + getDecorSurface()); } @Override @@ -3666,6 +3680,62 @@ class Task extends TaskFragment { } } + @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; } @@ -6673,4 +6743,77 @@ class Task extends TaskFragment { 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 8bc461f05387..39b4480a7da0 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 @@ class TaskFragment extends WindowContainer<WindowContainer> { /** 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 e7a1cf106a44..707f9fc9ea5f 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 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr .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 642d22f0bb9a..e5604eca3df0 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1756,6 +1756,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } /** + * 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. * diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index bacfda5fc528..e648d6417e88 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 @@ class TransitionController { 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 208df6c768bf..2af656942a2a 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.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 8473532754e4..c625b1e1eef7 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -592,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 - - The N entries of this array define N + 1 control points as follows: - (1-based arrays) - - 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 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> + <!-- Sets the list of display brightness points, each representing the desired screen brightness + in a certain lux environment. + + The first value of each point is the lux value and the second value is the brightness value. + + The first lux value must be 0. + + 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 2437261e3a54..8c8c1230f944 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 @@ package com.android.server.display.config { 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 @@ package com.android.server.display.config { 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(); @@ -222,6 +217,12 @@ package com.android.server.display.config { 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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0a2e80606e96..1919eb33c38c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2112,6 +2112,15 @@ public final class SystemServer implements Dumpable { 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 0ad418427183..000000000000 --- 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 97e582607133..a2e80f0d9b9b 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 @@ public class BrightnessMappingStrategyTest { 468.5f, }; - private static final int[] DISPLAY_LEVELS_BACKLIGHT = { + private static final int[] DISPLAY_LEVELS_INT = { 9, 30, 45, @@ -118,6 +118,20 @@ public class BrightnessMappingStrategyTest { 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 @@ public class BrightnessMappingStrategyTest { 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 @@ public class BrightnessMappingStrategyTest { 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 @@ public class BrightnessMappingStrategyTest { @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 @@ public class BrightnessMappingStrategyTest { @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 @@ public class BrightnessMappingStrategyTest { 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 @@ public class BrightnessMappingStrategyTest { 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 @@ public class BrightnessMappingStrategyTest { 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 e80e9e805369..31d7e88e671b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -50,6 +50,7 @@ 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; @@ -93,10 +94,14 @@ public final class DisplayDeviceConfigTest { @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(); } @@ -111,10 +116,6 @@ public final class DisplayDeviceConfigTest { 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(), @@ -607,7 +608,7 @@ public final class DisplayDeviceConfigTest { 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); @@ -727,6 +728,31 @@ public final class DisplayDeviceConfigTest { 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" @@ -1048,8 +1074,8 @@ public final class DisplayDeviceConfigTest { + "<screenBrightnessRampDecreaseMaxIdleMillis>" + "5000" + "</screenBrightnessRampDecreaseMaxIdleMillis>\n"; - } + private String getContent() { return getContent(getValidLuxThrottling(), getValidProxSensor(), /* includeIdleMode= */ true); @@ -1100,16 +1126,18 @@ public final class DisplayDeviceConfigTest { + "<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" @@ -1386,7 +1414,7 @@ public final class DisplayDeviceConfigTest { 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()); } @@ -1395,25 +1423,15 @@ public final class DisplayDeviceConfigTest { 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); @@ -1475,7 +1493,8 @@ public final class DisplayDeviceConfigTest { R.integer.config_screenBrightnessCapForWearBedtimeMode)) .thenReturn(35); - mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true); + 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 4fd8f26d91a8..dc6abf1981c0 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 @@ public class DisplayDeviceTest { @Mock private SurfaceControl.Transaction mMockTransaction; + @Mock + private DisplayAdapter mMockDisplayAdapter; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -67,34 +70,39 @@ public class DisplayDeviceTest { @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 @@ public class DisplayDeviceTest { 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 55f56e93fea5..02e3ef4d5f0b 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 @@ public class DisplayManagerServiceTest { 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 @@ public class DisplayManagerServiceTest { @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 @@ public class DisplayManagerServiceTest { @Override public void destroyDisplay(IBinder displayToken) { } - }); + }, flags); } @Override @@ -329,6 +331,7 @@ public class DisplayManagerServiceTest { @Mock SensorManager mSensorManager; @Mock DisplayDeviceConfig mMockDisplayDeviceConfig; @Mock PackageManagerInternal mMockPackageManagerInternal; + @Mock DisplayAdapter mMockDisplayAdapter; @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor; @Mock DisplayManagerFlags mMockFlags; @@ -788,7 +791,8 @@ public class DisplayManagerServiceTest { 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 @@ public class DisplayManagerServiceTest { 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 8270845657c6..f36854b1ea78 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 @@ public class LocalDisplayAdapterTest { // 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 43d2b8f741ae..28ec89629df0 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 @@ public class LogicalDisplayMapperTest { @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 @@ public class LogicalDisplayMapperTest { 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 9f91916a4046..5676a388acff 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.InstrumentationRegistry; 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 @@ public class PersistentDataStoreTest { 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 @@ public class PersistentDataStoreTest { @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 class PersistentDataStoreTest { 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 @@ public class PersistentDataStoreTest { 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 @@ public class PersistentDataStoreTest { 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 @@ public class PersistentDataStoreTest { @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 @@ public class PersistentDataStoreTest { @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 @@ public class PersistentDataStoreTest { @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 b7cbac5a7f18..5c50acb13f30 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 @@ public class RefreshRateSettingsUtilsTest { @Test public void testFindHighestRefreshRateForDefaultDisplay() { - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); - assertEquals(120, + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); + assertEquals(DEFAULT_REFRESH_RATE, RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), /* delta= */ 0); - } - @Test - public void testFindHighestRefreshRate() { - int displayId = 13; - when(mDisplayManagerMock.getDisplay(displayId)).thenReturn(mDisplayMock); + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).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); - } } 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 73e7ba0750e0..c01b15c17483 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 android.os.IBinder; 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 @@ public class VirtualDisplayAdapterTest { 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/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index d0859232778d..60a0c039dd03 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 @@ public class DisplayModeDirectorTest { }; 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 class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { 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 class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { 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 @@ public class DisplayModeDirectorTest { @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 be29163e7677..1a3a6a388392 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.assertFalse; 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 static org.mockito.Mockito.verify; 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 @@ public class DeviceIdleControllerTest { @Mock private ConnectivityManager mConnectivityManager; @Mock + private ContentResolver mContentResolver; + @Mock private IActivityManager mIActivityManager; @Mock private LocationManager mLocationManager; @@ -332,7 +336,7 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 000000000000..c5e6824099ed --- /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 931b38dc2951..f8fe97e9af13 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_DENIED 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 @@ class DeletePackageHelperTest { 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 7148b164efa9..2e0ba0083850 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 @@ public class BatteryUsageStatsProviderTest { .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 07c486c6ce58..079ea2c7832f 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { .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 @@ public class BatteryUsageStatsTest { .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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { } 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 @@ public class BatteryUsageStatsTest { 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 000000000000..fc91e47534f1 --- /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 000000000000..34ac47e1fe96 --- /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 82efdd3ce40a..800350a7d326 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.internal.compat.IPlatformCompat; 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 @@ public class AccessibilityManagerServiceTest { @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 @@ public class AccessibilityManagerServiceTest { 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 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(true); + verify(mMockMagnificationConnectionManager).requestConnection(true); } @SmallTest @@ -547,7 +547,7 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(true); + verify(mMockMagnificationConnectionManager).requestConnection(true); } @SmallTest @@ -565,7 +565,7 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(false); + verify(mMockMagnificationConnectionManager).requestConnection(false); } @SmallTest @@ -583,7 +583,7 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(true); + verify(mMockMagnificationConnectionManager).requestConnection(true); } @SmallTest @@ -602,7 +602,7 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(false); + verify(mMockMagnificationConnectionManager).requestConnection(false); } @SmallTest @@ -616,7 +616,7 @@ public class AccessibilityManagerServiceTest { // Invokes client change to trigger onUserStateChanged. mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); - verify(mMockWindowMagnificationMgr).requestConnection(true); + verify(mMockMagnificationConnectionManager).requestConnection(true); } @SmallTest @@ -630,7 +630,8 @@ public class AccessibilityManagerServiceTest { // 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 @@ public class AccessibilityManagerServiceTest { // 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 @@ public class AccessibilityManagerServiceTest { // 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 @@ public class AccessibilityManagerServiceTest { // 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 a02807fe766c..7829fcc4b44d 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.accessibilityservice.MagnificationConfig; 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 @@ public class MagnificationProcessorTest { @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 @@ public class MagnificationProcessorTest { 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 @@ public class MagnificationProcessorTest { mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false); - verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false, + verify(mMockMagnificationConnectionManager).disableWindowMagnification(TEST_DISPLAY, false, null); } @@ -296,7 +296,7 @@ public class MagnificationProcessorTest { mMagnificationProcessor.resetAllIfNeeded(connectionId); verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId)); - verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId)); + verify(mMockMagnificationConnectionManager).resetAllIfNeeded(eq(connectionId)); } @Test @@ -450,7 +450,7 @@ public class MagnificationProcessorTest { .setActivated(false).build(); mMagnificationProcessor.setMagnificationConfig(TEST_DISPLAY, config, false, SERVICE_ID); - verify(mMockWindowMagnificationManager) + verify(mMockMagnificationConnectionManager) .disableWindowMagnification(eq(TEST_DISPLAY), anyBoolean()); } @@ -481,11 +481,11 @@ public class MagnificationProcessorTest { 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 @@ public class MagnificationProcessorTest { } } - 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 @@ public class MagnificationProcessorTest { 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 @@ public class MagnificationProcessorTest { 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/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java index 24ad976f6e45..3843e2507df6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java @@ -33,8 +33,6 @@ 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; @@ -76,7 +74,7 @@ import org.mockito.invocation.InvocationOnMock; /** * Tests for WindowMagnificationManager. */ -public class WindowMagnificationManagerTest { +public class MagnificationConnectionManagerTest { private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM; private static final int SERVICE_ID = 1; @@ -91,9 +89,9 @@ public class WindowMagnificationManagerTest { @Mock private MagnificationAnimationCallback mAnimationCallback; @Mock - private WindowMagnificationManager.Callback mMockCallback; + private MagnificationConnectionManager.Callback mMockCallback; private MockContentResolver mResolver; - private WindowMagnificationManager mWindowMagnificationManager; + private MagnificationConnectionManager mMagnificationConnectionManager; @Before public void setUp() throws RemoteException { @@ -102,7 +100,7 @@ public class WindowMagnificationManagerTest { LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); mResolver = new MockContentResolver(); mMockConnection = new MockWindowMagnificationConnection(); - mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(), + mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(), mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext)); when(mContext.getContentResolver()).thenReturn(mResolver); @@ -122,11 +120,11 @@ public class WindowMagnificationManagerTest { final Context context = ApplicationProvider.getApplicationContext(); context.getMainThreadHandler().postDelayed( () -> { - mWindowMagnificationManager.setConnection( + mMagnificationConnectionManager.setConnection( connect ? mMockConnection.getConnection() : null); }, 10); } else { - mWindowMagnificationManager.setConnection( + mMagnificationConnectionManager.setConnection( connect ? mMockConnection.getConnection() : null); } return true; @@ -135,17 +133,17 @@ public class WindowMagnificationManagerTest { @Test public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + 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 { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + assertTrue(mMagnificationConnectionManager.isConnected()); verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); verify(mMockConnection.getConnection()).setConnectionCallback( any(IWindowMagnificationConnectionCallback.class)); @@ -153,31 +151,31 @@ public class WindowMagnificationManagerTest { @Test public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); mMockConnection.getDeathRecipient().binderDied(); - assertNull(mWindowMagnificationManager.mConnectionWrapper); + assertFalse(mMagnificationConnectionManager.isConnected()); 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 + * 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 { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); MockWindowMagnificationConnection secondConnection = new MockWindowMagnificationConnection(); - mWindowMagnificationManager.setConnection(secondConnection.getConnection()); + mMagnificationConnectionManager.setConnection(secondConnection.getConnection()); mMockConnection.getDeathRecipient().binderDied(); - assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + assertTrue(mMagnificationConnectionManager.isConnected()); verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0); verify(secondConnection.asBinder(), never()).unlinkToDeath( secondConnection.getDeathRecipient(), 0); @@ -185,20 +183,20 @@ public class WindowMagnificationManagerTest { @Test public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.setConnection(null); + mMagnificationConnectionManager.setConnection(null); - assertNull(mWindowMagnificationManager.mConnectionWrapper); + assertFalse(mMagnificationConnectionManager.isConnected()); verify(mMockConnection.getConnection()).setConnectionCallback(null); } @Test public void enableWithAnimation_hasConnection_enableWindowMagnification() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); + 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()); @@ -207,9 +205,9 @@ public class WindowMagnificationManagerTest { @Test public void enableWithCallback_hasConnection_enableWindowMagnification() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f, + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f, mAnimationCallback, SERVICE_ID); verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f), @@ -221,10 +219,10 @@ public class WindowMagnificationManagerTest { @Test public void disable_hasConnectionAndEnabled_disableWindowMagnification() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false); verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY), notNull()); @@ -233,10 +231,10 @@ public class WindowMagnificationManagerTest { @Test public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false, + mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false, mAnimationCallback); verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY), @@ -246,28 +244,28 @@ public class WindowMagnificationManagerTest { @Test public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); - assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @Test public void getPersistedScale() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f); + assertEquals(mMagnificationConnectionManager.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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); + mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f); - mWindowMagnificationManager.persistScale(TEST_DISPLAY); + mMagnificationConnectionManager.persistScale(TEST_DISPLAY); assertEquals(Settings.Secure.getFloatForUser(mResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f, @@ -276,11 +274,12 @@ public class WindowMagnificationManagerTest { @Test public void persistScale_setValueWhenScaleIsOne_nothingChanged() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - final float persistedScale = mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + final float persistedScale = + mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY); - mWindowMagnificationManager.setScale(TEST_DISPLAY, 1.0f); - mWindowMagnificationManager.persistScale(TEST_DISPLAY); + mMagnificationConnectionManager.setScale(TEST_DISPLAY, 1.0f); + mMagnificationConnectionManager.persistScale(TEST_DISPLAY); assertEquals(Settings.Secure.getFloatForUser(mResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f, @@ -289,50 +288,53 @@ public class WindowMagnificationManagerTest { @Test public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); - mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f); + mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f); - assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), 2.5f); + assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY), 2.5f); } @Test public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); - mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f); + mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f); - assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), + assertEquals(mMagnificationConnectionManager.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); + MagnificationConnectionManager spyMagnificationConnectionManager = spy( + mMagnificationConnectionManager); + spyMagnificationConnectionManager.enableWindowMagnification( + TEST_DISPLAY, 3.0f, 50f, 50f); + spyMagnificationConnectionManager.onImeWindowVisibilityChanged( + TEST_DISPLAY, /* shown */ true); - spyWindowMagnificationManager.processScroll(TEST_DISPLAY, 10f, 10f); + spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f); - verify(spyWindowMagnificationManager).logTrackingTypingFocus(anyLong()); + verify(spyMagnificationConnectionManager).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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.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, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection(), never()) @@ -345,16 +347,16 @@ public class WindowMagnificationManagerTest { 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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY); + mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection(), never()) @@ -364,15 +366,15 @@ public class WindowMagnificationManagerTest { @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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.inset(-10, -10); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection(), never()) @@ -381,14 +383,14 @@ public class WindowMagnificationManagerTest { @Test public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection(), never()) @@ -398,15 +400,15 @@ public class WindowMagnificationManagerTest { @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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), @@ -417,16 +419,16 @@ public class WindowMagnificationManagerTest { @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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection(), never()) @@ -436,17 +438,17 @@ public class WindowMagnificationManagerTest { @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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY); - mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region outRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion); final Rect requestedRect = outRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), @@ -456,58 +458,58 @@ public class WindowMagnificationManagerTest { @Test public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); - mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true); final Region beforeRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); final Rect requestedRect = beforeRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false); + mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); final Region afterRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion); + mMagnificationConnectionManager.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); + 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(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); final Rect requestedRect = beforeRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); final Region afterRegion = new Region(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion); + mMagnificationConnectionManager.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); + 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(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); final Rect requestedRect = beforeRegion.getBounds(); requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false); // Enabling a window magnifier again will turn on the tracking typing focus functionality. - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN); - mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), @@ -517,43 +519,43 @@ public class WindowMagnificationManagerTest { @Test public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); - mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300); + mMagnificationConnectionManager.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()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY, + mMagnificationConnectionManager.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); + mMagnificationConnectionManager.removeMagnificationButton(TEST_DISPLAY); verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY); } @Test public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.removeMagnificationSettingsPanel(TEST_DISPLAY); + mMagnificationConnectionManager.removeMagnificationSettingsPanel(TEST_DISPLAY); verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY); } @Test public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); final float testScale = 3f; - mWindowMagnificationManager.onUserMagnificationScaleChanged( + mMagnificationConnectionManager.onUserMagnificationScaleChanged( CURRENT_USER_ID, TEST_DISPLAY, testScale); verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged( eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale)); @@ -561,8 +563,8 @@ public class WindowMagnificationManagerTest { @Test public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + 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]; @@ -570,15 +572,15 @@ public class WindowMagnificationManagerTest { pointersLocation[1] = new PointF(300, 400); MotionEvent event = generatePointersDownEvent(pointersLocation); - assertEquals(mWindowMagnificationManager.pointersInWindow(TEST_DISPLAY, event), 1); + assertEquals(mMagnificationConnectionManager.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); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); mMockConnection.getConnectionCallback().onPerformScaleAction( TEST_DISPLAY, newScale, updatePersistence); @@ -590,8 +592,8 @@ public class WindowMagnificationManagerTest { @Test public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY); @@ -600,22 +602,22 @@ public class WindowMagnificationManagerTest { @Test public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); mMockConnection.getDeathRecipient().binderDied(); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @Test public void requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection() throws RemoteException { - assertTrue(mWindowMagnificationManager.requestConnection(true)); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); + assertTrue(mMagnificationConnectionManager.requestConnection(true)); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); - assertTrue(mWindowMagnificationManager.requestConnection(false)); + assertTrue(mMagnificationConnectionManager.requestConnection(false)); verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null); verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false); @@ -623,40 +625,40 @@ public class WindowMagnificationManagerTest { @Test public void requestConnection_requestWindowMagnificationConnection() throws RemoteException { - assertTrue(mWindowMagnificationManager.requestConnection(true)); + assertTrue(mMagnificationConnectionManager.requestConnection(true)); verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true); } @Test public void isConnected_requestConnection_expectedValue() throws RemoteException { - mWindowMagnificationManager.requestConnection(true); - assertTrue(mWindowMagnificationManager.isConnected()); + mMagnificationConnectionManager.requestConnection(true); + assertTrue(mMagnificationConnectionManager.isConnected()); - mWindowMagnificationManager.requestConnection(false); - assertFalse(mWindowMagnificationManager.isConnected()); + mMagnificationConnectionManager.requestConnection(false); + assertFalse(mMagnificationConnectionManager.isConnected()); } @Test public void requestConnection_registerAndUnregisterBroadcastReceiver() { - assertTrue(mWindowMagnificationManager.requestConnection(true)); + assertTrue(mMagnificationConnectionManager.requestConnection(true)); verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); - assertTrue(mWindowMagnificationManager.requestConnection(false)); + assertTrue(mMagnificationConnectionManager.requestConnection(false)); verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } @Test public void requestConnectionToNull_expectedGetterResults() { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1); - mWindowMagnificationManager.requestConnection(false); + mMagnificationConnectionManager.requestConnection(false); - assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0); - assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY))); - assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY))); + 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(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds); assertTrue(bounds.isEmpty()); } @@ -664,9 +666,10 @@ public class WindowMagnificationManagerTest { public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected() throws RemoteException { stubSetConnection(true); - mWindowMagnificationManager.requestConnection(true); + mMagnificationConnectionManager.requestConnection(true); - assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1)); + assertTrue(mMagnificationConnectionManager.enableWindowMagnification( + TEST_DISPLAY, 3f, 1, 1)); // Invoke enableWindowMagnification if the connection is connected. verify(mMockConnection.getConnection()).enableWindowMagnification( @@ -676,69 +679,73 @@ public class WindowMagnificationManagerTest { @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)); + 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)); - mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID); + mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertFalse(mMagnificationConnectionManager.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)); + 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)); - mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID); + mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); - assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2)); } @Test public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification() throws RemoteException { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); - mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext, + mMagnificationConnectionManager.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)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @Test public void centerGetter_enabledOnTestDisplay_expectedValues() { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f); - assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f); - assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); + assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f); + assertEquals(mMagnificationConnectionManager.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); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, + 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER); - assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f); - assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); + 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()); @@ -747,12 +754,12 @@ public class WindowMagnificationManagerTest { @Test public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues() throws RemoteException { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, - 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, + 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT); - assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f); - assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); + 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()); @@ -760,48 +767,48 @@ public class WindowMagnificationManagerTest { @Test public void magnifierGetters_disabled_expectedValues() { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, - 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, + 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER); - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + mMagnificationConnectionManager.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))); + 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(); - mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds); + mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds); assertTrue(bounds.isEmpty()); } @Test public void onDisplayRemoved_enabledOnTestDisplay_disabled() { - mWindowMagnificationManager.requestConnection(true); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f); + mMagnificationConnectionManager.requestConnection(true); + mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f); - mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY); + mMagnificationConnectionManager.onDisplayRemoved(TEST_DISPLAY); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @Test public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() { - mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); + mMagnificationConnectionManager.setConnection(mMockConnection.getConnection()); + mMagnificationConnectionManager.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); + 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); - mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false); verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY), anyBoolean()); 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 cfd0289e5650..8f85f11b7c49 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 @@ import org.mockito.MockitoAnnotations; /** * 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 @@ public class MagnificationConnectionWrapperTest { } @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 d4c6fad99645..e8cdf35dee13 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { )); 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID); mMockConnection.invokeCallbacks(); - assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); + assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @Test @@ -594,11 +601,11 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { // 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 class MagnificationControllerTest { 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 class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { // 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { 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 @@ public class MagnificationControllerTest { } 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 612a091a6b1b..c4be51f9ecbd 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 class WindowMagnificationGestureHandlerTest { 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 @@ public class WindowMagnificationGestureHandlerTest { @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 @@ public class WindowMagnificationGestureHandlerTest { } 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 @@ public class WindowMagnificationGestureHandlerTest { } private boolean isWindowMagnifierEnabled(int displayId) { - return mWindowMagnificationManager.isWindowMagnifierEnabled(displayId); + return mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId); } private static String stateToString(int state) { @@ -495,13 +495,14 @@ public class WindowMagnificationGestureHandlerTest { 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/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java index 749b07d16ebe..9c8276aac4dd 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.AudioManager.GET_DEVICES_OUTPUTS; 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.ILoudnessCodecUpdatesDispatcher; 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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { @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 @@ public class LoudnessCodecHelperTest { @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 @@ public class LoudnessCodecHelperTest { @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 @@ public class LoudnessCodecHelperTest { 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 @@ public class LoudnessCodecHelperTest { 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/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java index 01922e08d71d..edfe1b416f22 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.AndroidTestingRunner; 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 @@ public class VirtualCameraControllerTest { 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 class VirtualCameraControllerTest { 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 @@ public class VirtualCameraControllerTest { 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 5aef7a320930..000000000000 --- 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 14b121d3945c..0961b7d97177 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 @@ package com.android.server.media; 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 @@ public class DeviceRouteControllerTest { @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 @@ public class DeviceRouteControllerTest { @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/utils/UserSettingDeviceConfigMediatorTest.java b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java new file mode 100644 index 000000000000..377e4c3d6810 --- /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 d00060564e74..32082e3d857e 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.content.pm.Signature; 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 @@ public class WebViewUpdateServiceTest { 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 f2a1fe859634..c3074bb0fee8 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 f8ebeaddcb7e..46e87dceb8d4 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 2c35cf0c4151..8d236eda5dc5 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 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { } /** - * 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 71098aa5e883..360fdf3ae3f7 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 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { {"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/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java index 8119fd486a87..3b9ed2652610 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 android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 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.Button; 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.Test; 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 @@ public class SurfaceControlViewHostTests { private SurfaceControlViewHost mScvh1; private SurfaceControlViewHost mScvh2; + private SurfaceView mSurfaceView; + @Before public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -96,15 +100,17 @@ public class SurfaceControlViewHostTests { 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 @@ public class SurfaceControlViewHostTests { } 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 class SurfaceControlViewHostTests { } } - 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/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 8a90f127f4eb..06f29c262b42 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_CLOSE; 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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { /** 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 5e531b4cbc4f..da7612b17dc9 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.eq; 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.DisplayMetrics; 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 @@ public class TaskTests extends WindowTestsBase { 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 dade3b91e0eb..71447e72de8c 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 @@ public class TransitionTests extends WindowTestsBase { 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 000000000000..c12dcddd1b36 --- /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/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt index 356e1fa1327c..8354d985abfd 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 @@ class AndroidHeuristicsFilter( 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 214de59bbb4d..3956893ee7ed 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 @@ RuntimeVisibleAnnotations: 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ SourceFile: "IPretendingAidl.java" 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 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl } 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 90312289db1c..9349355d4d02 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub } 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 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl 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 @@ RuntimeVisibleAnnotations: 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 e01f49baf320..4f8c408eb7d9 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 @@ RuntimeVisibleAnnotations: 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub } 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 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl 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 @@ RuntimeVisibleAnnotations: 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 90312289db1c..9349355d4d02 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub } 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 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl 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 @@ RuntimeVisibleAnnotations: 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 5246355fb777..5ff3cded38b3 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 @@ RuntimeVisibleAnnotations: 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy 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 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub } 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 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl 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 @@ RuntimeVisibleAnnotations: 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 583e13c5573b..0a07c2b91fc3 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 interface IPretendingAidl { 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 0d527915ef63..d3501057163d 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 @@ public class TinyFrameworkClassTest { @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); } } |